On Github lathil / PtocetiSpaPres
.. inspired from Rest and HATEOAS
<obj href="http://localhost:8080/obix/rest/" is="obix:Lobby"> <op name="batch" href="/batch/" is="obix:Op" in="obix:BatchIn" out="obix:BatchOut"></op> <ref name="about" href="/about/" is="obix:About"></ref> <ref name="watchService" href="/watchservice/" is="obix:WatchService"></ref> </obj>
<obj name="about" href="http://localhost:8080/obix/rest/about/" status="ok" is="obix:About"> <abstime name="serverBootTime" val="2014-04-07T19:31:30+02:00"></abstime> <abstime name="serverTime" val="2014-04-07T21:58:42+02:00"></abstime> <str name="obixVersion" val=""></str> <str name="productName" val="Obix-Lib"></str> <uri name="productUrl" val="www.ptoceti.com"></uri> <str name="productVersion" val="1.0.0.SNAPSHOT"></str> <str name="vendorName" val="ptoceti"></str> <uri name="vendorUrl" val="www.ptoceti.com"></uri> <str name="serverName" val="org.ops4j.pax.web.pax-web-jetty-bundle"></str> </obj>
There's more than one way do do it
Backbone provides only for Models or Collection of Models structures.
override model : function(attrs, option) in Backbone.Collection and override parse() & implements initialise() in Backbone.CollectionObix responses are structured by Entities :
<obj href="http://localhost:8080/obix/rest/" is="obix:Lobby"> <op name="batch" href="/batch/" is="obix:Op" in="obix:BatchIn" out="obix:BatchOut"> <ref name="about" href="/about/" is="obix:About"> <ref name="watchService" href="/watchservice/" is="obix:WatchService"> </ref></ref></op></obj>
We map each entity to a model, that allows us to define and use helper functions :
var aboutRef = this.lobby.getAbout().getHref());
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
uses backbone-deep-model or override set() in Backbone.Modellast 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
Backbone let it to you to install your handlers Many plugins availables from Epoxy.js to Bacbone.ModelBinder to ease the painOn 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 }));
<div class="row"> <div class="col-md-8 col-sm-8 col-xs-10"> <p><span name="displayName" {{#if="" "contenteditable"="" }}contenteditable="true" {{="" if}}="">{{displayName}}</span></p> </div> <div class="col-md-3 col-sm-3 hidden-xs"> </div> <div class="col-md-1 col-sm-1 col-xs-2"> <p><a data-toggle="collapse" data-target="#monitoredDetails{{elemId}}"><span name="detailsCollapseControl" class="glyphicon glyphicon-chevron-down h3"></span></a></p> </div> </div>
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.