.. inspired from Rest and HATEOAS
Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.
There's more than one way do do it
Backbone provides only for Models or Collection of Models structures.
For each Model of known type, re-assigne proper Obix Model
var Objs = Backbone.Collection.extend({
...
model : function(attrs, option) {
switch (attrs.type) {
case "abstime":
return new Abstime(attrs, option);
...
}
}
...
})
By default parse() convert response to a hash of attributes, here, we convert it into a Model when appropriate.
var Obj = Backbone.Model.extend({
...
parse : function(response, options) {
if (response !== null) {
...
if (response.childrens !== null)
response.childrens = new Objs(response.childrens, {
urlRoot : _.result(this, 'urlRoot'),
parent : this
});
...
}
return response;
},
...
})
Events are not generated on nested attributes
last solution chosen because of the nested nature of Obix models
For each attribute property that is an Model, invoke set() to generate the events.
var Obj = Backbone.Model.extend({
set: function(key, val, options) {
...
// For each `set` attribute, update or delete the current value.
for (attr in attrs) {
val = attrs[attr];
var type = Object.prototype.toString.call(current[attr]);
if( current[attr] instanceof Objs && val instanceof Objs){
current[attr].set(val.models,options);
} else {
...
}
}
...
}
})
No binding mechanism for generated events betwen DOM element and model
On model initialisation, create a new binder.
initialize : function() {
...
// initialize Backbone.ModelBinder for dual binding
this.modelbinder = new ModelBinder();
...
}
once the view is rendered, bind models attributes to dom elements
onRender : function() {
this.modelbinder.bind(this.model, this.el, {
count: {selector: '[name=count]'},
});
...
},
View management is very light in Backbone
To avoid re-creating the wheel, use a third part plugin such as Chaplin, or ...
An library on top of Backbone that provides bricks to build large scale web app.
Use Marionnette's region for subviews placement
regions : {
header : '#header',
content : '#content',
footer : '#footer'
},
Insert subview when ready
layout.header.show(new HeaderView({
model : about
}));
layout.footer.show(new FooterView({
model : about
}));
var HeaderView = Marionette.ItemView.extend({
...
historyMenuClicked : function() {
this.ui.lobbyMenu.parent().removeClass('active');
this.ui.watchesMenu.parent().removeClass('active');
this.ui.historyMenu.parent().addClass('active');
ventAggr.trigger("app:goToHistory");
...
});
headerview.js is controller of the header, listen to history tab being click. We need to 'activate' through css the menu, Bootstrap failing to do this in our setup.
var app = new Marionette.Application();
app.on('start', function() {
...
ventAggr.on("app:goToHistory", function() {
if( Backbone.history.fragment != "history" ) {
Backbone.history.navigate("history", {});
app.controller.goToHistory();
}
});
...
});
Require.js used to link them at runtime and optimization phase
I've got big walls ..
... wi-fi don't go well throught them
Example: module declaration
define(['backbone', 'marionette', 'underscore', 'eventaggr', 'views/obixview','models/obix', 'models/watcheslocal', 'models/watches'], function(
Backbone, Marionette, _, ventAggr, ObixView, Obix, WatchesLocal, Watches) {
var AppController = Marionette.Controller.extend({
...
});
return AppController;
});
Example: loading a module asynchronously
goToHistory : function() {
var controller = this;
require(['views/historyview'], function(HistoryView) {
controller.obixView.content.show(new HistoryView());
});
}
No optimisation
Redefine your modules contents
Enter your modules definitions in require.js config inside your grunt.js file (if your are using Grunt)
requirejs: {
compile_build: {
options: {
optimize:"uglify",
optimizeCss: "standard",
modules : [
{name: 'main', include: ["i18n", "text",'courier','moment','mediaenquire','jquery.enterkeyevent']},
{name:'views/headerview', exclude: [...]},
{name:'views/footerview', exclude: [...]},
...
]
}
}
}
with optimisation
if( Modernizr.svg) {
var region = this.historyRegion;
require(['views/historyrollupchartview'], function(HistoryRollupChartView){
...
});
}
This avoid sending d3.js + xcharts.js + xcharts.css to the browser if it can't use it.
we use:
Maven launch Grunt, which in turn launch Bower, Modernizr an then RequireJs
We make use of a Jenkins platform at
Simply need to add and configure the NodeJS plugin in 'Administer Jenkins'
Server is deployed on target using
Application js served with npm serve on developemnt machine, make access through CORS to remote server.