fetch is not updating collection.model.models - javascript

I have a backbone collection that i've initialized like this:
myCollection = new MyCollection([], {type: 'animals', params: {username: 'steve'}});
myCollection.fetch();
console.log(myCollection) // prints out an object that includes 'models' and the newly fetched models
console.log(myCollection.models) // prints out an empty list []
does anyone know why?

fetch is an asynchronous operation so whatever you do immediately after fetch is most likely executed before the fetch is finished, which leads to quite random results. put the console logging inside the fetch's success-function and see what happens

The model of your collection must have an url to the server to fetch it into a collection, my thought that you have it on "MyCollection", just in case. And then you only need to add a success callback to display the collection populated, like this:
myCollection = new MyCollection([], {type: 'animals', params: {username: 'steve'}});
myCollection.fetch({
success : function(returnedCollection, response){
console.log(returnedCollection);
console.log(returnedCollection.models);
}
});

Related

Backbone collection fetch imported incorrectly

I have a collection which is fetched from a REST endpoint, where it receives a JSON.
So to be completely clear:
var Products = Backbone.Collection.extend({
model: Product,
url : 'restendpoint',
customFilter: function(f){
var results = this.where(f);
return new TestCollection(results);
}
});
var products = new Products();
products.fetch();
If I log this, then I have the data. However, the length of the object (initial) is 0, but it has 6 models. I think this difference has something to do with what is wrong, without me knowing what is actually wrong.
Now, if I try to filter this:
products.customFilter({title: "MyTitle"});
That returns 0, even though I know there is one of that specific title.
Now the funky part. If I take the ENTIRE JSON and copy it, as in literally copy/paste it into the code like this:
var TestCollection = Backbone.Collection.extend({
customFilter: function(f){
var results = this.where(f);
return new TestCollection(results);
}
});
var testCollectionInstance = new TestCollection(COPY PASTED HUGE JSON DATA);
testCollectionInstance.customFilter({title: "MyTitle"});
Now that returns the 1 model which I was expecting. The difference when I log the two collections can be seen below. Is there some funky behaviour in the .fetch() I am unaware of?
Edit 2: It may also be of value that using the .fetch() I have no problems actually using the models in a view. It's only the filtering part which is funky.
Edit 3: Added the view. It may very well be that I just don't get the flow yet. Basically I had it all working when I only had to fetch() the data and send it to the view, however, the fetch was hardcoded into the render function, so this.fetch({success: send to template}); This may be wrong.
What I want to do is be able to filter the collection and send ANY collection to the render method and then render the template with that collection.
var ProductList = Backbone.View.extend({
el: '#page',
render: function(){
var that = this; /* save the reference to this for use in anonymous functions */
var template = _.template($('#product-list-template').html());
that.$el.html(template({ products: products.models }));
//before the fetch() call was here and then I rendered the template, however, I needed to get it out so I can update my collection and re-render with a new one (so it's not hard-coded to fetch so to speak)
},
events: {
'keyup #search' : 'search'
},
search : function (ev){
var letters = $("#search").val();
}
});
Edit: New image added to clearify the problem
It's a bit tricky, you need to understand how the console works.
Logging objects or arrays is not like logging primitive values like strings or numbers.
When you log an object to the console, you are logging the reference to that object in memory.
In the first log that object has no models but once the models are fetched the object gets updated (not what you have previously logged!) and now that same object has 6 models. It's the same object but the console prints the current value/properties.
To answer your question, IO is asynchronous. You need to wait for that objects to be fetched from the server. That's what events are for. fetch triggers a sync event. Model emits the sync when the fetch is completed.
So:
var Products = Backbone.Collection.extend({
model: Product,
url : 'restendpoint',
customFilter: function(f){
var results = this.where(f);
return new TestCollection(results);
}
});
var products = new Products();
products.fetch();
console.log(products.length); // 0
products.on('sync',function(){
console.log(products.length); // 6 or whatever
products.customFilter({title: 'MyTitle'});
})
It seems like a response to your ajax request hasn't been received yet by the time you run customFilter. You should be able to use the following to ensure that the request has finished.
var that = this;
this.fetch({
success: function () {
newCollection = that.customFilter({ title: 'foo' });
}
});

Backbone Collection: How to fetch a specific object from API to place in model

I am using a collection to fetch data from an API to place this into a model.
I do receive all data from the API, however the data is returned in an object and I only need a part of this object.
Let me clarify this with an example of the returned JSON:
{
"all-users":
[
{"username":"poekoe","lastsyncdate":"1376496898"},
{"username":"plaap","lastsyncdate":"1376494547"}
],
"total-users": "10",
"selected": 2
}
This is the JSON returned, however I only want need the 'all-users' array to be placed in my model.
At this point I am fetching data like this:
var userCollection = new UserCollection;
userCollection.fetch({
data: {
"search": "p",
"session: login.session
}
});
What can I do here to only use the 'all-users' array to be placed in the model?
Is there something in the Fetch what I can do? or should I alter the Model to only use the all-users array?
Thanks in advance!
You can override the parse method of the collection:
var UserCollection = new Backbone.Collection.extend({
parse: function(data){
return data["all-users"];
}
});
So your Collection will only consist of what the parse method returns, in this case the all-users array from your response json.

how to use spinejs fetch ajax data with conditions

For example, I have this Model as Users:
var Users = Spine.Model.sub();
Users.configure('Users', 'name', 'gender', 'age');
Users.extend(Spine.Model.Ajax);
Users.extend({url:"/users"});
Assume that we already have some data saved in database.
If run
var users = Users.fetch();
Ajax will send a GET request to /users, and all results will be returned.
But if I wanna fetch all female or male users, or age above 25, or top 10 users in specified order, how to pass these variables? I cant find spec in the document. The fetch method can pass a callback function parameter to revoke when fetch complete, clearly not what I want.
I found the solution myself..Actually the document tells how to paginate results.
var Photo = Spine.Model.sub();
Photo.configure('Photo', 'index');
Photo.extend(Spine.Model.Ajax);
Photo.extend({
fetch: function(params){
if ( !params && Photo.last() )
params = {data: {index: this.last().id}}
this.constructor.__super__.fetch.call(this, params);
}
});
But I found the code can't run, first
this.constructor.__super__.fetch.call(this, params);
should be
this.__super__.constructor.fetch.call(this, params);
secondly, if run Photo.fetch({data:{id:1}}), it will send a GET request like this
GET /photos?[object%20Object]
correct it
Photo.fetch({data: $.param({id:1})});
HTTP Request
GET /photos?id=1

Fetch Backbone Collection by Model IDs list

I have a REST API serving a few URLs:
/rest/messages
provides all messages. A message is a JSON/Backbone Model
{
title: 'foo',
body : 'bar'
}
To get a single message I have:
/rest/messages/:id
Is it possible to fetch a Backbone Collection using message IDs array? I don't want the whole message list, but just a few messages I specify by ID.
I could fetch Models one-by-one and fill up the Collection, but I'm wondering if Backbone has a cleaner way to do this.
Thanks
According to documentation, you can pass ajax options to the fetch call. So, you can pass ids as data attribute to the fetch call being done and based on it, return the respective models from the server.
For example (when doing fetch),
collection.fetch({
data : {
message_ids : [1, 3, 5] // array of the message ids you want to retrieve as models
}
})
This message_id array will be accessible as parameters (not sure of the name in your case) in the server code being executed at /rest/messages, from there you can return only specific models based on ids you receive as message_ids. The only thing you need is, client side must be aware of the ids of all the message models it needs.
You can use any data structure instead of array to send message_ids.
The url property of collection reference to the collection location on the server. When you use fetch, backbone uses that url.
The url property can be also a function that returns the url. So you can do something like that:
var ids = [1,2,3]
var messages = new MessegecCollection();
messages.url = function() {
return "/rest/messages/"+ids.join("-"); //results "/rest/messages/1-2-3"
}
messages.fetch();
You can also create a method in your collection that generated and set the url, or even fetchs a set of models.
Now all you have to do is to support this url: /rest/messages/1-2-3
Hope this helps!

Printing Backbone.js collection through console.log(), what's wrong with this?

OK. I'm starting out on Backbone.js, and trying to do some very simple things. Here's a very simple Model and Collection.
// my model
Friend = Backbone.Model.extend({});
// my collection
Friends = Backbone.Collection.extend({});
// instantiate friends and add some friends objects!
var friends = new Friends();
friends.add([
{name: "James"},
{name: "Michael"}
]);
console.log(friends.length) // prints out 2! which is correct!
Above example is fine. But now I want to initialize my collection from server, which returns the exact same JSON object.
// my model
Friend = Backbone.Model.extend({});
// my collection
Friends = Backbone.Collection.extend({
url: 'http://localhost/myapp/get_users'
});
// instantiate friends and add some friends objects!
var friends = new Friends();
friends.fetch();
console.log(friends.length) // prints out 0! WHY???
I've been looking at my Chrome inspection panel for both of them and regardless, I have no idea why one from the server is not working?
FYI, on the server side, I have CodeIgniter 2.02, using Phil Stuegeon's REST API to return a JSON data.
I've also tried a simple function on my PHP side, like
function get_users() {
echo '{name:"James"}, {name:"Michael"}';
}
But without any success.
What am I doing wrong here?
Have you forgot the [] to make it an array?
echo '[{name:"James"}, {name:"Michael"}]';
Also you check the length in the onsuccess method you can pass to fetch. At the moment you check the length directly after fetching, so your not sure if the result is still loaded.
friends.fetch(
{success:function(){
console.log(friends.length)
}}
);
fetch() is an asynchronous call, so you'll be printing to the console before the fetch() has returned to populate your collection
To verify, put a breakpoint on the console.log(friends.length) line. The breakpoint will give the fetch() time to happen
I think the JSON you're passing to .add() is invalid. You've got:
[
{name: "James"},
{name: "Michael"}
]
But you need to quote the keys too:
[
{"name": "James"},
{"name": "Michael"}
]

Categories

Resources