how model and view are connected in backbone.js - backbone.js

I am new in Backbone.js and I keep failing to understand how the model and the view are connected.
I played with Angular where things are pretty clear there, how model, view and controller are connected.
I know Angular and Backbone are different and the latter is MV*.
In Backbone I can understand how model and view are created and work, but how are they connected? Seems to me they are seperated.
Please take a minute to explain or point me to a tutorial.
Thanks in advance
EDIT
OK, here is an example. It happens that I read the book that trolle suggests. This is a code from the book's github
I start reading. I understand the Todo model. I understand the TodoList collection. Then I get to the TodoView
creates a new li
uses Underscore template to compile html
defines some functions that imlements later in the same view
defines an initialize function
inside that function
what is this? this.model.bind('change', this.render, this);
how he can magically bind the action change to a model? How the code knows about the model? When he defined the model and how? Just because is there, the code knows that model = Todo model?
How does he do that bind? What am I missing.
This confuses me, so reading AppView view does not help me much
Thanks again

In backbone.js views are used for displaying models in browser.
For example you can have a model object, whose JSON representation resembles the following: {'firstName': 'foo', 'lastName': 'bar' }
And you use view object to map this model to browser DOM.
As a rule, you use view object along with certain template engine.
Templates allows for creating html chunks filled with model's data.
If you are using underscore template function, your template may look something like this:
<div>
<div>First Name: <%= firstName %></div>
<div>Last Name: <%= lastName%></div>
</div>
After merging template with model's data it would be:
<div>
<div>First Name: foo</div>
<div>Last Name: bar</div>
</div>
You can reuse this view object and its template to display another model object, for example {'firstName':'another foo', 'lastName':'another bar'}, so that the result html would be:
<div>
<div>First Name: another foo</div>
<div>Last Name: another bar</div>
</div>
That is one thing about connection between model and view.
Also view object can listen to changes in your model object to render immediately last updates. For example (inside view object):
initialize: function() {this.listenTo(this.model, 'change', this.render);}

In short, views are the logic behind the presentation of the model's data to the user. So in its simplest form, you bind a model to a view through the models change events, so you can update the presentation instantly whenever your data changes. So a simple view would take in a model, create HTML elements based on that models data, insert that html into the DOM and update that HTML whenever the data changes.
You can find a great book full of helpful examples here (free): http://addyosmani.github.io/backbone-fundamentals/
EDIT:
With regards to your updated question about how the view knows about the model, this.model is a reference to the actual model object. You can set the reference to the model when you create the view. That is, when you call your view-constructor to instantiate a view, you could pass in a model. You need to go all the way into the AppView object in the code example to see where this happens, in the addOne method:
addOne: function(todo) {
var view = new TodoView({model: todo});
this.$("#todo-list").append(view.render().el);
}
The function gets a model as a parameter, and when the view is instantiated that model is referenced. So now you have a view that knows about the model, and when the view.render method is called, the view can render it's template with the model data. When you change the data in the model, for instance by using the set method, myModel.set({title: "March 20", content: "In his eyes she eclipses..."});, you trigger the change event for that model. You can see all the built in events for backbone here: http://backbonejs.org/#Events-catalog. The view is listening for that event, just like it could listen for a click event or any other event. In the code in your example the view listenes for a change event from the model. If it hears it it knows that the object behind this.model has changed, and it can then update the DOM with the new data or do something else. In the case of the example it calls this.render, which updates the DOM with the new model data.

I think you want to know about Backbone.Events (http://backbonejs.org/#Events), both Models and Views make use of this module and that's how the view learns about changes in the Model, if you want to learn how this is implemented you can always read the annotated source (http://backbonejs.org/docs/backbone.html#section-19), but more important I think you want to learn about the observer pattern: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjavascript.

Related

Backbone.js: Why can't I retrieve the model in this case?

I'm trying to retrieve the model, because I need to access certain attributes, but I cannot. I'm inside a view, which extends another one. This is how the view looks like:
var ImageGridControlView = GridControlView.extend({
//.... stuff ....
alert(this.model.get('property')) //This gives me an error in console and nothing is alerted.
});
In the class that the above code extends, the model can be retrieved, like this:
var GridControlView = ControlView.extend({
//.... stuff ....
alert(this.model.get('property')) //This shows the property value correctly
});
Why can't the model be retrieved from ImageGridControlView? What is the persistence of models across different classes, children, etc? I'm new to Backbone, and the official documentation only covers models superficially.
Your snippets are too small to actually find the problem, so we have to just guess. Most likely candidates are:
you are accessing this.model inside a function triggered by an event binding that is not properly bound, so this isn't actually your view instance. It could be a the source DOM element of the window object instead.
You aren't passing a model property in the options object provided to the view's constructor

how to sync a backbone view with the attached model on bootstrap/creation?

Usually I have a View on which I set listeners to model-changes, like so (conflated):
var jsonModel = {bla: 'interesting stuff'}; //some model in json, probably rendered in a dom-element and passed from the server to the client
var someModelType = Backbone.RelationalModel.extend({
bla: "String"
});
var someModelInstance = new someModelType(jsonModel);
var someViewType = Backbone.View.extend({
initialize: function(){
this.listenTo(this.model,'change:bla', function(model){
//update view to sync with model change here
}
}
});
var someViewInstance = new someViewType({
model: someModelInstance
});
I'm looking for the preferred / backbone - way of bootstrapping the view, i.e: I want my view (form fields or what have you) to be synced with the model on creation. With the above this is impossible since the model is created before the view is attached, which results in model-updates/changes to be fired before listeners from the view were initialized.
Of course I could code some custom bootstapping logic which would call the listener-functions manually, but since this must be such a common problem, I'm looking on some best practice advice, or even better, a Backbone-switch I need to set to get this to work.
This will sound silly, but what I usually do is do a render() after the view creation.
For me, the whole point of the change event is to notify of a change. The fact that it's triggered also on creation (as a "changed compared to the default") is more a side effect than anything deep...
In that sense, when I create the views, and give it an existing model, I expect the model to be somewhat ready and so I should be able to do a render() right away. So my bootstrapping code would be more like:
var someViewInstance = new someViewType({
model: someModelInstance
});
someViewInstance.render()
Or do it in the initialize() after bindings if you feel adventurous (I personally don't like that because when dealing with subviews, it's hard to know exactly when you want the render to happen, especially when dependent on libraries who need the DOM to be ready, and can't work on detached nodes).
So to sum up, although your problem is common, to me, it doesn't need any kind of complex solution: you have a render() function to transform your model into DOM-stuff, your model may or may not be ready but it exists, so you pass it to the view, and you do the first rendering after creating the view, manually, because you know it needs to happen. Then you bind to changes, in case your model is updated, to update the DOM accordingly.
side note: in case of a collection of models, when the Model isn't ready at ALL, then it probably shouldn't even have an instance, and you should let the Collection give you some add/remove events for when it pops into existence.

How do I design MarionetteJS ItemView to properly show loading message?

First off - I am a MarionetteJS noob.
I am having trouble making an ItemView display a loading message or throbber while it is being fetched. This is especially problematic when this ItemView is being displayed from a deep link in Backbone's history (i.e. the ItemView is the first page of the app being displayed since the user linked directly to it). I want to indicate that the page is loading (fetching), preferably with a simple view, and then show the real templated view with the fetched model.
I have seen other answers on SO like Marionette.async, (which has been deprecated) and changing the template during ItemView.initalize().
Anybody (Derrick?) got any suggestions or best practices here?
UPDATE:
I am getting the model from the collection using collection.get(id), not using model.fetch() directly.
After thinking about this, the real question is where should this be implemented:
I could change my controller to see if the model exists in the collection (and if the collection is loaded) and decide which view to show accordingly. this seems like a lot of boilerplate everywhere since this could happen with any ItemView and any controller.
I could change my ItemView initialize to test for existence of the model (and a loaded collection), but same comment here: every ItemView could have this problem.
UPDATE 2:
This is what I ended up with, in case anybody else want this solution:
app.ModelLayout = Backbone.Marionette.Layout.extend({
constructor: function(){
var args = Array.prototype.slice.apply(arguments);
Backbone.Marionette.Layout.prototype.constructor.apply(this, args);
// we want to know when the collection is loaded or changed significantly
this.listenTo(this.collection, "reset sync", this.resetHandler);
},
resetHandler: function () {
// whenever the collection is reset/sync'ed, we try to render the selected model
if (this.collection.length) {
// when there is no model, check to see if the collection is loaded and then try to get the
// specified id to render into this view
this.model = this.collection.get(this.id);
}
this.render();
},
getTemplate: function(){
// getTemplate will be called during render() processing.
// return a template based on state of collection, and state of model
if (this.model){
// normal case: we have a valid model, return the normal template
return this.template;
} else if (this.collection && this.collection.isSyncing) {
// collection is still syncing, tell the user that it is Loading
return this.loadingView;
} else {
// we're not syncing and we don't have a model, therefore, not found
return this.emptyView;
}
}
});
And here is how to use it:
// display a single model on a page
app.Access.Layout.CardLayout = app.ModelLayout.extend({
regions: {
detailsRegion:"#detailsRegion",
eventsRegion:"#eventsRegion"
},
template:"CardLayout", // this is the normal template with a loaded model
loadingView:"LoadingView", // this is a template to show while loading the collection
emptyView:"PageNotFoundView", // this is a template to show when the model is not found
onRender : function() {
this.detailsRegion.show( blah );
this.eventsRegion.show( blah );
}
});
thanks!
For the ItemView
I think you can add a spinner in your initialize function, I really like spin.js http://fgnass.github.io/spin.js/ because its pretty easy and simple to use, and you can hide the spinner in the onRender function of the Itemview
For The CollectionView
in the CollectionView you could handle it like this....
Take a look at the solution that Derick posted..
https://github.com/marionettejs/backbone.marionette/wiki/Displaying-A-%22loading-...%22-Message-For-A-Collection-Or-Composite-View
I'd suggest using jQuery deferreds:
Start fetching your data, and store the return value (which is a jQuery promise)
Instanciate your content view
Show your loading view
When the promise is done, show the view containing the content
I've talked about implementing this technique on my blog:
http://davidsulc.com/blog/2013/04/01/using-jquery-promises-to-render-backbone-views-after-fetching-data/
http://davidsulc.com/blog/2013/04/02/rendering-a-view-after-multiple-async-functions-return-using-promises/
The issue with the solution linked by Rayweb_on, is that your loading view will be displayed any time your collection is empty (i.e. not just when it's being fetched). Besides, the ItemView doesn't have an emptyView attribute, so it won't be applicable to your case anyway.
Update:
Based on your updated question, you should still be able to apply the concept by dynamically specifying which template to use: https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.view.md#change-which-template-is-rendered-for-a-view
Then, when/if the data has been fetched successfully, trigger a rerender in the view.

Backbonejs: How to unbind collection bindings when a view is removed?

I've got a backbonejs application with two view. It kind of looks like this:
<body>
<header></header>
<div id="content"></div>
</body>
Every time a view is loaded the app overwrites the current view by completely overwriting the contents of #content.
// Like this...
$('#content').html( new primaryView().render() );
// ...and this.
$('#content').html( new secondaryView().render() );
The application has a global collection.
App.Collection();
The secondary view modifies itself depending on the global collection. Therefor it binds a function to the 'add' event' on App.Collection in the views initialize function;
App.Collection.bind('add', function(){
console.log('Item added');
});
Which result in my problem. Every time the secondary view is loaded a new function is binded to App.Collection's add event. If I go from the primary view to the secondary view three times, the function will fire three times everytime an item is added to App.Collection.
What am I doing wrong?
I can see how I would do it if there was an uninitialize function on views.
I can see how I could do it if I never removed a view once it was loaded.
I can see how I would do it if I could namespace events like in Jquery. (by unbinding before binding).
You can generalize your problem quite a bit. Basically, you are writing an event-driven app, and in such app events should be taken care of.
Check out this post to see a recommended way to work with event handlers in backbone.
Depending on the situation, you can use initialize and render methods to handle different aspects of creating a view. For instance, you can put your binding inside the initialize
initialize: function() {
App.Collection.bind('add', function(){
this.view.render()
});
}
which only fires when the view is created. This binds your render method to the add event. Then in your render method you can actually create the html.
This prevents the binding from happening every time you need to re-render.

Backbone boilerplate Events

What is the best way to bind events to a Backbone boilerplate application? I've been trying to bind my events directly to the models associated with my views, in my views, but it doesn't seem to be working. I see within 'namespace.js', that there is an app key that extends Backbone.Events like so:
// Keep active application instances namespaced under an app object.
app: _.extend({}, Backbone.Events)
I don't fully understand how to use it...
I was able to get things working without the boilerplate, but it does provide some very cool functionality, so I'd love to be able to use it. Thanks!
ADDED
the code I was using was with the underscore bind method like so:
this.module.bind('change', this.render);
But then, I realized that 'this.model' is returning undefined, and so this doesn't work. I really am not sure how the boilerplate wants me to reference my model from the view.
I'm not sure if it is a typo that you copied from your code or a typo you only entered here, but I believe this.module (which IS undefined) should be this.model, which you also must be sure to pass in when you instantiate your view, of course, as so:
myView = new BBView({model: myModel});
then you can say this.model.bind('change', this.render); or this.model.on('change', this.render); for the most recent version of Backbone
I frequently bind my views to change events on my models in this way.
As for extending Backbone.Events, the way I have used it is to create an independent "event aggregator" that will help connect events between different views on your page. Let's say for example you have some action on one view that needs to set off an action on another view. In this case, you can pass your event aggregator object as an option to each of your views when you instantiate them, so that they can both trigger events or bind to events on a common object (i.e. your event aggregator).
whatsUp = _.extend({}, Backbone.Events) // the aggregator
myFirstView = new FirstBBView ({whatsUp: whatsUp});
(the aggregator shows up as this.options.whatsUp inside the view)
mySecondView = new SecondBBView2 ({whatsUp: whatsUp});
inside FirstBBView:
this.options.whatsUp.bind('specialEvent', function(arg1,arg2) {
// do stuff
});
inside SecondBBView, when something important happens:
this.options.whatsUp.trigger('specialEvent', {arg1: 'some data', arg2: 'more data'});
For a great explanation, see this great article by Derick Bailey

Resources