Skip to content

Instantly share code, notes, and snippets.

@richardm
Last active December 18, 2015 09:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richardm/5761343 to your computer and use it in GitHub Desktop.
Save richardm/5761343 to your computer and use it in GitHub Desktop.
Extending Backbone Views to add global namespaced events
// Basic App Setup
var App = window.App = {
Models: {},
Views: {},
Collections: {},
View: {} // set below
};
// Custom App View with global namespaced events
(function(){
// Global events handler
// It's a private global :-)
var GlobalEvents = _.extend({}, Backbone.Events); // could probably just use Backbone.Events
// App.View extends Backbone.View to provide global namespaced triggers
// Could probably just override Backbone.View
App.View = Backbone.View.extend({
constructor: function(options){
var options = _.extend({ns: 'global'}, options); // namespace defaults to global
App.View.__super__.constructor.call(this, options);
},
triggerGlobal: function(eventName, attrs){
GlobalEvents.trigger(this.options.ns + ':' + eventName, attrs);
},
handleGlobal: function(eventName, callback){
this.listenTo(GlobalEvents, this.options.ns + ':' + eventName, callback );
},
// Preserves namespace for sub-views
subView: function(viewName, options){
var options = _.extend({ ns: this.options.ns }, options);
return new viewName(options);
}
});
})();
// App components...
App.View.Container = App.View.extend({
initialize: function(){
this.handleGlobal('selectItem', this.updateSelectedItem);
},
render: function(){
var list = this.subView(App.View.List, { collection: ... });
this.$el.append(list.render().el);
},
updateSelectedItem: function(model){
// do something with the model corresponding to the clicked row
}
});
App.View.List = App.View.extend({
tagName: 'ul',
render: function(){
this.collection.each(function(item){
var itemView = this.subView(App.Views.ListItem, { model: item });
this.$el.append(itemView.render().el);
}, this);
return this;
}
});
App.View.ListItem = App.View.extend({
tagName: 'li',
events: {
'click': function(){ this.triggerGlobal('selectItem', this.model); }
},
render: function(){
this.$el.html(...); // create list item with values from this.model
return this;
}
});
// App logic
var leftPane = new App.View.Container({ ns: 'leftpane' });
// leftPane can now handle events triggered by leftPane -> list -> listitem
var rightPane = new App.View.Container({ ns: 'rightpane' });
// rightPane can now handle events triggered by rightPane -> list -> listitem
// Why bother?
// Clean design
// views only have references to immediate children
// children have no reference to parent
// Clean event handling
// triggering event in list item event handler allows us to pass model (cleaner than looking up model)
// global events object allows container to handle event (remember it has no reference to list items)
// Namespacing
// allows lists to trigger events without affecting event handling for other lists
// subView method allows sub views to automatically inherit parent's namespace with no clutter
// Real World use?
// Populate details pane when person is selected from list
// Update image preview when thumbnail is selected from list
// Reuse List / ListItem to do both with containers handling custom logic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment