(function(root, factory) {
if (typeof define === "function" && define.amd) {
define(["backbone"], factory);
} else {
root.BackboneLayout = root.Backbone.Layout = factory();
}
}(this, function() {
Backbone-Layout.js 0.1.1
(c) 2013 Evan Stern Backbone-Layout may be freely distributed under the MIT license.
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define(["backbone"], factory);
} else {
root.BackboneLayout = root.Backbone.Layout = factory();
}
}(this, function() {
A managed view is simply a wrapper around a Backbone.View
. It stores
meta information about the view so that it can be appropriately managed
by the ViewManager
.
var ManagedView = function ManagedView(view, options) {
options || (options = {});
this.id = _.uniqueId("managed_");
this.view = view;
this.anchor = options.anchor;
this.replace = options.replace;
};
The view manager keeps track of the views that have been registered with a BackboneLayout. It is the interface to those views.
Views that are in the view manager will have their events bubbled up to the parent view, they will be rendered when the parent is rendered and closed when the parent is closed.
var ViewManager = function() {
var views = [];
Register a view with the view manager. This begins the management of the view.
Available options:
anchor
A jQuery selector string specifying the anchor element for the view relative to the parent view. (i.e. where the view will be rendered)
replace
A boolean flag specifying whether or not to replace the anchor
with the view's element.
Defaults to false
If no anchor is given, the view will be managed as normal but the view's element will not be added to the DOM.
Examples:
// The view will be appended to $(".container", this.el)
this.registerView(view, {anchor: ".container"})
// The view will replace $(".container", this.el)
this.registerView(view, {anchor: ".container", replace: true});
// The view will be registered but not added to the DOM
this.registerView(view);
this.register = function(view, options) {
options || (options = {});
Bubble up all the events from this view.
this.listenTo(view, "all", function() {
this.trigger.apply(this, arguments);
}, this);
var mView = new ManagedView(view, options);
views.push(mView);
};
this.each = function(iterator, context) {
_.each(this.getViews(), iterator, context);
};
this.getViews = function() {
return views;
};
_.bindAll(this, "register", "each");
};
_.extend(ViewManager.prototype, Backbone.Events);
When extending BackboneLayout there are a few interface methods that can optionally be defined.
beforeRender
This is the first function that is run during the
render()
call.
afterRender
This is the last function that is run during the
render()
call.
Examples:
var MyLayout = BackboneLayout.extend({
beforeRender: function() {
// executed before rendering itself or its children
}
, afterRender: function() {
// executed after rednering itself and its children
}
});
var BackboneLayout = Backbone.View.extend({
Pre-processing to make sure the BackboneLayout has all the data and attributes we expect it to have.
constructor: function(options) {
options || (options = {});
this.viewManager = new ViewManager();
Process defaults
similarly to Backbone.Model
var MyLayout = BackboneLayout.extend({
defaults: {
'foo': 'bar'
}
});
var layout = new MyLayout();
layout.options.foo
>> 'bar'
if (this.defaults) {
_.defaults(options, _.result(this, "defaults"));
}
Serialize can be overridden via the options
var layout = new BackboneLayout({
serialize: function() {
return {"foo": "bar"};
}
});
layout.serialize();
>> {"foo": "bar"}
if (options.serialize) {
this.serialize = options.serialize;
}
Restore pre Backbone 1.1.0 this.options
functionality
this.options = options;
Backbone.View.prototype.constructor.call(this, options);
}
When initializing a BackboneLayout
it is good practice to define
any nested views so that they can be rendered as soon as this view
is rendered.
Example:
var MyLayout = BackboneLayout.extend({
initialize: function(options) {
BackboneLayout.prototype.initialize.call(this, options);
this.registerView(new NestedView(), {
anchor: ".nested-view"
, replace: true
});
}
});
See ViewManager
and BackboneLayout.render
for more details on
how nested views are rendered.
, initialize: function(options) {
Bubble up anything the view manager triggers
this.listenTo(this.viewManager, "all", function() {
this.trigger.apply(this, arguments);
}, this);
Backbone.View.prototype.initialize.call(this, options);
}
Register a view with the ViewManager
. This will specify an
explicit parent-child relationship between this view and the view
added.
See ViewManager.register
for more information.
, registerView: function(view, options) {
options || (options = {});
this.viewManager.register(view, options);
}
Render this view and all child views registered with thie view manager.
The order of operations is:
1) Call beforeRender
2) Add this view to the DOM (if we can)
3) Recurse through all nested views and render them
4) Call afterRender
, render: function() {
this.beforeRender && this.beforeRender();
if (this.template) {
this.$el.empty().append(this.template(this.serialize()));
}
this.viewManager.each(function(managed, index, list) {
var view = managed.view;
view.render();
if (managed.anchor) {
var anchor = $(managed.anchor, this.el);
if (managed.replace) {
anchor.replaceWith(view.el);
} else {
anchor.html(view.el);
}
}
}, this);
this.afterRender && this.afterRender();
return this;
}
Before closing this view, close all the child views. This prevents those views from becoming orphaned and prevents zombie binds from persisting.
, close: function() {
this.viewManager.each(function(managed, index, list) {
view = managed.view;
view.close && view.close();
});
this.trigger("close", this);
this.unbind();
this.stopListening();
this.remove();
this.onClose && this.onClose();
return this;
}
Returns an object of data. This becomes the template context object. It is common practice to re-define this method as needed.
Can be overridden via the options
argument at instantiation time
(see BackboneLayout.initialize
)
, serialize: function() {
return this.model ? this.model.toJSON() : {};
}
});
return BackboneLayout;
}));