Backbone.js attribute / fetching seems to work funny - javascript

So, I have the following code that access my rest api:
Employees.Employee = Backbone.Model.extend({
urlRoot: 'api/employee'
})
var daryl = new Employees.Employee({id:17})
daryl.fetch()
console.log(daryl.attributes)
Now, when I console.log the attributes, the daryl object is set up like this roughly:
daryl = {
attributes:
[0]: {
id: 17,
first: 'Daryl',
last: 'xxxx',
email: 'xxx'
},
id: 17,
watchers...
protos...
}
So trying to daryl.get('first') results in undefined. Everything else is stored in the object in the array at index 0. Why is this? I'm a newbie here but this is definitely not how most of the tutorials seem to show how backbone works.
So if I do daryl.get('first'), I get undefined. daryl.get('id') works as expected. daryl.get('0') actually returns a plain old javascript object of the actual model, i.e. what I would probably expect to be my backbone model to ACTUALLY be. why is this?

Your server appears to be returning an array in its response, hence why calling model.get('0') is returning the attributes you really wanted. You either need to modify the server's response to only return the object (instead of an object inside an array) or you need to add a parse method to your model to return the first item in the response array.

Not sure if this is the issue in question (but doing console.log after calling fetch is problematic), but it is important to keep in mind that daryl.fetch()is happening asynchronously.
That is to say, you should try:
daryl.fetch().done(function(){
console.log(daryl.attributes);
model.get("first");
});
or
daryl.fetch({success : function(model){
console.log(model);
model.get("first");
}});
This ensures that the AJAX request was complete prior to trying to act on the model and very well maybe why get returns undefined.

Related

Created a new object, then put an array after it in a loop to patch a property, it works but I dont know why?

I'm working with JavaScript in Node.js and using Express with some mongoose and following a well known RESTful API tutorial on youtube, I've come to patching the API and have been trying to understand why the following code works for sometime now;
updateItem = {};
for (const changes of req.body) {
updateItem[changes.propName] = changes.value;
}
Product.updateOne({ _id: id }, { $set: updateItem })
The rest is just your standard .then().catch() to send the response status, but I'm lost on how creating the object then placing it before an array works to update a value.
It's my current understanding that the object must be instantiated before use, I couldn't just put brackets there and have it work, even if I wasn't using it to set something later. Then I loop through the changes from the request body which must be an array to allow looping, but here's where I get lost.
Does the array of iterated prop names changes.propName get placed inside the updateItem object which is then set to the changed values from the array of properties that are being changed? Do I need to understand $set syntax more? I'm struggling to pick it apart to make it longer or simpler but better to understand.
This is the json array setup I'm passing for testing through postman if it helps;
[
{
"propName": "name", "value": "placeholder user"
}
]
I was unable to find anything to help me understand the interactions going on here, I haven't seen something like this before either but please redirect me if this has already been asked.

Backbone: I am trying to fetch a collection (using a rails server). However, my fetch doesn't look to be working right

I am using a rails server that returns this JSON object when going to the '/todos' route.
[{"id":1,"description":"yo this is my todo","done":false,"user_id":null,"created_at":"2015-03-19T00:26:01.808Z","updated_at":"2015-03-19T00:26:01.808Z"},{"id":2,"description":"Shaurya is awesome","done":false,"user_id":null,"created_at":"2015-03-19T00:40:48.458Z","updated_at":"2015-03-19T00:40:48.458Z"},{"id":3,"description":"your car needs to be cleaned","done":false,"user_id":null,"created_at":"2015-03-19T00:41:08.527Z","updated_at":"2015-03-19T00:41:08.527Z"}]
I am using this code for my collection.
var app = app || {};
var TodoList = Backbone.Collection.extend({
model: app.Todo,
url: '/todos'
});
app.Todos = new TodoList();
However, when trying to fetch the data it states that the object is undefined. I originally thought that my function wasn't parsing the JSON correctly. However, that doesn't look to be the case. I created a parse function with a debugger in it to look at the response. In gives back, an array with three objects.
Here what happens when I try testing the fetch().
var todos = app.Todos.fetch()
todos.length // returns undefined
todos.get(1) // TypeError: undefined is not a function
The todos collection doesn't automatically populate the function get() in console. I am running out of ideas of what can be the problem. Please help. Thanks!
Fetch is a ayncronous, you need to listen to the add event:
var todos = app.Todos.fetch()
todos.on('add', function(model){
console.log(todos.length);
});
If you pass the parameter reset, you could listen for the would new models:
var todos = app.Todos.fetch({reset: true})
todos.on('reset', function(model){
console.log(todos.length);
});
You could also read here.
There are two problems:
Fetch is asynchronous; we don't know exactly when we'll have a result, but we do know that it won't be there when you are calling todos.length.
Fetch sets the collection's contents when it receives a response; calling app.Todos.fetch() will result in app.Todos containing whatever models were fetched by the request. Its return value is not useful for inspecting the collection, so var todos = app.Todos.fetch() won't give you what you want in any case.
If you want to inspect what you receive from the server, your best option is to set a success callback:
app.Todos.fetch({
success: function (collection, response, options) {
console.log(collection);
}
});

Creating and updating nested objects in Ember

I got nested JSON data from the server like this:
{
name: "Alice",
profile: {
something: "abc"
}
}
and I have the following model:
App.User = Ember.Object.extend({
name: null,
profile: Ember.Object.extend({
something: null
})
})
If I simply do App.User.create(attrs) or user.setProperties(attrs), the profile object gets overwritten by plain JS object, so currently I'm doing this:
var profileAttr = attrs.profile;
delete attrs.profile
user.setProperties(attrs); // or user = App.User.create(attrs);
user.get('profile').setProperties(profileAttrs);
It works, but I've got it in a few places and in the real code I've got more than one nested object, so I was wondering if it's ok to override User#create and User#setProperties methods to do it automatically. Maybe there's some better way?
Based on your comment, you want the automatic merging behaviour you get with models (the sort of thing you get with .extend()). In that case, you could try registering a custom transformer, something like:
App.ObjectTransform = DS.Transform.extend({
deserialize: function(json){
return Ember.Object.create(json);
}
});
App.User = DS.Model.extend({
profile: DS.attr('object')
});
See: https://github.com/emberjs/data/blob/master/TRANSITION.md#json-transforms
If you are doing your server requests without an adapter you can use the model class method load() with either an array of json objects or a single object. This will refresh any known records already cached and stash away the JSON for future primary key based lookups. You can also call load() on a model instance with a JSON hash as well but it will only update that single model instance.
Its unclear why you are not using an adapter, you can extend one of the Ember Model adapters and override the the record loading there, eg. extend from the RESTAdapter and do any required transform on the JSON if required by overriding _loadRecordFromData
You can also override your models load function to transform data received if required as well. The Ember Model source is fairly easy to read so its not hard to extend to your requirements.

BackboneJS collection.reset() vs collection.fetch()

I have read and read the docs on these two methods, but for the life of me cannot work out why you might use one over the other?
Could someone just give me a basic code situation where one would be application and the other wouldn't.
reset sets the collection with an array of models that you specify:
collection.reset( [ { name: "model1" }, { name: "model2" } ] );
fetch retrieves the collection data from the server, using the URL you've specified for the collection.
collection.fetch( { url: someUrl, success: function(collection) {
// collection has values from someUrl
} } );
Here's a Fiddle illustrating the difference.
We're assuming here that you've read the documentation, else it'l be a little confusing here.
If you look at documentation of fetch and reset, what it says is, suppose you have specified the url property of the collection - which might be pointing to some server code, and should return a json array of models, and you want the collection to be filled with the models being returned, you will use fetch.
For example you have the following json being returned from the server on the collection url:
[{
id : 1,
name : "a"
}, {
id : 2,
name : "b"
}, {
id : 3,
name : "c"
}]
Which will create 3 models in your collection after successful fetch. If you hunt for the code of collection fetch here you will see that fetch will get the response and internally will call either reset or add based on options specified.
So, coming back to discussion, reset assumes that we already have json of models, which we want to be stored in collection, we will pass it as a parameter to it. In your life, ever if you want to update the collection and you already have the models on client side, then you don't need to use fetch, reset will do your job.
Hence, if you want to the same json to be filled in the collection with the help of reset you can do something like this:
var _self = this;
$.getJSON("url", function(response) {
_self.reset(response); // assuming response returns the same json as above
});
Well, this is not a practice to be followed, for this scenario fetch is better, its just used for example.
Another example of reset is on the documentation page.
Hope it gives a little bit of idea and makes your life better :)
reset() is used for replacing collection with new array. For example:
#collection.reset(#full_collection.models)
would load #full_collections models, however
#collection.reset()
would return empty collection.
And fetch() function returns default collection of model

Backbone.js fetch not actually setting attributes

I have a basic backbone model, its urlRoot attribute is set and the corresponding target on the server side returns a correct JSON output (both JSON string and application/json header).
I call a fetch like this:
var athlete = new Athlete({ id: 1 });
athlete.fetch();
at this point if I add a
console.log(athlete);
I can see the model, and inspecting it in firebug I can open the attributes object and see all the values returned from the server.
BUT if I do a:
console.log(athlete.get('name'));
I get undefined (the name appears under the attributes in the DOM inspection I mentioned above)
also doing a:
console.log(athlete.attributes);
returns an object containing only {id: 1} which is the argument I passed while creating the model.
If I create the model like this:
var athlete = new Athlete(<JSON string copypasted from the server response>);
then everything works fine, the .get() method returns whatever I ask, and athlete.attributes shows all the values.
What am I doing wrong?
fetch is asynchronous, which means that the data won't be available if you immediatly call console.log(athlete.get('name')) after the fetch.
Use events to be notified when the data is available, for example
var athlete = new Athlete({id: 1});
athlete.on("change", function (model) {
console.log(model.get('name'));
});
athlete.fetch();
or add a callback to your fetch
var athlete = new Athlete({ id: 1 });
athlete.fetch({
success: function (model) {
console.log(model.get('name'));
}
});
or take advantage of the promise returned by fetch:
athlete.fetch().then(function () {
console.log(athlete.get('name'));
});
Just as a quick remark when using events in this example. It did not work with change in my case because this events fire on every change. So sync does
the trick.
var athlete = new Athlete({id: 1});
athlete.on("sync", function (model) {
console.log(model.get('name'));
});
athlete.fetch();

Categories