A quick question regarding Events in Backbone - is there any way to define a set of global events, which can be re-used in different views?
For instance - say I have several Views which are rendered on the page. Each View has a button that will expand a menu. There are also several other generic elements and events. Without putting this event logic into each View, is there a way that each of these Views could inherit, or pull these events from a global events definition? It would certainly save time and be a lot cleaner solution by defining these generic events in one place.
I've seen terms such as Event Aggregator and Factory patterns thrown around - but not sure on the best approach (or if they will achieve what I'm after).
You're basically describing an event aggregator or dispatcher, yes. I've got a couple of articles on building and using them with Backbone:
http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/
http://lostechies.com/derickbailey/2012/04/03/revisiting-the-backbone-event-aggregator-lessons-learned/
And there are plenty more articles around the web for doing the same with Backbone and other pub/sub libraries.
It's quite simple to do in Backbone:
vent = _.extend({}, Backbone.Events);
and now you have a vent object that can be used as an event aggregator throughout all of your application's views and other objects.
vent.on("some:event", function(){
console.log("some event was fired");
});
vent.trigger("some:event");
Related
or should I just create one view for each model ? I mean with backbone alone I was doing some kind of "renderSubview" , with marionette this is pointless and I just shoudl avoid this ?
Is it bad to bind a marionette view to several model ? (and update different part of its template according to multiple models update ?)
thansk a lot
Marionette doesn't give you any tools to do exactly that, but it's a thin layer on top of Backbone; the approach you describe will work just fine.
However, if your sub-views are not very simple, it's probably better to use a Marionette LayoutView. That way you'll benefit from encapsulation and DOM isolation (so sub-views won't interfere with each others' events).
Simple sub-views, Backbone-style
This approach is good for trivial subviews - probably with very simple templates and little user interaction. You don't need anything more than the approach you described:
You can add renderSubViewX methods to any Marionette view type (or even a vanilla Backbone.View). They'll look like typical Backbone render methods - call a template function or create some DOM nodes and insert them into the document. Use this.listenTo(this.model1, "change", this.renderSubView1, this) to re-render on changes.
LayoutView
If the sub-views are more complex (perhaps they allow non-trivial user interaction) you will benefit from creating a separate ItemView for each model. Use a Marionette LayoutView for the parent view.
Derick Bailey's blog post on Layouts is a little out-of-date but provides a good overall introduction.
What are the advantages and disadvantages of the following 2 lines of code? I don't understand why there are 2 different ways to do the same thing.
this.listenTo(app.Todos, 'change:completed', this.filterOne);
app.Todos.on('change:completed', this.filterOne);
Also when using .on, how do I determine is the default context?
listenTo is the newer and better option because these listeners will be automatically removed for you during stopListening which is called when a view gets removed (via remove()). Prior to listenTo there was a really insidious problem with phantom views hanging around forever (leaking memory and causing misbehavior) because view methods were referenced as event listeners on models even though the view instances themselves were long gone and no longer in the DOM.
If you want to read the back story for listenTo, search the backbone github repository for listenTo and read through some of the longer issue discussions.
As to the default context, several things can end up bound to this:
if you do the binding via this.listenTo, it will always be the view instance (pointed out by Wim Leers in the comments)
without this.listenTo, the story gets complicated
For misc events, it will be the global object (best to avoid this)
for DOM events, it will be the source element just like in regular DOM event binding
If you provide an explicit context (the 3rd argument to foo.on), backbone will use that (thus this is a more robust approach)
If you use the ECMA standard function () {//your event handler}.bind(this), you can also manually control the context (also recommended)
As #mu pointed out, _.bind or $.proxy are available alternatives to ECMA function.bind
For backbone views, doing this.bindAll('onClick', ...) will ensure the view instance is the this context when any view methods are used as event handlers
any events wired up by using the view's standard events property will get bound for you automatically to the view instance by backbone (this is belt & suspenders with bindAll)
So to summarize into some guidelines:
use the events property whenever possible as it is concise and correct
use this.listenTo for all bindings to models and collections
any additional bindings remember to bind the context reliably using your preferred method. I usually use ECMA Function.bind because hey, standards, but there are several good options here.
With listenTo, the object whose events you want to listen to is passed as the first argument. In the case of on, it is actually a method on that object.
The advantages of listenTo over on are:
The listener keeps track of all the event handlers, making it easier to remove them all at once when needed.
The callback’s context is always set to the listener itself.
I'm trying to understand why we always put event listening type code like, in intialize:
initialize: function(){
this.model.on('change', this.render, this);
}
I thought the initialize function was only called once when the view way created so it doesn't really make sense why it would go in the initialize function, can anyone explain?
Backbone has many built in event listeners, 'change' being one of these. There are a few reasons why you add model listeners inside of the initialize function. Backbone uses jQuery for its eventing system. One of its notable features are creating arbitrary event names. Backbone collections and models come pre-built with existing events (add, create, sync, update, change, etc). These events are not built into the javascript language, thus, during initialization of models and collections in Backbone, you register the events that you would like to intercept and/or listen to.
If you're wondering why can't you just create an update (or any other event listener) method inside of your models and expect Backbone to invoke these automatically is because this would cause a major performance and memory issues.
so... Inside your Backbone views, you must 'subscribe' to events on the model or collection objects your view is bound to. Because Backbone does not have 2-way data binding, it is customary to invoke the 'render' event whenever necessary, which is more often than not when your models or collections update, sync, change, and so on.
Pretty cryptic question I admit.
I'm looking for references / best practice in updating views based on a GUI event.
Basically I'm seeing 2 different ways to do this:
every view-change is a reaction to a model-change
Gui event
viewhandler (custom or 2-way binding lib) that
updates Model based on view
the View has a listenTo defined on the MOdel that was updated, which gets invoked
do whatever DOM-changes we want
Do DOM-changes directly in the viewhandler
Gui event
viewhandler (custom or 2-way binding lib) that
updates Model based on view
do whatever DOM-changes we want
the View has a listenTo defined on the MOdel that was updated, which gets invoked
The fist approach seems the most clean to me: it can use validation rules on the model, before changing the DOM and the flow just feels better. However, I can't find any good references to back this up. What is generally considered best practice here?
I wouldn't know what is Overall Internet Agreement on that topic, but working on a fairly large Backbone application with complex views, the first approach is what has proven the most maintainable.
One way to see it is that as you know, Backbone's Views and Backbone's Routers are kinda sharing the workload of what would be an actual Controller in a standard MVC (say, Rails). What that means is that in a view, you'll typically have the logic to translate GUI events (click on X) to Model Change (do Y) and the logic to translate Model Changes (new state after Y) into DOM changes. That doesn't mean the logic needs to be in the same place, those are two separate things. Your model may be shared among multiple views and you need your View to react regardless of where the change is originating. You could also be waiting for the server to answer whatever, and so, the DOM update would be in a callback anyway.
A pattern I have been using a lot lately is to have a _renderUpdating() to update the DOM to let the user know that his action has been taking into account (add a spinner near to whatever triggered the change), but I let events do the actual re-rendering once the server has returned the new model.
There is nothing that prevents you from going with the 2nd approach in simple cases, but as your application grow, you'll be very happy to have more than just a single render() that erases everything (including subviews) and you'll be looking at having a clean break between view -> model (which is the role of a controller) and model -> view to allow for more flexibility.
How to send data from one view to another which are on different urls?
A little bit strange question, but OK, let's try to imagine your problem in complex and give an answer.
Imagine application for reading books (like iBooks in web). We have one parent View called ApplicationView which creates several children views and some of them are BookshelfView (available on #bookshelf url) and BookView (available on #book/:id url).
Now, you mark your book as unread from your BookView and you know that your BookshelfView should change the appearance of this book. OK, it is not "moving data from one view to another". You just change the state of your model and your views catch this "change" event and update their html.
Let's describe more complicated situation. Imagine the same application. But at this time you decide to switch portrait orientation to landscape orientation. You make it in one view and you want this change to affect other application views. This can be done in several ways:
Views should stay loosely coupled you should use some kind of mediator pattern.
From Backbone 0.9.9 you can use global Backbone object as Mediator, as Backbone supports Events interface
You can create and additional model for Mediation between two or more views but there is more beautiful solution:
If you have one parent view for several child views you already have that mediator. Just send events to parent view from one view and listen to that events on parent from other view.
I also recommend you check this question