Backbone-Relational firing add event before server responds - backbone.js

I'm using Backbone-Relational in a project at the moment, and it's not behaving the way it seems like it should.
Let's say Model A has a HasMany relationship with Model B. Model A listens for an "add:model_b" event to render a necessary view for Model B whenever an instance is added to the Model A relationship. In order to render the view template properly though, we need some data from the server. From reading the backbone docs, it seems to me the logical way to do this would be as follows:
modelA.get("model_b").create(newModelData, {wait: true});
As Backbone-Relational implements relations as Backbone Collections, I just call the collection.create method to instantiate my new Model B, and pass {wait: true} to the options in order to delay the "add" event til the server responds.
However, this seems to have no effect: the "add" event is firing instantly, and I'm therefore getting an error when trying to render a view that needs data that is not yet part of the model. Does anyone know why {wait: true} is doing nothing in this instance, and how can I get the desired behavior out of Backbone-Relational?

Related

Backbone best practices where to fetch models/collections

I've inherited a codebase that follows the format of: a router sets a controller, the controller fetches the collection/model needed, then the controller set/passes the view the collection/model.
The current View I'm working on loads a collection, and now I need to build in a feature where I fetch a single model after the view has rendered, based on an id clicked (note the model is from a different collection).
I only want to load the model when/if they click a button. So my question is, can I setup the model/fetch in the View, or should I be doing that in the controller? Is there a backbone best practice when adopting a controller/view setup like this?
I primarily ask because it seems easier for me to add this new feature right in the View. But is that a bad practice? I thought so, so I started down the path of triggering an event in the View for the controller to the fetch the model, and then somehow pass that back to the View (but I'm not sure really how to even do that)...it seems like a lot of unnecessary hoop jumping?
Its OK to fetch collection via views. As 'plain' backbone does not Controller, View in charge of it responsibilities.
But imho fetch collections via controller is better practice, its easier to scale and support and test.
The only difficulty is to set communication between Controller and View context event. One of the approach is trigger Message Bus event on context event like
events: {
'click .some': function() {
this.trigger('someHappend', { some: data })
}
}
and listen to this event in Controller
this.on(someViewInstance, 'someHappend', function() {
// do something in controller
})
If you already inherited code with structure you described you'd better follow it. Also you might be interested in MarionetteJS as significant improvement. Also highly recommend you to checkout BackboneRails, screencasts not free but very usefull, especially in large scale app maintain

Backbone model listener stops working as soon as the model is saved

In the initialize function of my backbone View, I created the following listener:
this.listenTo(this.model.get('clusters'), 'add remove', this.saveChanges);
This successfully causes my saveChanges function to be called the first time a model is added/removed from the 'clusters' Collection. There's only one line in that saveChanges function:
this.model.save();
Once that is invoked, adding/removing clusters no longer invokes the "add" or "remove" event. Why would saving the model destroy the listener? Can that be prevented, or is there a way to re-establish the listener?
Or is there something fundamental I'm not understanding about Models and/or Collections...?
Let's break down your code:
You write
this.listenTo(this.model.get('clusters'), 'add remove', this.saveChanges);
Which is equal to
var clusters = this.model.get('clusters');
this.listenTo(clusters, 'add remove', this.saveChanges);
Now I only assume that after you get that event you set() a new clusters object inside your model.
The issue here is that your view still listens to events from that same old clusters object, which is not relevant anymore - your model deals with another object!
Another case could be that Backbone clears your view's event handlers from its model when it's being removed.. Could easily answer for sure if you'd share the whole code.

Backbone.js event system (model and collection)

In Backbone.js (0.9.2) I have the following:
One collection and one method that gets called when something changes
this.collection.on("change", this.methodOne, this);
When I change a value of one model of this collection ...
model.set("value1", "abc");
... the change event from the model BUBBLES UP to the collection and so "methodOne" is called.
When I look into the source code of Backbone.js, "collection", method "add", a private
method "_prepareModel" is called that makes the model - collection reference:
model.collection = this;
But from there on I do not understand how and in which methods this bubble up mechanism
is functioning?
Has anybody got information for me about that?
Thanks alot in advance!
Wolfgang
Take a look at Backbone.Collection _onModelEvent method. In the docs:
Internal method called every time a model in the set fires an event. Sets need to update their indexes when models change ids. All other events simply proxy through. "add" and "remove" events that originate in other collections are ignored.

Does backbone trigger .add when a collection is .fetched?

I am converting my backbone app so that it starts communicating with the server, previously I had just been populating the collection with test data using .add()
I have tied some events to the collections add event. So every time an item is added to the collection I can render the view and update some statistics.
it appears that as soon as i add the .fetch() call to get data from the server the add events stop.
for example
var PayableCommitmentCollection = Backbone.Collection.extend({
model:PayableCommitment,
url:"/cc/account/contributions/",
initialize: function() {
this.bind("add",this.setInitialAmount,this);
}
}
this.SetInitialAmount() is never called after fetch creates the models in the collection.
I also have 2 views that are watching for items to be added to this collection that are now not updating.
My obvious work around is to write my own AJAX call so that I can add the items the same way I have been during development so far, however I'm sure backbone has the smarts to help me out here.
Can anyone suggest a way i can bind to the completion of fetch, or to make it stimulate the add event.
The fetch method accepts a hash of options. One of these options can be the "add" option, which calls add on the collection instead of reset.
collection.fetch({ add: true });
From the fine manual:
fetch collection.fetch([options])
Fetch the default set of models for this collection from the server, resetting the collection when they arrive.
And a reset:
reset collection.reset(models, [options])
Adding and removing models one at a time is all well and good, but sometimes you have so many models to change that you'd rather just update the collection in bulk. Use reset to replace a collection with a new list of models (or attribute hashes), triggering a single "reset" event at the end.
So a fetch will trigger a single "reset" event rather than a bunch of "add" events. You need a collection-wide version of setInitialAmount that you can bind to "reset".
In Backbone 1.0, Collection#fetch has this to say:
fetch collection.fetch([options])
Fetch the default set of models for this collection from the server, setting them on the collection when they arrive.
[...]
The behavior of fetch can be customized by using the available set options. For example, to fetch a collection, getting an "add" event for every new model, and a "change" event for every changed existing model, without removing anything: collection.fetch({remove: false})
So, if you're using 1.0+ then all you need to do is call your fetch with the remove: false option.

Who is supposed fetch a model in a backbone application?

In a backbone application what is the best practice regarding when a model is fetched? I can see the following possibilities.
View calls the model fetch method
Some other JavaScript code calls the fetch model? if so when and what structure would this code have? is this the missing controller concept in Backbone?
A few best practives:
1 Collections and models that are necessary from the very first milliseconds of the app's life should be 'bootstrapped' in place (so there shouldn't be need to fetch them to gain access to vital data)
So when the user is served the correct pages from the server, the models and collections should be already in place (nice example from backbone.js docs)
var ExampleCollection = new Backbone.Collection();
ExampleCollection.reset(<%= #your_collection_data.to_json() %>); // Or whatever your server-side language requires you to do, this is a ruby example
2 The rest can be fetched just in time
The models and collections that aren't needed at the moment your app is initialized can be fetched whenever you feel like it, but I think that the logical time to do that is when the user expresses intent to use those models. E.g. user presses a button to open a view that needs some model/collection -> fetch that collection, user wants to clear unsaved changes from a model -> fetch that model from the server to get the last saved status of the model, and so forth. Usually the place where the fetching is bound to happen is the view that 'owns' the model/collection, because it relays the users actions to the model and displays the model's state to the user.
But like it was said, Backbone.js isn't strict about when a model or collection should be fetched. It can be done anywhere in the app, just make sure you do it only when it's necessary.
Hope this helps!
If you want to be standard, your view must render one time when initialize and listen for the change event of the Model and re render the view every time that model changes, that is all. (regarding what does View needs to do when fetch is completed)
And for call the model.fetch() if you follow the standard that I said, no matters where the fetch is called your view will be updated.
Some people could have a module named load in the view where do something like this:
load : function(){
this.model.fetch();
}
Others could do external fetch call, like this:
var myModel = new YourModel();
var myView = new SomeView( {model : model} );
//Probably you could render with the default data in the while model is fetched
myView.render();
model.fetch();

Resources