So i have a problem whereby I have a backbone collection that am using to create function to save data to a REST API. The data is saved to the server and a model is added to the current collection but then the add event for the collection is not fired.Below are snippets of the code
The views initialize function
intialize : function() {
this.listenTo(this.collection, 'add', this.updateList);
},
The updateList function only does a console log.The views function that saves data using the collection is:
cards = this.collection;
debugger
cards.create(data, {
success : function(model, response) {
console.log("success on saving card");
console.log(response);
console.log("Updating list");
console.log(cards);
},
error : function(model, response) {
console.log("error on saving card");
console.log(model);
console.log("response");
console.log(response);
}
})
return false;
Try this code in your view:
initialize: function() {
this.collection.on('add', this.updateList, this);
}
Or:
var someCollection = new SomeCollection();
var view = new SomeView({collection: someCollection});
view.listenTo(someCollection, 'add', view.updateList);
why don't you just use: this.model.on('change', doAction, this); inside the view? if I understood correctly, this is a better solution since your model is changing.
plus, I couldn't find anywhere a place where the ListenTo() is mandatory over the On() function, can you tell me where you saw it?
Related
I seem to be fetching correctly from the server using backbone. A GET request is made to a MongDB collection, via the Node.js server code here:
exports.getTeams = function(req,res,next){
var system_db = req.system_db;
var user_id = req.mainUser._id;
var teams = teamModel.getNewTeam(system_db,user_id);
teams.find({}, function (err, items) {
res.json(items);
});
};
I am fetching from Backbone like so:
var teamCollection = new TeamCollection([]);
teamCollection.url = '/api/teams';
teamCollection.fetch(
{success:function(){
console.log('teamCollection length:',teamCollection.length);
console.log('teamCollection[0]:',teamCollection[0]);
}}
);
using this model and collection:
var Team = Backbone.Model.extend({
idAttribute: "_id",
urlRoot: 'http://localhost:3000/api/teams'
});
var TeamCollection = Backbone.Collection.extend({
model: Team,
initialize: function() {
this.bind('add', this.onModelAdded, this);
this.bind('remove', this.onModelRemoved, this);
this.bind("change", this.onModelChanged, this);
},
/* parse: function(data) {
//return JSON.stringify(data).objects;
//return JSON.parse(data).objects;
return data.objects;
},*/
onModelAdded: function(model, collection, options) {
console.log("added, options:", options);
},
onModelRemoved: function (model, collection, options) {
console.log("removed, options:", options);
},
onModelChanged: function (model, collection, options) {
console.log('Collection has changed.');
},
comparator: function (model) {
return model.get("_id");
}
});
the problem is that the logging statements above log the following in the browser console:
It says I am sending 4 items from the server to the Backbone client, but the first one is undefined. How could this be?
A Backbone.Collection is not an array-like object : it has a length attribute representing the number of models but you can't access individual models via indexes, hence
console.log(teamCollection[0]); //undefined
To get a model at a given position, use collection.at .Try
console.log(teamCollection.at(0));
And a demo of those behaviors http://jsfiddle.net/nikoshr/013gjpny/
Basically, I'm trying to send a GET request to my Node server, so that I can get back blog posts to create links. I do a collection.fetch, which successful completes the GET request (the Node server logs that it's sending the right objects). The model successfully parses the right data, but when I try to use the collection, it says that it's empty. Here's the code:
var mdm = mdm || {};
// MODEL
mdm.Post = Backbone.Model.extend({
parse: function( response ) {
response.id = response._id;
console.log(response); // logs the two documents
return response;
}
});
// COLLECTION
mdm.Posts = Backbone.Collection.extend({
model: mdm.Post,
url: '/api/posts'
});
// MODEL VIEW
mdm.LinkView = Backbone.View.extend({
template: _.template( $('#link_template').html() ),
render: function() {
this.$el.html( this.template( this.model.toJSON() ));
return this;
}
});
// COLLECTION VIEW
mdm.LinksView = Backbone.View.extend({
el: '#link_list',
initialize: function() {
this.collection = new mdm.Posts();
this.collection.fetch({reset: true});
// makes the request properly, but collection is empty
this.render();
// never gets called because the collection is empty
console.log(this.collection.length);
// logs a length of 0
},
render: function() {
// renders collection
}
});
$(function() {
new mdm.LinksView();
});
The data is being sent and is parsed in the models, so I'm not sure what the collection ends up being empty. Any help would be greatly appreciated.
The most likely reason you are not seeing the models in your view is because the render is happening before the asynchronous fetch is complete.
Something like below would work better:
mdm.LinksView = Backbone.View.extend({
el: '#link_list',
initialize: function() {
this.collection = new mdm.Posts();
this.listenTo(this.collection, 'reset', this.render);
this.collection.fetch({reset: true});
}
The above code sets a listener for the reset event on the collection and executes the render function when that happens.
Also, you could passing in success and error handlers into fetch and call the render function manually as well.
this.collection.fetch({
success: _.bind(function() {
this.render(); }, this)
});
Hope this helps!
Per #fbynite's comment, the problem was related to fetch being asynchronous. I made the following changes to the collection view, and it did the trick:
initialize: function() {
var self = this;
this.collection = new mdm.Posts();
this.collection.fetch({reset: true,
success: function() {
self.render();
console.log(self.collection.length);
}
});
},
The code is a modification from a Backbone tutorial, so other users may encounter a similar problem. http://addyosmani.github.io/backbone-fundamentals/#exercise-2-book-library---your-first-restful-backbone.js-app
This is my problem:
I have a container view that holds a collection.
On page load I get some models, populate this collection with them, then render the models
I fire and event
When this event fires, I want to make a call to my api (which returns models based on input parameters)
I then want to remove all existing models from the collection, repopulate with my new models, and then render the models
This is how I set up my model/collection/view
var someModel = Backbone.Model.extend({});
var someCollection = Backbone.Collection.extend({
model: someModel,
url: "api/someapi"
});
var someView = Backbone.View.extend({
events: {
"click #refresh": "refreshCollection"
},
initialize: function () {
this.collection.bind("reset", this.render, this);
},
render: function () {
// render stuff
},
refreshCollection: function (e) {
this.collection.fetch({data: {someParam: someValue});
this.render();
}
});
var app = function (models) {
this.start = function () {
this.models = new someCollection();
this.view = new someView({collection: this.models});
this.view.reset(models);
};
};
My point of interest is here:
refreshCollection: function (e) {
this.collection.fetch({data: {someParam: someValue});
this.render();
}
I pass in some paramaters, and my api returns a json array of models. I want to get rid of all existing models in the collection, and put all of my returned models into the collection, then update the view (with render())
I understand this is possible with collection.set, or collection.reset. Both of these take in an array of models. I don't have an array of models to pass in.
I tried:
this.collection.fetch({
data: {someParam: someValue},
success: function (response) {
doSomethingWith(response.models)
}
});
But I don't know what to do with the models when I get them.
Any pushed in the right direction would be appreciated!
From the fine manual:
fetch collection.fetch([options])
[...] When the model data returns from the server, it uses set to (intelligently) merge the fetched models, unless you pass {reset: true}, in which case the collection will be (efficiently) reset.
So you just need to include reset: true in the options and fetch will call reset to replace the collection's contents with the fetched models:
this.collection.fetch({
data: { ... },
reset: true
});
When the first page of my Backbone app load I fetch a collection then iterate over it to render the page:
Page Router:
home: function ()
{
new BodyView ({page:'home'});
new HomeView();
new PostView();
postCollection = new PostCol({userId:getId(),start:0,offset:50});
postCollection.fetch();
postView = new Post({collection:postCollection});
},
Post View:
el:'.stream',
initialize: function ()
{
this.collection.on('reset',this.render.bind(this));
this.collection.on ('change',this.render.bind (this));
},
render: function ()
{
this.collection.each (function (model)
{
actor = new Actor ({model:model});
this.$el.append (actor.render().el);
},this);
return this;
},
What I am trying to accomplish now is that when a user save some data in another view it update the Post view. This is what I did but its not working.
Other View:
post = new PostModel ({userid:getId(),post:postText,objectid:0});
post.save({},
{
success: function (response)
{
postCol = new PostCol ({userId:getId(),start:0,offset:50});
postView = new Post ({collection:postCol});
postCol.fetch ().success({change:true});
},
error: function (response)
{
console.log (response);
}
}
);
It looks like you postCollection is global, so you could update the existing model instead of creating a new one.
// find existing model in the collection.
var post = postCollection.get(getId());
// save model with updated info.
post.save({post:postText},
{
success: function (response)
{
// if the save succeeded, the Post view will re-render,
// since you are listening for the 'change' event.
// so here, you don't really need to do anything.
},
error: function (response)
{
console.log (response);
}
);
Instead of this.collection.on ('change',this.render.bind (this)); in the Post view, you could do this in the individual Actor view, so the whole collection does not re-render.
this.model.on ('change',this.render, this); // 'this' as third parameter replaces the `bind`
I am using the backbone LayoutManager to manage my views.
I am having an issue fetching the model data before render is called, which obviously throws an error since the Ajax success callback is not done yet.
One way to fix this would be to fetch the model within the router and put app.useLayout("main").render(); into the success method. Is that the right way to do it or is there a better solution?
Router code :
app.useLayout("main").setViews({
".place-detail": new Place.Views.Show({
model: new Place.Model({ place_id: place_id })
})
});
app.useLayout("main").render();
View code :
Views.Show = Backbone.View.extend({
initialize: function(options) {
_.bindAll(this,"render");
this.model.on("change", this.render, this);
this.model.fetch();
});
});
Backbone's fetch accepts success and error callbacks, so if you want to wait for the fetching to finish before executing more code, put it in the success callback:
Views.Show = Backbone.View.extend({
initialize: function(options) {
var _this = this;
_.bindAll(this,"render");
this.model.fetch({
success: function(model, response) {
_this.render();
_this.model.on("change", _this.render, _this);
_this.someOutsideFunctionCall();
},
error: function(model, response) {
console.log("Error Fetching.");
}
});
};
});
I've been coding a lot in CoffeeScript so I'm not sure if I got all the ({});s correct, but that's the basic gist of it. Note the declaration of _this = this, since if you try to reference this inside the callback function, it won't know what you're talking about.