Backbone Collection - Model Update - backbone.js

I retrieve a list of models from server into a collection. When a user is interested in a specific model, I route it to a viewer for that model based on the id.
Now, let's say a user need edits to that model in the view - eg: he updates his name. I set the url of the model, update the name and call save. It calls the backend (Jersey in my case) and it gets saved. However, the collection is still not updated automatically.
Why cannot a collection bind to each change in the model and update it self?
What is the correct way of saving a model which is a part of the collection? Is it right to call save on a model (by setting its url) and then remove from its parent collection and then add it again with silent:true?
Thanks.

The Collection is not updated?
When you say "the collection is not updated automatically" I guess you mean "the View is not updated automatically".
The View will not respond to Model changes at least you say so explicitly using bindings:
// code simplified and no tested
var MyModel = Backbone.Model.extend({});
var MyModelView =
Backbone.View.extend({
initialize: function(){
this.model.on( "change:all", this.render, this );
},
render: function(){
this.$el.html( "<h1>" + this.model.title + "</h1>" );
}
})
Declare the URL explicitly for each Model
As #OlliM has said, this shouldn't be necessary if your backend is supporting an standard CRUD API. You can declare the URL in your Collection like this:
/app/collection
And Backbone will use this URLs as the API:
(GET) /app/collection fetch the whole Collection
(GET) /app/collection/1 fetch only the Model with id = 1
(PUT) /app/collection/1 update the Model with id = 1
(POST) /app/collection create a new Model
... DELETE and so on

Related

View event reflected in collection convention

What is the best way to remove a model from a collection that has been removed in the DOM. Let me ask a better question, how do I keep views in sync with a collection?
remove the view first, while removing execute
this.model.collection.remove(this.model);
you can check with conditions to whether current view has a model, and that model has a collection etc before you execute the same.
I've followed the backbone Todos example application. This keeps view state up to date with collection.
Pass models to any view created like so:
var someView = new SomeItemView({ model: modelFromCollection });
Then listen to events on that model and react from the view:
initialize: function() {
this.listenTo(this.model, 'destroy', this.remove);
// listen to other events ...
}

Backbone - Using fetched data inside the view

I have fatched the data to Collection variable, but I am not sure what should I do next. How to use this data and to fill the template with it? Here is my code from the render function inside the View.
Collection.url = "../data";
Collection.fetch();
var compiled = _.template(self.data);
self.$el.prepend(compiled(/*MY JSON SHOULD GO HERE*/));
I am a newbie to backbone, so every help is appreaciated.
Here is a Collection definition:
var MainCollection = Backbone.Collection.extend({
model: MainModel,
//localStorage: new Backbone.LocalStorage("kitchen"),
initialize: function (models,options) { }
}), Collection = new MainCollection;
Here is a log of Collection and Collection coverted to JSON:
Assuming Collection is your collection's name (that's pretty confusing I have to say), this is what you're looking for:
self.$el.prepend(compiled(Collection.toJSON()));
Edit:
Don't forget you're fetching the data asynchronously. So when you're evaluating your template, the data hasn't come back yet, and your collection's still empty. Listen to the end of the request ('sync' event I think) or some other events so you know when the collection's populated OR use the success option of the fetch method to specify a callback :)
As for your logs. When you log an object, it will be automatically updated until you check the details. So you logged it while it was empty, but checked it after it was populated (a few ms afterwards).

How to update model in collection?

I have the following Backbone.js collection:
var Tags = Backbone.Collection.extend({
url: "/api/v1/tags/"
}),
How do I update one of the models in the collection so that it posts to /api/v1/tags/id and saves the data for that model.
So if I change name of model with id 2 in the collection
It should PUT to
/api/v1/tags/2 with the following data:
name: new name id: 2
I've also recently wanted to update particular model in the collection. The problem was that if I did use just model.save it didn't update the collection. The goal was to change the model in collection, change it on the server, update collection accordingly and not use the sync method. So for example I have my variable collection and I want to change the model with id = 2. So the first thing, I will create an instance model, like this: var model = collection.get(2)Then I will update the attributes on this particular model:model.set({name: 'new name'})Then I will save it to the server:model.save({}, {url:'/api/v1/tags/'+model.get('id')})Then we have to update collection accordingly to the changes:collection.set({model},{remove: false})set method - it's a 'smart' update of the collection with the list of the models you passed in parameters. remove: false parameter - it's a restriction for a collection to remove existing models in collection. More here.
The first thing you can miss is that in your corresponding Tag model you'll need to set "urlRoot" to match the Collection's "url". Otherwise it doesn't know about the collection at all:
var Tag = Backbone.Model.extend({
urlRoot: "/api/v1/tags"
});
var Tags = Backbone.Collection.Extend({
model: Tag,
url: "/api/v1/tags"
});
This is useful if you want to save the tag separately:
var tag = collection.get(2);
tag.set({key: "something"});
tag.save(); // model.save works because you set "urlRoot"
On the collection, "create()" is also "update()" if id is not null. That's not confusing. :) Therefore, this is pretty much equivalent to the previous sample:
collection.create({id: 2; key: "something"});
This will update the existing tag with id=2 and then trigger a PUT.
This is an ancient question; answering because I was searching for the same answer--you've probably long since solved this problem and moved on. :)
You can pass variables to the save method. It accepts all the options which jQuery's ajax method uses (unless you overrided Backbone.Sync)
You could do something like:
model.save( { name:'new name' } );
The id and PUT method will automatically be added by Backbone for you.

Accessing other collection inside backbone model

I have 2 post collections and a model as follows.
# router file
#posts = new MyApp.Collections.PostsCollection()
#posts.reset options.posts
#followed_posts = new MyApp.Collections.PostsCollection()
#followed_posts.reset options.followed_posts
# Post model file
class MyApp.Models.Post extends Backbone.Model
paramRoot: 'post'
follow_post: ->
# ajax call
console.log "_________Index:#{this.collection.indexOf(this);}"
console.log this.collection
console.log "_________Followed:"
console.log #followed_posts
class MyApp.Collections.PostsCollection extends Backbone.Collection
model: MyApp.Models.Post
url: '/posts_all'
What I am trying to do is when one of the model changed in one collection, I want to update the other model in other collection too.
These collections may or may not hold same models.
So let's say if a model in #posts changed in my Post model, I want to update that model in #followed_posts too. If #followed_posts doesn't have that model, I need to add a duplicate of the model to #followed_posts collection.
I can access the collection that model belongs, but I cannot access the other collection.
Any ideas appreciated, thanks.
If the two collections are antisocial and can't talk directly to each other, which is usually good design, then you'll need an intermediary -- a global event dispatcher. When a model changes, propagate that event to the dispatcher along with a reference to the model. Listen for the event in the other collection and use the model passed to check for existence and respond as needed.
EDIT:
Backbone's documentation mentions this pattern:
For example, to make a handy event dispatcher that can coordinate
events among different areas of your application: var dispatcher =
_.clone(Backbone.Events)
But in fact, this is such a common pattern that the Backbone object itself is extended with Events. So you can just do:
// In your Post model
#on "change", -> Backbone.trigger "post:change", this, #collection
// And then something like this in the collection class definition:
#listenTo Backbone, "post:change", (model, collection) =>
if post = #get model.cid
post.set model.toJSON()
else
#add model
Also, is followed posts a subset of posts? If so, why not put an attribute on the model designating it as followed? Then you could find all followed posts with a simple filter function.
I would strongly suggest that you should consider having a single collection and add some kind of attribute in the model to differentiate between what kind of posts they are.

Collection fetch from server, not firing change when model has been delete from server

I am fetching a collection from the server, lets say I originally start with 30 models and in the database one of these models has some of attributes changed, when I fetch the collection the change is detected and the changes are rendered. Fine works just fine.
But when the model is delete in the database and the collection which had 30 and now has 29 does not fire destroy on the missing model. The model does not exist anymore but the view is still rendered and it does not correspond to any model, because the model is not part of the collection anymore. Need help with this one. And the view is binded to "change" and also "destroy".
I already tried all kinds of stuff, many variations in code and nothing seems to work.
Thanks
var commentCollection = new CommentList;
commentCollection.fetch({ data: $.param({ user_id:id}), success: function(){
Profile_view = new Profile({collection: commentCollection});
$("div.Profile_container").html(this.Profile_view.el);
} });
function fetch_collection(commentCollection, id){
//commentCollection.reset();
commentCollection.fetch({update: true, data: $.param({ user_id:id})});
console.log(commentCollection)
}
setInterval(function(){fetch_collection(commentCollection, id)},10000);
I got it!!!!!
All I had to do was bind the remove event to the view function that removes the actual view from the DOM
this.model.bind("remove", this.close, this)

Resources