PageRenderTime 74ms CodeModel.GetById 3ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

/backbone-stack.rst

https://github.com/htulipe/resthub.org
ReStructuredText | 2460 lines | 1676 code | 784 blank | 0 comment | 0 complexity | 21a7eaacc0dfaf4db1c73a3ef91673f5 MD5 | raw file
   1:tocdepth: 2
   2
   3=================
   4Backbone.js Stack
   5=================
   6
   7RESThub Backbone stack provides a client-side full stack and guidelines for building enterprise grade HTML5 applications. It could be used with any server backend: Ruby, PHP, NodeJS, JEE, Spring, Grails ...
   8
   9In addition to the existing librairies included in the stack, it provides additional functionalities (mainly Backbone.js addons) designed to allow you to build a real enterprise grade application, and described in this documentation.
  10
  11.. contents::
  12   :depth: 3
  13
  14The Backbone.js 2.1.1 stack includes the following librairies:
  15    * jQuery 1.9.1 (`documentation <http://docs.jquery.com/Main_Page>`_)
  16    * Backbone.js 1.0 (`documentation <http://documentcloud.github.com/backbone/>`_) and its `localstorage adapter 
  17      <http://documentcloud.github.com/backbone/docs/backbone-localstorage.html>`_
  18    * Underscore.js 1.4.4 (`documentation <http://documentcloud.github.com/underscore/>`_)
  19    * Underscore.String 2.3.0 (`documentation <https://github.com/epeli/underscore.string#readme>`_)
  20    * Require.js 2.1.5 with `i18n <http://requirejs.org/docs/api.html#i18n>`_ and `text <http://requirejs.org/docs/api.html#text>`_ plugins 
  21      (`documentation <http://requirejs.org/docs/api.html>`_)
  22    * Handlebars 1.0-rc3 (`documentation <http://handlebarsjs.com>`_)
  23    * A console shim + client logging to server mechanism
  24    * Twitter Bootstrap 2.3 (`documentation <http://twitter.github.com/bootstrap/>`_) and its JS plugins
  25    * Form Validation: `Backbone Validation`_
  26    * Parameters support on view routing: `Backbone Query Parameters`_
  27    * Datagrid: `Backbone Datagrid`_
  28    * Paginated lists: `Backbone Paginator`_
  29    * Asynchronous calls: Async_
  30    * Dispatching keyboard shortcuts: Keymaster_
  31    * Get and set relations (one-to-one, one-to-many, many-to-one) for Backbone models: `Backbone Relational`_
  32    * Parsing, validating, manipulating, and formatting dates: `Moment`_
  33
  34Before going deeper in the RESThub Backbone stack, you should read the great documentation `Developing Backbone.js Applications <http://addyosmani.github.com/backbone-fundamentals/>`_ by Addy Osmani, it is a great introduction to pure Backbone.js.
  35
  36Changelog
  37=========
  38
  39* 2013-03-26: `RESThub Backbone.js stack 2.1.0 has been released <https://github.com/resthub/resthub-backbone-stack/blob/master/CHANGELOG.rst>`_
  40* 2012-12-04: `RESThub Backbone.js stack 2.0.0 has been released <http://pullrequest.org/2012/12/04/resthub-2.html>`_!
  41* 2012-11-13: RESThub Backbone.js stack 2.0-rc4 has been released
  42* 2012-10-24: RESThub Backbone.js stack 2.0-rc3 has been released
  43* 2012-10-22: `RESThub Backbone.js stack 2.0-rc2 <https://github.com/resthub/resthub-backbone-stack/issues?milestone=4&state=closed>`_ has been released
  44* 2012-10-01: `RESThub 2.0-rc1 <https://github.com/resthub/resthub-backbone-stack/issues?milestone=3&state=closed>`_ has been released
  45* 2012-08-29: `RESThub 2.0-beta2 <https://github.com/resthub/resthub-backbone-stack/issues?milestone=1&state=closed>`_ has been released
  46
  47Bootstrap your project
  48======================
  49
  50There are 2 ways to use it in your project:
  51    * If you are starting a new RESThub Spring + Backbone stack project, the better way to use it is to use one of the Backbone.js webappp Maven Archetypes described `here <spring-stack.html#bootstrap-your-project>`_
  52    * You can simply download `latest RESThub Backbone.js stack <https://github.com/resthub/resthub-backbone-stack/archive/resthub-2.1.0.zip>`_, and extract it at the root of your webapp
  53
  54The `Todo RESThub example <https://github.com/resthub/todo-backbone-example>`_ project is the reference example project using this stack.
  55
  56Tutorial
  57========
  58
  59You should follow `RESThub Backbone Stack tutorial <tutorial/backbone.html>`_  in order to learn step by step how to use it.
  60
  61Project layout
  62==============
  63
  64Directories and filename conventions
  65------------------------------------
  66
  67Here is the typical RESThub Backbone.js stack based application directories and filename layout:
  68
  69.. code-block:: text
  70
  71    /
  72    ├── img
  73    ├── css
  74    │   ├── style.css
  75    │   ├── bootstrap.css
  76    │   ├── bootstrap-responsive.css
  77    ├── template
  78    │   ├── project
  79    │   │   ├── projects.hbs
  80    │   │   └── project-edit.hbs
  81    │   └── user
  82    │       ├── users.hbs
  83    │       └── user-edit.hbs
  84    ├── js
  85    │   ├── lib
  86    │   │   ├── async.js
  87    │   │   ├── backbone.js
  88    │   │   ├── ...
  89    │   │   └── resthub
  90    │   │       ├── backbone-resthub.js
  91    │   │       ├── backbone-validation-ext.js
  92    │   │       └── ...
  93    │   ├── model
  94    │   │   ├── user.js                                 var User = Backbone.Model.extend(...); return User;
  95    │   │   └── project.js                              var Project = Backbone.Model.extend(...); return Project;
  96    │   ├── collection
  97    │   │   ├── users.js                                var Users = Backbone.Collection.extend(...); return Users;
  98    │   │   └── projects.js                             var Projects = Backbone.Collection.extend(...); return Projects;
  99    │   ├── view
 100    │   │   ├── project
 101    │   │   │   ├── projects-view.js                    var ProjectsView = Resthub.View.extend(...); return ProjectsView;
 102    │   │   │   └── project-edit-view.js                var ProjectEditView = Resthub.View.extend(...); return ProjectEditView;
 103    │   │   └── user
 104    │   │       ├── users-view.js                       var UsersView = Resthub.View.extend(...); return UsersView;
 105    │   │       └── user-edit-view.js                   var UserEditView = Resthub.View.extend(...); return UserEditView;
 106    │   ├── router
 107    │   │   └── app-router.js                           var AppRouter = Backbone.Router.extend(...); return AppRouter;
 108    │   ├── app.js
 109    │   └── main.js
 110    └── index.html
 111
 112index.html
 113----------
 114
 115index.html is provided by RESThub Backbone stack, so you don't have to create it.
 116
 117.. code-block:: html
 118
 119    <!DOCTYPE html>
 120    <html lang="en">
 121        <head>
 122            <meta charset="utf-8">
 123            <title>RESThub Backbone.js Bootstrap</title>
 124            <meta name="viewport" content="width=device-width, initial-scale=1.0">
 125            <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
 126            <meta name="description" content="">
 127            <meta name="author" content="">
 128
 129            <link href="css/bootstrap.css" rel="stylesheet">
 130
 131            <!--[if lt IE 9]>
 132                <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
 133            <![endif]-->
 134        </head>
 135
 136        <body>
 137            <div id="main"> </div>
 138
 139            <!-- Placed at the end of the document so the pages would load faster -->
 140            <script data-main="js/main" src="js/lib/require.js"></script>
 141        </body>
 142    </html>
 143
 144main.js
 145-------
 146
 147This application bootstrap file is main.js located at your webapp root (usually src/main/webapp). The goal of this file is mainly to intialize require.js configuration. Your application code should not be here but in app.js (automatically loaded by main.js) in order to allow easy Backbone stack updates.
 148
 149Here's the default main.js file:
 150
 151.. code-block:: javascript
 152
 153    //Set the require.js configuration for your application.
 154    require.config({
 155    
 156        shim: {
 157            'underscore': {
 158                exports: '_'
 159            },
 160            'underscore-string': {
 161                deps: [
 162                    'underscore'
 163                ]
 164            },
 165            'handlebars-orig': {
 166                exports: 'Handlebars'
 167            },
 168            'backbone': {
 169                deps: [
 170                    'underscore',
 171                    'underscore-string',
 172                    'jquery'
 173                ],
 174                exports: 'Backbone'
 175            },
 176            'backbone-queryparams': {
 177                deps: [
 178                    'backbone'
 179                ]
 180            },
 181            'backbone-datagrid': {
 182                deps: [
 183                    'backbone'
 184                ],
 185                exports: 'Backbone.Datagrid'
 186            },
 187            'backbone-paginator': {
 188                deps: [
 189                    'backbone'
 190                ],
 191                exports: 'Backbone.Paginator'
 192            },
 193            'bootstrap': {
 194                deps: [
 195                    'jquery'
 196                ]
 197            },
 198            'backbone-relational': {
 199                deps: [
 200                    'backbone'
 201                ]
 202            },
 203            'keymaster': {
 204                exports: 'key'
 205            },
 206            'async': {
 207                exports: 'async'
 208            }
 209        },
 210    
 211        // Libraries
 212        paths: {
 213            jquery: 'lib/jquery',
 214            underscore: 'lib/underscore',
 215            'underscore-string': 'lib/underscore-string',
 216            backbone: 'lib/backbone',
 217            resthub: 'lib/resthub/resthub',
 218            localstorage: 'lib/localstorage',
 219            text: 'lib/text',
 220            i18n: 'lib/i18n',
 221            pubsub: 'lib/resthub/pubsub',
 222            'bootstrap': 'lib/bootstrap',
 223            'backbone-validation-orig': 'lib/backbone-validation',
 224            'backbone-validation': 'lib/resthub/backbone-validation-ext',
 225            'handlebars-orig': 'lib/handlebars',
 226            'handlebars': 'lib/resthub/handlebars-helpers',
 227            'backbone-queryparams': 'lib/backbone-queryparams',
 228            'backbone-datagrid': 'lib/backbone-datagrid',
 229            'backbone-paginator': 'lib/backbone-paginator',
 230            'backbone-relational': 'lib/backbone-relational',
 231            async: 'lib/async',
 232            keymaster: 'lib/keymaster',
 233            hbs: 'lib/resthub/require-handlebars',
 234            moment: 'lib/moment',
 235            template: '../template',
 236            console: 'lib/resthub/console'
 237        }
 238    });
 239    
 240    // Load our app module and pass it to our definition function
 241    require(['console', 'app']);
 242
 243**shim** config is part of `Require 2.0`_ and allows to `Configure the dependencies and exports for older, traditional "browser globals" scripts that do not use define() to declare the dependencies and set a module value`. See `<http://requirejs.org/docs/api.html#config-shim>`_ for more details.
 244
 245**path** config is also part of Require_ and allows to define paths for libs not found directly under baseUrl. See `<http://requirejs.org/docs/api.html#config-paths>`_ for details.
 246
 247RESThub suggests to **preload some libs** that will be used for sure as soon the app starts (dependencies required by Backbone itself and our template engine). This mechanism also allows us to load other linked libs transparently without having to define it repeatedly (e.g. ``underscore.string`` loading - this libs is strongly correlated to ``underscore`` - and merged with it and thus should not have to be defined anymore)
 248
 249app.js
 250-------
 251
 252app.js is where your application begins. You should customize it in order to initialize your routers and/or views.
 253
 254Here's the default app.js file:
 255
 256.. code-block:: javascript
 257
 258    define(['router/app-router'], function(AppRouter) {
 259        new AppRouter();
 260        // ...
 261    });
 262
 263Resthub.View
 264============
 265
 266RESThub Backbone stack provides an enhanced Backbone View named Resthub.View with the following functionalities:
 267    * Default render() with root and context attributes
 268    * Automatic view dispose + callbacks unbind when a view is removed from DOM
 269    * View model population from a form
 270
 271Default render() with root and context attributes
 272-------------------------------------------------
 273
 274Backbone views contain an $el attribute that represents the element (a div by default) where the template will be rendered, but it does not provide an attribute that represents the DOM element in which the view will be attached.
 275
 276In order to follow separation of concerns and encapsulation principles, RESThub Backbone stack manages a $root element in which the view will be attached. You should always pass it as constructor parameter, so as to avoid hardcoding view root elements. Like el, model or collection, it will be automatically as view attributes.
 277
 278.. code-block:: javascript
 279
 280    new MyView({root: this.$('.container'), collection: myCollection});
 281
 282In this example, we create the MyView view and attach it to the .container DOM element of the parent view. You can also pass a String selector parameter.
 283
 284.. code-block:: javascript
 285
 286    new MyView({root: '#container', collection: myCollection});
 287
 288RESThub provides a default implementation that will render your template with **model**, **collection** and **labels** as template attributes context if these properties are defined.
 289
 290.. code-block:: javascript
 291
 292    define(['underscore', 'resthub', 'hbs!template/my'], function(_, Resthub, myTemplate){
 293        var MyView = Resthub.View.extend({
 294
 295            template: myTemplate,
 296
 297            initialize: function() {
 298                _.bind(this.render, this);
 299                this.collection.on('reset', this.render, this);
 300            }
 301        });
 302    });
 303
 304A sample template with automatic collection provisionning:
 305
 306.. code-block:: html
 307
 308    <ul>
 309        {{#each collection}}
 310        <li>{{this.firstname}} {{this.name}}</li>
 311        {{/each}}
 312    </ul>
 313
 314Or with automatic model and labels provisionning:
 315
 316.. code-block:: html
 317
 318    <p>{{labels.user.identity}}: {{model.firstname}} {{model.name}}</li>
 319
 320After instantiation, ``this.$root`` contains a cached jQuery element and ``this.root`` the DOM element. By default, when render() is called, Backbone stack empties the root element, and adds el to the root as a child element. You can change this behaviour with the strategy parameter that could have following values:
 321    * replace: replace the content of $root with $el view content
 322    * append: append the content of $el at the end of $root
 323    * prepend: prepend the content of $el at the beginning of $root
 324
 325.. code-block:: javascript
 326
 327    var MyView = Resthub.View.extend({
 328
 329        template: myTemplate,
 330        tagName:  'li',
 331        strategy: 'append'
 332    });
 333
 334You can customize the rendering context by defining a context property:
 335
 336.. code-block:: javascript
 337
 338    var MyView = Resthub.View.extend({
 339
 340        template: myTemplate,
 341
 342        context: {
 343            numberOfElemnts: 42,
 344            collection: this.collection
 345        }
 346    });
 347
 348Or by passing a function if you need dynamic context:
 349
 350.. code-block:: javascript
 351
 352    var MyView = Resthub.View.extend({
 353
 354        template: myTemplate,
 355        labels: myLabels,
 356
 357        context: function() {
 358            var done = this.collection.done().length;
 359            var remaining = this.collection.remaining().length;
 360            return {
 361                total:      this.collection.length,
 362                done:       done,
 363                remaining:  remaining,
 364                labels:   this.labels
 365            };
 366        }
 367    });
 368
 369Or by passing the context as a render parameter when you call it explicitely:
 370
 371.. code-block:: javascript
 372
 373    this.render({messages: messages, collection: this.collection});
 374
 375If you need to customize the render() function, you can replace or extend it. Here is an example about how to extend it. This sample calls the default render method and adds children elements:
 376
 377.. code-block:: javascript
 378
 379    var MyView = Resthub.View.extend({
 380
 381        render: function() {
 382            // Call super render function with the same arguments
 383            MyView.__super__.render.apply(this, arguments);
 384            // Add child views
 385            this.collection.each(function(child) {
 386                this.add(child);
 387            }, this);
 388        },
 389        add: function(todo) {
 390            var childView = new ChildView({
 391                model: child,
 392                root: this.$('.childcontainer')
 393            });
 394        }
 395    });
 396
 397.. _backbone-dispose:
 398
 399Automatic view dispose + callbacks unbind
 400-----------------------------------------
 401  
 402RESThub offers an extension to this mechanism that listens on any removal in the ``view.el`` DOM element and **automatically calls stopListening() on remove**. This means that you don't have to manage this workflow anymore and any replacement done in el parent will trigger a dispose call.
 403
 404i.e.: each time a jQuery ``.html(something)``, ``.remove()`` or ``.empty()`` is performed on view el parent or each time a ``remove()`` is done on the el itself, **the view will be properly destroyed**.
 405
 406.. warning::
 407
 408    Since Backbone 0.9.10 (included in RESThub Backbone stack 2.1), you should use listenTo() and stopListening() instead of on() and off(), since it will allow Backbone.js to manage properly event listener cleanup.
 409
 410View model population from a form
 411---------------------------------
 412
 413`Backbone Validation`_ provides some helpers to validate a model against constraints. Backbone_ defines some methods (such as ``save``) to validate a model and then save it on the server. But neither `Backbone Validation`_ nor Backbone_ allow to fill a model stored in a view with form values. 
 414
 415RESThub comes with a really simple ``Backbone.View`` extension that copies each input field of a given form in a model. This helper is a new View method called ``populateModel()``. This function has to be explicitely called (e.g. before a ``save()``):
 416
 417.. code-block:: javascript
 418
 419    Resthub.View.extend({
 420
 421        ...
 422
 423        saveUser:function () {
 424            this.populateModel();
 425
 426            // save model if it's valid, display alert otherwise
 427            if (this.model.isValid()) {
 428                this.model.save(null, {
 429                    success:this.onSaveSuccess.bind(this),
 430                    error:this.onSaveError.bind(this)
 431                });
 432            }
 433        }
 434    });
 435
 436``populateModel`` searches for the form element provided and copies each form input value into the given model (matching the form input name to an model attribute name). API is: 
 437
 438.. code-block:: javascript
 439
 440    /** utility method providing a default and basic handler that
 441     * populates model from a form input
 442     *
 443     * @param form form element to 'parse'. Form parameter could be a css selector or a
 444     * jQuery element. If undefined, the first form of this view el is used.
 445     * @param model model instance to populate. If no model instance is provided,
 446     * search for 'this.model'
 447     */
 448    populateModel:function (form, model);
 449
 450So you can use it in multiple ways from your view: 
 451
 452.. code-block:: javascript
 453
 454    // take the first el form element and copy values into 'this.model' instance
 455    this.populateModel();
 456   
 457    // get the form element matching the provided selector (form with id "myForm") and copy values into 'this.model' instance
 458    this.populateModel("#myForm");
 459   
 460    // get the provided jquery form element and copy values into 'this.model' instance
 461    this.populateModel(this.$("#myForm");
 462   
 463    // take the first el form element and copy values into provided myModel instance
 464    this.populateModel(null, myModel);
 465   
 466    // get the form element matching the provided selector (form with id "myForm") and copy values into provided myModel instance
 467    this.populateModel("#myForm", myModel);
 468   
 469    // get the provided jquery form element and copy values into provided myModel instance
 470    this.populateModel(this.$("#myForm"), myModel);
 471
 472As said before, this approach could appear naive but will probably fit your needs in most cases. If not, you are free not to use this helper, to extend this method, globally or locally with your own logic or to use a third party lib to bind model and form (see `Backbone.ModelBinder <http://github.com/theironcook/Backbone.ModelBinder>`_ or `Rivets.js <http://rivetsjs.com/>`_ for instance).
 473
 474.. _templating:
 475
 476Templating
 477==========
 478
 479Handlebars
 480----------
 481
 482Client-side templating capabilities are based by default on Handlebars_.
 483
 484Templates are HTML fragments, without the <html>, <header> or <body> tag:
 485
 486.. code-block:: html
 487
 488    <div class="todo {{#if done}}done{{/if}}">
 489        <div class="display">
 490            <input class="check" type="checkbox" {{#if done}}checked="checked"{{/if}}/>
 491            <div class="todo-content">{{content}}</div>
 492            <span class="todo-destroy"></span>
 493        </div>
 494        <div class="edit">
 495            <input class="todo-input" type="text" value="{{content}}" />
 496        </div>
 497    </div>
 498
 499RequireJS Handlebars plugin
 500---------------------------
 501
 502Templates are injected into Views by the RequireJS Handlebars plugin, based on RequireJS text plugin. This hbs plugin will automatically **retrieve and compile** your template. So it should be defined in your main.js:
 503
 504.. code-block:: javascript
 505
 506    require.config({
 507        paths: {
 508            // ...
 509            text: 'lib/text',
 510            hbs: 'resthub/handlebars-require'
 511        }
 512    });
 513
 514Sample usage in a Backbone.js View:
 515
 516.. code-block:: javascript
 517
 518    define(['jquery', 'resthub', 'hbs!template/todo'],function($, Resthub, todoTmpl) {
 519        var TodoView = Resthub.View.extend({
 520
 521        //... is a list tag.
 522        tagName:  'li',
 523
 524        // Resthub.View will automtically Handlebars template with model or collection set in the context
 525        template: todoTmpl;
 526    });
 527
 528Helpers
 529-------
 530
 531Resthub provide some usefull **Handlebars helpers** included by default:
 532
 533ifinline
 534++++++++
 535
 536This helper provides a more fluent syntax for inline ifs, i.e. if embedded in quoted strings.
 537
 538As with Handlebars ``#if``, if its first argument returns ``false``, ``undefined``, ``null``
 539or ``[]`` (a "falsy" value), ``''`` is returned, otherwise ``returnVal`` argument is rendered.
 540
 541e.g:
 542
 543.. code-block:: html
 544
 545    <div class='{{ifinline done "done"}}'>Issue number 1</div>
 546
 547with the following context:
 548
 549.. code-block:: javascript
 550
 551    {done:true}
 552
 553will produce:
 554
 555.. code-block:: html
 556
 557    <div class='done'>Issue number 1</div>
 558
 559unlessinline
 560++++++++++++
 561
 562Opposite of ifinline helper.
 563
 564As with Handlebars ``#unless``, if its first argument returns ``false``, ``undefined``, ``null``
 565or ``[]`` (a "falsy" value), ``returnVal`` is returned, otherwise ``''`` argument is rendered.
 566
 567e.g:
 568
 569.. code-block:: html
 570
 571    <div class='{{unlessinline done "todo"}}'>Issue number 1</div>
 572
 573with the following context:
 574
 575.. code-block:: javascript
 576
 577    {done:false}
 578   
 579will produce:
 580
 581.. code-block:: html
 582
 583    <div class='todo'>Issue number 1</div>
 584
 585ifequalsinline
 586++++++++++++++
 587
 588This helper provides a if inline comparing two values.
 589
 590If the two values are strictly equals (``===``) return the returnValue argument, ``''`` otherwise.
 591
 592e.g:
 593
 594.. code-block:: html
 595
 596    <div class='{{ifequalsinline type "details" "active"}}'>Details</div>
 597
 598with the following context:
 599
 600.. code-block:: javascript
 601
 602    {type:"details"}
 603
 604will produce:
 605
 606.. code-block:: html
 607
 608    <div class='active'>Details</div>
 609
 610unlessequalsinline
 611++++++++++++++++++
 612
 613Opposite of ifequalsinline helper.
 614
 615If the two values are not strictly equals (``!==``) return the returnValue  argument, ``''`` otherwise.
 616
 617e.g:
 618
 619.. code-block:: html
 620
 621    <div class='{{unlessequalsinline type "details" "active"}}'>Edit</div>
 622
 623with the following context:
 624
 625.. code-block:: javascript
 626
 627    {type:"edit"}
 628
 629will produce:
 630
 631.. code-block:: html
 632
 633    <div class='active'>Edit</div>
 634
 635ifequals
 636++++++++
 637
 638This helper provides a if comparing two values.
 639
 640If only the two values are strictly equals (``===``) display the block
 641
 642e.g:
 643
 644.. code-block:: html
 645
 646    {{#ifequals type "details"}}
 647        <span>This is details page</span>
 648    {{/ifequals}}
 649
 650with the following context:
 651
 652.. code-block:: javascript
 653
 654    {type:"details"}
 655   
 656will produce:
 657
 658.. code-block:: html
 659
 660    <span>This is details page</span>
 661
 662unlessequals
 663++++++++++++
 664
 665Opposite of ifequals helper.
 666
 667If only the two values are not strictly equals (``!==``) display the block
 668
 669e.g:
 670
 671.. code-block:: html
 672
 673    {{#unlessequals type "details"}}
 674        <span>This is not details page</span>
 675    {{/unlessequals}}
 676
 677with the following context:
 678
 679.. code-block:: javascript
 680
 681    {type:"edit"}
 682   
 683will produce:
 684
 685.. code-block:: html
 686
 687   <span>This is not details page</span>
 688
 689for
 690+++
 691
 692This helper provides a for i in range loop.
 693
 694start and end parameters have to be integers >= 0 or their string representation. start should be <= end.
 695In all other cases, the block is not rendered.
 696
 697e.g:
 698
 699.. code-block:: html
 700
 701    <ul>
 702        {{#for 1 5}}
 703            <li><a href='?page={{this}}'>{{this}}</a></li>
 704        {{/for}}
 705    </ul>
 706
 707will produce:
 708
 709.. code-block:: html
 710
 711    <ul>
 712        <li><a href='?page=1'>1</a></li>
 713        <li><a href='?page=2'>2</a></li>
 714        <li><a href='?page=3'>3</a></li>
 715        <li><a href='?page=4'>4</a></li>
 716        <li><a href='?page=5'>5</a></li>
 717    </ul>
 718
 719.. _sprintf-helper:
 720
 721sprintf
 722+++++++
 723
 724This helper allows to use sprintf C like string formatting in your templates. It is based on `Underscore String <https://github.com/epeli/underscore.string>`_ implementation. A detailed documentation is available `here <http://www.diveintojavascript.com/projects/javascript-sprintf>`_.
 725
 726e.g:
 727
 728.. code-block:: html
 729
 730   <span>{{sprintf "This is a %s" "test"}}</span>
 731
 732will produce:
 733
 734.. code-block:: html
 735
 736   <span>This is a test</span>
 737
 738This helper is very usefull for Internationalization_, and can take any number of parameters.
 739
 740modulo
 741++++++
 742
 743This helper provides a modulo function.
 744
 745If (n % m) equals 0 then the block is rendered, and if not, the else block is rendered if provided.
 746
 747e.g:
 748
 749.. code-block:: html
 750
 751    {{#modulo index 2}}
 752        <span>{{index}} is even</span>
 753    {{else}}
 754        <span>{{index}} is odd</span>
 755    {{/modulo}}
 756
 757with the following context:
 758
 759.. code-block:: javascript
 760
 761    {index:10}
 762
 763will produce:
 764
 765.. code-block:: html
 766
 767    <span>10 is even</span>
 768
 769formatDate
 770++++++++++
 771
 772This helper provides a date formatting tool.
 773The date will be parsed with the inputPattern and then formatted with the outputPattern.
 774
 775Parameters are:
 776
 777* date: the date to parse and format
 778* outputPattern: the pattern used to display the date (optional)
 779* inputPattern: the pattern used to parse the date (optional)
 780
 781inputPattern and outputPattern are optionals: the default pattern is 'YYYY-MM-DD HH:mm:ss'
 782
 783Full documentation about date format can be found `here <http://momentjs.com/docs/#/displaying/format/>`_.
 784
 785e.g:
 786
 787.. code-block:: html
 788
 789    <span>{{formatDate myDate pattern}}</span>
 790
 791with the following context:
 792
 793.. code-block:: javascript
 794
 795    { myDate: new Date(), pattern: '[today] MM/DD/YYYY' }
 796   
 797will produce:
 798
 799.. code-block:: html
 800
 801    <span>today 10/24/2012</span>
 802
 803and:
 804
 805.. code-block:: html
 806
 807    <span>{{formatDate myDate outputPattern inputPattern}}</span>
 808
 809with the following context:
 810
 811.. code-block:: javascript
 812
 813    { myDate: '2012/17/02 11h32', inputPattern: 'YYYY/DD/MM HH\\hmm', outputPattern: 'HH:mm, MM-DD-YYYY' }
 814   
 815will produce:
 816
 817.. code-block:: html
 818
 819    <span>11:32, 02-17-2012</span>
 820
 821.. _backbone-pushstate:
 822   
 823Backbone effective pushState extension
 824======================================
 825
 826Backbone_ allows ``pushState`` activation that permits usage of real URLs instead of `#` anchors.
 827PushState offers a better navigation experience, better indexation and search engine ranking:
 828
 829.. code-block:: javascript
 830
 831   Backbone.history.start({pushState:true, root:"/"});
 832
 833
 834The `root` option defines the path context of our Backbone_ application;
 835
 836However, Backbone_ stops here. Direct access to views by URL works fine but, each link leads to
 837**a full reload**! Backbone_ does not intercept html links events and it is necessary to implement it ourselves.
 838
 839Branyen Tim, the creator of `Backbone boilerplate <http://github.com/tbranyen/backbone-boilerplate>`_ shares the following solution that RESThub integrates in its extensions with an additional test to check pushState activation.
 840
 841If ``Backbone.history`` is started with the ``pushState`` option, **any click on a link will be intercepted and bound to a Backbone navigation instead**. If you want to provide **external links**, you only have to use the ``data-bypass`` attribute:
 842
 843.. code-block:: html
 844
 845    <a data-bypass href="http://github.com/bmeurant/tournament-front" target="_blank">
 846
 847.. _backbone-form-helper:
 848
 849Internationalization
 850====================
 851
 852You should never use directly labels or texts in your source files. All labels should be externalized in order to prepare your 
 853application for internationalization. Doing such thing is pretty simple with RESThub Backbone.js stack thanks to `requireJS i18n plugin <http://requirejs.org/docs/api.html#i18n>`_.
 854
 855Please find below the steps needed to internationalize your application.
 856
 8571. **Configure i18n plugin**
 858
 859In your main.js file you should define a shortcut path for i18n plugin and the default language for your application:
 860
 861.. code-block:: javascript
 862
 863    require.config({
 864        paths: {
 865            // ...
 866            i18n: "lib/i18n"
 867        },
 868        locale: localStorage.getItem('locale') || 'en-us'
 869    });
 870
 871
 8722. **Define labels**
 873
 874Create a labels.js file in the js/nls directory, it will contain labels in the default locale used by your application. You can change labels.js to another name (messages.js or functionality related name like user.js or product.js), but js/nls is the default location.
 875
 876Sample js/nls/labels.js file:
 877
 878.. code-block:: javascript
 879
 880    define({
 881        // root is mandatory.
 882        'root': {
 883            'titles': {
 884                'login': 'Login'
 885            }
 886        },
 887        "fr-fr": true
 888    });
 889
 890Add translations in subfolders named with the locale, for instance js/nls/fr-fr ...
 891You should always keep the same file name, and the file located at the root will be used by default.
 892
 893Sample js/nls/fr-fr/labels.js file:
 894
 895.. code-block:: javascript
 896
 897    define({
 898        'titles': {
 899            'login': 'Connexion'
 900        }
 901    });
 902
 9033. **Use it**
 904
 905Add a dependency in the js, typically a View, where you'll need labels. You'll absolutely need to give a scoped variable to the result (in this example ``myLabels``, but you can choose the one you want). 
 906
 907Prepending 'i18n!' before the file path in the dependency indicates RequireJS to get the file related to the current locale:
 908
 909.. code-block:: javascript
 910
 911    define(['i18n!nls/labels'], function(myLabels) {
 912        // ...
 913
 914        labels: myLabels,
 915
 916        // ...
 917    });
 918
 919In your html template:
 920
 921.. code-block:: html
 922
 923    <div class="title">
 924        <h1>{{labels.titles.login}}</h1>
 925    </div>
 926
 9274. **Change locale**
 928
 929Changing locale require a page reloading, so it is usually implemented with a Backbone.js router configuration like the following one:
 930
 931.. code-block:: javascript
 932
 933    define(['backbone'], function(Backbone){
 934        var AppRouter = Backbone.Router.extend({
 935            routes: {
 936                'fr': 'fr',
 937                'en': 'en'
 938            },
 939            fr: function( ){
 940                var locale = localStorage.getItem('locale');
 941                if(locale != 'fr-fr') {
 942                    localStorage.setItem('locale', 'fr-fr'); 
 943                    location.reload(); 
 944                }
 945            },
 946            en: function( ){
 947                var locale = localStorage.getItem('locale');
 948                if(locale != 'en-us') {
 949                    localStorage.setItem('locale', 'en-us'); 
 950                    location.reload();
 951                }
 952            }
 953        });
 954
 955        return AppRouter;
 956    });
 957
 9585. **sprintf to the rescue**
 959
 960Internalionalization can sometimes be tricky since words are not always in the same order depending on the language. To make your life easier, RESThub backbone stack includes Underscore.String. It contains a sprintf function that you can use for your translations.
 961
 962You can use the ``_.sprintf()`` function and the ``sprintf`` helper to have substitutions in your labels.
 963
 964labels.js
 965
 966.. code-block:: javascript
 967
 968    'root': {
 969        'clearitem': "Clear the completed item",
 970        'clearitems': 'Clear %s completed items',
 971    }
 972
 973RESThub also provides a ``sprintf`` handlebars helper to use directly in your templates (cf. :ref:`sprintf-helper`):
 974
 975.. code-block:: html
 976
 977    {{#ifequals done 1}} {{messages.clearitem}} {{else}} {{sprintf messages.clearitems done}} {{/ifequals}}
 978
 979Logging
 980=======
 981
 982RESThub Backbone stack include a console.js implementation responsible for 
 983 * Creating console.* functions if they do not exists (old IE versions)
 984 * Optionnaly sending logs to the server, in order to make JS error tracking and debugging easier
 985
 986 In order to send logs to the server, import console.js in your main.js (already done by default):
 987
 988.. code-block:: javascript
 989
 990    // Load our app module and pass it to our definition function
 991    require(['console', 'app']);
 992
 993In your app.js, you can define different console.level values, which define what log level will be sent to the server:
 994
 995.. code-block:: javascript
 996
 997    console.level = 'off';   // Default, no log are sent to the server
 998    console.level = 'debug'; // debug, info, warn and error logs are sent to the server
 999    console.level = 'info';  // info, warn and error logs are sent to the server
1000    console.level = 'warn';  // warn and error logs are sent to the server
1001    console.level = 'error'; // error logs are sent to the server
1002
1003Javascript syntax error are also sent to the server with an error log level.
1004
1005You can customize the log server url:
1006
1007.. code-block:: javascript
1008
1009    console.serverUrl = 'api/log'; // Default value
1010
1011Log are sent thanks a POST request with the following JSON body:
1012
1013.. code-block:: javascript
1014
1015    {"level":"warn","message":"log message","time":"2012-11-13T08:18:52.972Z"}
1016
1017RESThub web server provide a builtin implementation of the serverside logging webservice, see the `related documentation <spring-stack.html#client-logging>`_ for more details.
1018  
1019Message bus
1020-----------
1021
1022Since backbone now extends Events, you can use it as a message bus for your global events.
1023In order to facilitate global events usage in Backbone Views, RESThub provides some syntactic sugar in ``Resthub.View``.
1024
1025Backbone Views events hash parsing has been extended to be capable of declaring global events as it is already done for DOM events binding. To declare such global events in your Backbone View, you only have to add it in events hash:
1026
1027.. code-block:: javascript
1028
1029    events:{
1030        // regular DOM event bindings
1031        "click #btn1":"buttonClicked",
1032        "click #btn2":"buttonClicked",
1033        // global events
1034        "!global":"globalFired",
1035        "!global1":"globalFired",
1036        "!globalParams":"globalFiredParams"
1037    },
1038
1039Please note that it is mandatory to prefix your global events with ``!`` to differenciate them from DOM events.
1040Under the cover, listenTo() and stopListening() are used so events cleanup will be done automatically by the view.
1041   
1042.. _resthub-validation:
1043   
1044Validation
1045==========
1046
1047Since 2.1.0, RESThub comes with custom server and client validation handlers allowing to export, via a dedicated API, the
1048server side declared validation constraints (see `Spring Stack documentation <./spring-stack.html#validation-api>`_) and 
1049to interpret these constraints on the client side.
1050
1051This feature allows to define once (server side) your validation constraints that will be (if configured)
1052automatically mapped on the client side to effective `Backbone Validation`_ (see also :ref:`backbone-validation`)
1053constraints.
1054
1055Server side declared constraint validations will thus be fully reused and you won't have to 'clone' these
1056constraints on the client side.
1057
1058Usage
1059-----
1060
1061This feature is available by default but not active unless explicit configuration.  
1062
1063Activate synchronization
1064++++++++++++++++++++++++
1065
1066Before any server side validation constraint reuse on any of your client models, **you have to 
1067implement or customize your model** ``initialize()`` **function** to call the ``Resthub.Validation`` namespace
1068``synchronize`` function:   
1069
1070.. code-block:: javascript
1071
1072    var UserModel = Backbone.Model.extend({
1073
1074        className: 'org.resthub.validation.model.User',
1075
1076        initialize: function() {
1077            Resthub.Validation.synchronize(UserModel);
1078        }
1079
1080    });
1081    
1082
1083This function takes the current model as a mandatory parameter. It accepts also an optional parameter
1084``errorCallback`` (cf. :ref:`validation-errors`).
1085
1086
1087Activate Backbone Validation in views
1088+++++++++++++++++++++++++++++++++++++
1089
1090RESThub Validation will be effective only if Backbone Validation is correctly configured in view 
1091(see :ref:`backbone-validation`). For instance: 
1092
1093.. code-block:: javascript
1094
1095    var UserView = Resthub.View.extend({
1096
1097        // Define view template
1098        template: userTemplate,
1099
1100        events: {
1101          'submit form': 'onSubmitForm'
1102        },
1103
1104        initialize: function() {
1105          // Initialize the model
1106          this.model = new User();
1107
1108          Backbone.Validation.bind(this);
1109
1110          this.render();
1111        },
1112
1113        onSubmitForm: function(event) {
1114            ...
1115            
1116            this.save();
1117        },
1118
1119        save: function() {
1120            this.populateModel();
1121
1122            if (this.model.isValid()) {
1123                // ...
1124            } else {
1125                // ...
1126            }
1127        }
1128
1129    });
1130    
1131    
1132This code sample is taken from a complete validation sample that you can find 
1133`here <https://github.com/bmeurant/resthub-validation-sample>`_. Don't hesitate to checkout this sample
1134to see working samples.
1135
1136.. _validation-lifecycle:
1137    
1138Lifecycle
1139+++++++++
1140
1141Doing this, all validation constraints will be **transparently synchronized from the server during a model instantiation** 
1142(i.e. ``new UserModel()``). A GET request will be thus sent to the server with the given className
1143to get server validation constraints.
1144
1145Resthub Validation optimizes this process by sending the GET request **only on the first model instantiation**. So
1146constraints validation synchronization will only be performed on the first instantiation of a given model - deduced 
1147Backbone Validation constraints will be **reused accross all instances of this model**.
1148
1149Note that the synchronization process will be **reset after a locale update** (see :ref:`validation-change-locale`) or
1150could be **manually forced** (see below).
1151
1152Force synchronization
1153#####################
1154
1155Synchronization of a given model (in fact, on a given class name) could be forced using a dedicated ``Resthub.Validation``
1156namespace function: ``forceSynchroForClass``.
1157
1158.. code-block:: javascript
1159
1160    Resthub.Validation.forceSynchroForClass("org.resthub.validation.model.User");
1161    
1162    
1163This function must be called with a mandatory parameter *className* corresponding to the declared model 
1164className (see :ref:`validation-options`).
1165
1166This operation resets the synchronized information for the given className, this means that **the GET request 
1167(and constraint binding) will be sent again on the next model instantiation**.
1168
1169.. _validation-options:
1170    
1171Parameters & Options
1172++++++++++++++++++++
1173
1174You can configure or parametrize RESThub Validation with a set of parameters and options.
1175
1176API url
1177#######
1178
1179The validation **api base url can be configured in** ``Resthub.Validation`` namespace ``options.apiUrl`` :
1180
1181.. code-block:: javascript
1182
1183    Resthub.Validation.options.apiUrl = 'new/url';
1184    
1185
1186Default value is ``'api/validation'``.
1187
1188
1189className
1190#########
1191
1192**Each model to be synchronized must hold a className attribute** containing the complete qualified name of the
1193corresponding Java class (i.e. package + name. see `Spring Stack documentation <./spring-stack.html#validation-api>`_).
1194
1195.. code-block:: javascript
1196
1197    var UserModel = Backbone.Model.extend({
1198
1199        className: 'org.resthub.validation.model.User',
1200
1201        initialize: function() {
1202            Resthub.Validation.synchronize(UserModel);
1203        }
1204        
1205        ...
1206        
1207    });
1208    
1209
1210messages
1211########
1212
1213You can provide an key/value pair object ``messages`` to any of your model or globally in ``Resthub.Validation`` namespace
1214to specify custom error messages that will replace default messages from server (see :ref:`validation-messages` for details).
1215    
1216.. code-block:: javascript
1217
1218    var UserModel = Backbone.Model.extend({
1219
1220        className: 'org.resthub.validation.model.User',
1221        messages: {
1222            'validation.Min.message': 'should be greater than {value} or equals'
1223        },
1224        
1225        initialize: function() {
1226            Resthub.Validation.synchronize(UserModel);
1227        }
1228
1229        ...
1230        
1231    });
1232
1233includes / excludes
1234###################
1235
1236By default, **all constraints exported by the server API are mapped** and converted into Backbone Validation constraints
1237and then added as active validation constraints on the client side.
1238
1239You can configure this behaviour **for each of your model by specifying includes or excludes retrictions on it**. 
1240
1241Only properties names found in an **includes** array will be **mapped** :
1242
1243.. code-block:: javascript
1244
1245    var UserModel = Backbone.Model.extend({
1246
1247        className: 'org.resthub.validation.model.User',
1248        includes: ['login', 'firstName', 'lastName'],
1249
1250        initialize: function() {
1251            Resthub.Validation.synchronize(UserModel);
1252        }
1253        
1254        ...
1255        
1256    });
1257    
1258
1259Each property name found in an **excludes** array will be **ignored** :
1260
1261.. code-block:: javascript
1262
1263    var UserModel = Backbone.Model.extend({
1264
1265        className: 'org.resthub.validation.model.User',
1266        excludes: ['password'],
1267
1268        initialize: function() {
1269            Resthub.Validation.synchronize(UserModel);
1270        }
1271        
1272        ...
1273        
1274    });
1275
1276
1277Server constraints mapping
1278--------------------------
1279
1280Once all server validation constraints retrieved from server, RESThub Validation tries to map each constraint to
1281a valid Backbone Validation constraint, if supported.
1282
1283.. _validation-supported-constraints:
1284
1285Supported constraints
1286+++++++++++++++++++++
1287
1288Supported constraints are described below. You will find in this chapter the description of the mapped constraints
1289and the way it is mapped to a Backbone Validation constraint.
1290
1291If the client receive a non supported server validation constraint, it will be ignored unless you provide a specific
1292and custom constraint validator (see :ref:`validation-add-constraint`).
1293
1294NotNull
1295#######
1296
1297    The property must not be undefined or null and, in case of String cannot be neither empty ("") 
1298    nor blank ("   ").
1299
1300NotBlank or NotEmpty
1301####################
1302
1303    The property must not be undefined or null, in case of String cannot be neither empty ("") 
1304    nor blank ("   "), in case of array cannot be empty.
1305
1306Null
1307####
1308
1309    The property must be null or undefined or, in case of String, empty ("") or blank ("   ").
1310
1311AssertTrue
1312##########
1313
1314    The property must be either a boolean to ``true`` or a String equals to ``"true"``.
1315
1316    null values are considered valid.
1317
1318AssertFalse
1319###########
1320
1321    The property must be either a boolean to ``false`` or a String different of ``"true"``.
1322
1323Size
1324####
1325
1326    The property must be a String or an array with size between the specified boundaries (included).
1327
1328    null values are considered valid.
1329
1330    available parameters:
1331        - *min*: size the property must be higher or equal to
1332        - *max*: size the property must be lower or equal to
1333
1334
1335Min
1336###
1337
1338    The property must be an integer number whose value must be higher or equal to the specified minimum.
1339
1340    null values are considered valid.
1341
1342    available parameters:
1343        - *value*: value the property must be higher or equal to
1344    
1345DecimalMin
1346##########
1347
1348    The property must be floating number whose value must be higher or equal to the specified minimum.
1349
1350    null values are considered valid.
1351
1352    available parameters:
1353        - *value*: value the property must be higher or equal to
1354
1355Max
1356###
1357
1358    The property must be an integer number whose value must be lower or equal to the specified minimum.
1359
1360    null values are considered valid.
1361
1362    available parameters:
1363        - *value*: value the property must be lower or equal to
1364
1365DecimalMax
1366##########
1367
1368    The property must be an integer number whose value must be lower or equal to the specified minimum.
1369
1370    null values are considered valid.
1371
1372    available parameters:
1373        - *value*: value the property must be lower or equal to
1374
1375Pattern
1376#######
1377
1378    The property must match the specified regular expression.
1379
1380    null values are considered valid.
1381
1382    available parameters:
1383        - *regexp*: regular expression to match
1384
1385URL
1386###
1387
1388    The property must represent a valid URL. Parameters allow to verify specific parts of the parsed URL.
1389    Per default the property must match ``/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[.\!\/\\w]*))?)/``
1390
1391    null values are considered valid.
1392
1393    available parameters: 
1394        - *protocol*: specify the protocol the property must match. Per default any protocol is allowed.
1395        - *host*: specify the host regexp the property must match. Per default any host is allowed.
1396        - *port*: specify the port the property must match. Per default any port is allowed.
1397
1398        
1399options
1400~~~~~~~
1401
1402You can **customize URL validator pattern** to match by overriding ``Resthub.Validation.options.URL.pattern``: 
1403
1404.. code-block:: javascript
1405
1406   Resthub.Validation.options.URL.pattern = /my pattern/; 
1407
1408   
1409Range
1410#####
1411
1412    The property must be numeric values or string representation of the numeric value with value between specified range.
1413    
1414    available parameters: 
1415        - *min*: value the property must be higher or equal to
1416        - *max*: value the property must be lower or equal to
1417
1418        
1419Length
1420######
1421
1422    The property must be a string with length between min and max included.
1423    
1424    available parameters: 
1425        - *min*: value the property length must be higher or equal to
1426        - *max*: value the property length must be lower or equal to
1427        
1428
1429Email
1430#####
1431
1432    The property must be a valid email (see `Backbone Validation built in email pattern constraint <https://github.com/thedersen/backbone.validation#pattern>`_).
1433
1434CreditCardNumber
1435################
1436
1437    The property must be a valid credit card number according `Lunh algorithm <http://en.wikipedia.org/wiki/Luhn_algorithm>`_.
1438
1439
1440Customize constraints definition
1441--------------------------------
1442
1443Model validation constraints can be customized by adding specific client validation, overriding
1444constraints synchronized from server or adding custom constraint mapper for a specific BeanValidation server constraint.
1445
1446Adding client constraints
1447+++++++++++++++++++++++++
1448
1449You can **provide additional client constraints** as usual in a standard Backbone Validation way. This client specific 
1450constraints **will then be merged** with synchronized server constraints: 
1451
1452
1453.. code-block:: javascript
1454
1455   var UserModel = Backbone.Model.extend({
1456
1457       className: 'org.resthub.validation.model.User',
1458
1459       initialize: function() {
1460           Resthub.Validation.synchronize(UserModel);
1461       },
1462
1463       validation: {
1464           confirmPassword: {
1465               equalTo: 'password'
1466           }
1467       }
1468   });
1469
1470
1471Overriding constraints
1472++++++++++++++++++++++
1473
1474You can also **override a property constraint already synchronized from server** : only the client constraint will
1475be kept: 
1476
1477
1478.. code-block:: javascript
1479
1480    var UserModel = Backbone.Model.extend({
1481
1482        className: 'org.resthub.validation.model.User',
1483
1484        initialize: function() {
1485            Resthub.Validation.synchronize(UserModel);
1486        },
1487
1488        validation: {
1489            email: {
1490                required: true,
1491                pattern: \my pattern\
1492            }
1493        }
1494    });
1495    
1496
1497.. _validation-add-constraint:
1498
1499Adding custom constraints
1500+++++++++++++++++++++++++
1501
1502If provided a custom JSR303 compliant validation annotation on the server side, you can easily add a custom client validator
1503for your custom constraint with a dedicated RESThub Validation API allowing to **define a new validator or override an 
1504existing one** and retrieve an existing validator: 
1505
1506.. code-block:: javascript
1507
1508    // add or replace the validator associated to the given constraintType.
1509    // validator parameter should be a function
1510    ResthubValidation.addValidator = function(constraintType, validator) {
1511        validators[constraintType] = validator;
1512    };
1513
1514    // retrieve the validator associated to a given constraint type
1515    ResthubValidation.getValidator = function(constraintType) {
1516        return validators[constraintType];
1517    };
1518
1519
1520To map your new constraint, you only have to declare a new validator associated to your constraint type (the annotation
1521name in server side) : 
1522
1523.. code-block:: javascript
1524
1525    Resthub.Validation.addValidator('TelephoneNumber', function(constraint, msg) {
1526        return {
1527            pattern: /^[+]?([0-9]*[\\.\\s\\-\\(\\)]|[0-9]+){6,24}$/,
1528            msg: msg
1529        };
1530    });
1531    
1532
1533.. _validation-messages:
1534
1535Messages and internationalization
1536---------------------------------
1537
1538Internationalization can be managed in different ways : sending locale to server or providing custom messages globally 
1539in resthub.Validation or locally in each of your model.
1540
1541Default behaviour
1542+++++++++++++++++
1543
1544By default, Resthub Validation adds a ``locale`` parameter to any validation related server call. 
1545e.g. ``/api/validation/org.resthub.validation.model.User?locale=en``.
1546
1547Error messages are thus returned from server with the asked locale and displayed client side as it.
1548
1549This is the behaviour that will be applied without any specific configuration. i.e: 
1550
1551.. code-block:: javascript
1552
1553    var UserModel = Backbone.Model.extend({
1554
1555        className: 'org.resthub.validation.model.User',
1556
1557        initialize: function() {
1558            Resthub.Validation.synchronize(UserModel);
1559        }
1560        
1561        ...
1562    });   
1563
1564.. _validation-change-locale:
1565
1566Change locale
1567+++++++++++++
1568
1569Wihtout any further configuration, the current browser locale is taken (copied in Resthub.Validation and sent
1570to server). But you can easily **change locale using Resthub Validation API function** ``locale()`` :
1571
1572.. code-block:: javascript
1573
1574    Resthub.Validation.locale("fr");
1575    
1576This operation will change the current active locale of Resthub Validation and, even more important, will **force
1577the synchronization process to send a new request** to server for next model initialization in order to **refresh
1578constraints** with server localized messages.
1579
1580**You have to explicitely call this function with your new locale on app local update**. If you don't, no request will
1581be sent to server for already synchronized models (because of caching - see :ref:`validation-lifecycle`).
1582
1583Client error messages customization
1584+++++++++++++++++++++++++++++++++++
1585
1586If you want to **manage all or parts of your error messages in client side** - allowing, for instance to build your messages
1587uppon a common i18n mechanism such as requirejs i18n plugin - you'll have to provide specific configuration
1588either globally in ``Resthub.Validation`` namespace or locally in each of your model.
1589
1590This means that you'll provide a dedicated ``messages`` key-value pairs object:
1591
1592    - **key**: contains the constraint message key built as follows: ``'validation.{ConstraintName}.message'``
1593      where ``ConstraintName`` is the name of the contraint, **in camel case and starting by an upper case letter**.
1594    - **value**: contains the constraint message text that could be parametrized, depending on available 
1595      parameters of each constraint (see below and :ref:`validation-supported-constraints`).
1596
1597e.g. :
1598
1599.. code-block:: javascript
1600
1601    messages: {
1602        'validation.Min.message': 'should be greater than {value} or equals',
1603        'validation.NotNull.message': 'should not be null'
1604    },
1605    
1606    
1607If a messages object is provided, globally or locally (see below), RESThub Validation will check if the current
1608constraint exists in messages and affect this message value to the corresponding built Backbone Validation
1609constraint. If the key does not exist, the default message returned by server is returned.
1610      
1611Error messages templating
1612#########################
1613
1614Client error message value definition can be **defined with custom messages templates** to dynamically include
1615constraints parameters values in the resulting message.
1616
1617You can thus display, in your message, any available parameter of the current constraint 
1618(see :ref:`validation-supported-constraints`) by using the curly brackets ``{...}`` syntax :
1619
1620.. code-block:: javascript
1621
1622    messages: {
1623        'validation.Size.message': 'should be greater than {min} or equals and lower than {max} or equals'
1624    },
1625
1626
1627Any parameter value that is not an available parameter for this constraint will be ignored.
1628    
1629Customize globally (Resthub.Validation)
1630#######################################
1631
1632Custom client messages can be provided directly in ``Resthub.Validation`` messages : 
1633
1634.. code-block:: javascript
1635
1636    Resthub.Validation.messages = {
1637        'validation.TelephoneNumber.message': 'telephone number is not valid'
1638    };
1639    
1640This allows you to define error messages that will be **global to your entire app and reused on all of your models**.
1641These messages will **override server error messages**.
1642
1643Customize locally (Model)
1644#########################
1645
1646You can also provide a **model specific messages object** if have specific needs for a given model: 
1647
1648.. code-block:: javascript
1649
1650    var UserModel = Backbone.Model.extend({
1651
1652        className: 'org.resthub.validation.model.User',
1653        messages: {
1654            'validation.Min.message': 'should be greater than {value} or equals'
1655        },
1656        
1657        initialize: function() {
1658            Resthub.Validation.synchronize(UserModel);
1659        }
1660
1661        ...
1662        
1663    });
1664
1665
1666These messages will **override server error messages and** ``Resthub.Validation`` **global messages**.
1667
1668
1669.. _validation-errors:
1670
1671Errors management
1672-----------------
1673
1674By default, any synchronization process error (e.g. server anavailable, className not found, etc.) will
1675**simply log an error message in console**.
1676
1677Obviously, no validation constraint will be retrieved from server and any client side defined cosntraint will be kept
1678as it.
1679
1680**You can provide either global or local customization of this behaviour** (for instance sending a global event
1681to display a user friendly alert, ...).
1682
1683Customize globally (Resthub.Validation)
1684+++++++++++++++++++++++++++++++++++++++
1685
1686You can override the error callback directly in ``Resthub.Validation`` namespace (for instance in your app.js file) :
1687
1688.. code-block:: javascript
1689
1690    Resthub.Validation.options.errorCallback = function(resp) {
1691        // your specific code
1692    };
1693    
1694The ``resp`` parameter is the server response.
1695
1696Customize locally (Model)
1697+++++++++++++++++++++++++
1698
1699Custom error callback could be also **provided in model on synchronize call** as an optional parameter : 
1700
1701.. code-block:: javascript
1702
1703    var UserModel = Backbone.Model.extend({
1704
1705        className: 'org.resthub.validation.model.User',
1706        
1707        initialize: function() {
1708            Resthub.Validation.synchronize(UserModel, function(resp) {// your specific code});
1709        }
1710
1711        ...
1712        
1713    });
1714
1715Customize locally (Model instance)
1716++++++++++++++++++++++++++++++++++
1717
1718You can even provide a model **instance specific callback** by customizing your model initialize method with
1719a custom ``errorCallback`` parameter option member (for instance, in your view in order to display the error in a 
1720view specific zone) :
1721
1722- **model**: 
1723
1724.. code-block:: javascript
1725
1726    var UserModel = Backbone.Model.extend({
1727
1728        ...
1729
1730        initialize: function (attributes, options) {
1731            Resthub.Validation.synchronize(UserModel, options.errorCallback);
1732        },
1733
1734        ...
1735
1736    });
1737
1738
1739- **view**: 
1740
1741.. code-block:: javascript
1742
1743    var UserView = Resthub.View.extend({
1744
1745        ...
1746
1747        initialize: function() {
1748          // Initialize the collection
1749          this.model = new User({}, {errorCallback: function(resp) {// your specific code}});
1750
1751          Backbone.Validation.bind(this);
1752
1753          this.render();
1754        },
1755        
1756        ...
1757    });
1758
1759
1760Other librairies included in the stack
1761======================================
1762
1763.. _backbone-validation:
1764
1765Backbone Validation
1766-------------------
1767
1768Backbone_ does not provide natively **any tool for form or validation management**. It is not necessary
1769to specify model attributes or related constraints.
1770
1771In terms of validation, Backbone_ provides only empty methods ``validate`` and ``isValid`` that have to be implemented by each developer. 
1772The only guarantee that the ``validate`` method is called before a ``save`` (canceled on error). But a complete form validation is 
1773not obvious (custom error array management ... ) and the errors are not distinguishable from inherent ``save`` errors (server communication and so on).
1774
1775`Backbone Validation`_ **only focus on validation aspects** and leaves us free to write our form. The lib has **a very large number of built-in 
1776validators** and **provides effective validators customization and extension mechanisms**.
1777
1778`Backbone Validation`_ does not neither propose automatic linking between form and model and leaves us the choice to use a dedicated lib or 
1779to implement custom behaviour (before the validation, process all form values to set to model). The behaviour of `Backbone Validation`_ perfectly matches standard
1780Backbone_ workflow through ``validate`` and ``isValid`` methods.
1781
1782**Model**: constraints definition:
1783
1784.. code-block:: javascript
1785
1786    define(['underscore', 'backbone'], function (_, Backbone) {
1787
1788        /**
1789         * Definition of a Participant model object
1790         */
1791        var ParticipantModel = Backbone.Model.extend({
1792            urlRoot:App.Config.serverRootURL + "/participant",
1793            defaults:{
1794            },
1795
1796            // Defines validation options (see Backbone-Validation)
1797            validation:{
1798                firstname:{
1799                    required:true
1800                },
1801                lastname:{
1802                    required:true
1803                },
1804                email:{
1805                    required:false,
1806                    pattern:'email'
1807                }
1808            },
1809
1810            initialize:function () {
1811            }
1812
1813        });
1814        return ParticipantModel;
1815
1816    });
1817
1818**HTML5 Form**:
1819
1820.. code-block:: html
1821
1822    {{#with participant}}
1823        <form class="form-horizontal">
1824            <fieldset>
1825                <div class="row">
1826                    <div class="span8">
1827                        <div class="control-group">
1828                            {{#if id}}
1829                                <label for="participantId" class="control-label">Id:</label>
1830                                <div class="controls">
1831                                    <input id="participantId" name="id" type="text" value="{{id}}" disabled/>
1832                                </div>
1833                            {{/if}}
1834                        </div>
1835
1836                        <div class="control-group">
1837                            <label for="firstname" class="control-label">First name:</label>
1838                            <div class="controls">
1839                                <input type="text" id="firstname" name="firstname" required="true" value="{{firstname}}" tabindex="1" autofocus="autofocus"/>
1840                                <span class="help-inline"></span>
1841                            </div>
1842                        </div>
1843
1844                        <div class="control-group">
1845                            <label for="lastname" class="control-label">Last name:</label>
1846                            <div class="controls">
1847                                <input type="text" id="lastname" name="lastname" required="true" value="{{lastname}}" tabindex="2"/>
1848                                <span class="help-inline"></span>
1849                            </div>
1850                        </div>
1851
1852                        <div class="control-group">
1853                            <label for="email" class="control-label">email address:</label>
1854                            <div class="controls">
1855                                <input type="email" id="email" name="email" value="{{email}}" tabindex="3"/>
1856                                <span class="help-inline"></span>
1857                            </div>
1858                        </div>
1859                    </div>
1860            </fieldset>
1861        </form>
1862    {{/with}}
1863
1864
1865**View**: initialization and usage:
1866
1867.. code-block:: javascript
1868
1869    initialize:function () {
1870
1871        ...
1872
1873        // allow backbone-validation view callbacks (for error display)
1874        Backbone.Validation.bind(this);
1875
1876        ...
1877    },
1878
1879    ...
1880
1881    /**
1882     * Save the current participant (update or create depending of the existence of a valid model.id)
1883     */
1884    saveParticipant:function () {
1885
1886        // build array of form attributes to refresh model
1887        var attributes = {};
1888        this.$el.find("form input[type!='submit']").each(function (index, value) {
1889            attributes[value.name] = value.value;
1890            this.model.set(value.name, value.value);
1891        }.bind(this));
1892
1893        // save model if it's valid, display alert otherwise
1894        if (this.model.isValid()) {
1895            this.model.save(null, {
1896                success:this.onSaveSuccess.bind(this),
1897                error:this.onSaveError.bind(this)
1898            });
1899        }
1900        else {
1901            ...
1902        }
1903    }
1904
1905You also natively beneficate of custom validation callbacks allowing to render validation errors in a 
1906form structured with `Twitter Bootstrap`_.
1907
1908Since the 2.1.0 version, Resthub provides **server to client validation bindings features** in order to define constraints
1909only once. See :ref:`resthub-validation` for details.
1910
1911Backbone Query Parameters
1912-------------------------
1913
1914Backbone_ routes management allows to define permet such routes:
1915``"participants":"listParticipants"`` and ``"participants?:param":"listParticipantsParameters"``. But the native behaviour seems not sufficient:
1916
1917* **management of an unknown number of parameters** (ex ``?page=2&filter=filter``) is not obvious
1918* we have to define (at least) **two routes to handle calls with or without parameters** without duplication
1919and without too much technical code
1920
1921Expected behaviour was that the **map a single route to a method with an array of request parameter as optional parameter.**
1922
1923`Backbone Query Parameters`_ provides this functionality.
1924
1925With this lib, included once and for all in the main router, You 'll get the following:
1926
1927**router.js**:
1928
1929.. code-block:: javascript
1930
1931    define(['backbone', 'backbone-queryparams'], function (Backbone) {
1932        var AppRouter = Backbone.Router.extend({
1933            routes:{
1934                // Define some URL routes
1935                ...
1936
1937                "participants":"listParticipants",
1938
1939                ...
1940            },
1941
1942            ...
1943
1944            listParticipants:function (params) {
1945                // params contains the list of all query params of is empty if no param
1946            }
1947        });
1948    });
1949
1950Query parameters array is automatically recovered **without any further operation** and **whatever the number
1951of these parameters**. It can then be passed to the view constructor for initialization:
1952
1953**list.js**:
1954
1955.. code-block:: javascript
1956
1957    askedPage:1,
1958
1959    initialize:function (params) {
1960
1961        ...
1962
1963        if (params) {
1964            if (params.page && this.isValidPageNumber(params.page)) this.askedPage = parseInt(params.page);
1965        }
1966
1967        ...
1968    },
1969
1970Backbone Datagrid
1971-----------------
1972
1973`Backbone Datagrid`_ is a powerful component, based on Backbone.View, that
1974displays your Bakbone collections in a dynamic datagrid table. It is highly
1975customizable and configurable with sensible defaults.
1976
1977You will find the full documentation on its `dedicated website
1978<http://loicfrering.github.com/backbone.datagrid/>`_. Do not miss the examples
1979listed on `this page <http://loicfrering.github.com/backbone.datagrid/examples/>`_. Their sources are
1980available in the `examples <https://github.com/loicfrering/backbone.datagrid/tree/master/examples/>`_
1981directory of the repository.
1982
1983* Solar: a simple and complete example with an in memory collection of planets from the Solar System.
1984
1985  * `Live version <http://loicfrering.github.com/backbone.datagrid/examples/solar.html>`_
1986  * `Sources <https://github.com/loicfrering/backbone.datagrid/tree/master/examples/js/solar.js>`_
1987
1988* GitHub: an example with a collection connected to GitHub's REST API.
1989
1990  * `Live version <http://loicfrering.github.com/backbone.datagrid/examples/github.html>`_
1991  * `Sources <https://github.com/loicfrering/backbone.datagrid/tree/master/examples/js/github.js>`_
1992
1993Note that the Backbone Datagrid handles pagination by itself and does not rely
1994on Backbone Paginator which is described below and should only be used to
1995paginate collections which are not displayed in a datagrid.
1996
1997Backbone Paginator
1998------------------
1999
2000`Backbone Paginator`_ offers both client side pagination (``Paginator.clientPager``) and integration with server side pagination
2001(``Paginator.requestPager``). It includes management of filters, sorting, etc.
2002
2003Client side pagination
2004++++++++++++++++++++++
2005
2006This lib extends Backbone_ collections. So adding options to collections is necessary:
2007
2008.. code-block:: javascript
2009
2010    var participantsCollection = Backbone.Paginator.clientPager.extend({
2011        model:participantModel,
2012        paginator_core:{
2013            // the type of the request (GET by default)
2014            type:'GET',
2015
2016            // the type of reply (jsonp by default)
2017            dataType:'json',
2018
2019            // the URL (or base URL) for the service
2020            url:App.Config.serverRootURL + '/participants'
2021        },
2022        paginator_ui:{
2023            // the lowest page index your API allows to be accessed
2024            firstPage:1,
2025
2026            // which page should the paginator start from
2027            // (also, the actual page the paginator is on)
2028            currentPage:1,
2029
2030            // how many items per page should be shown
2031            perPage:12,
2032
2033            // a default number of total pages to query in case the API or
2034            // service you are using does not support providing the total
2035            // number of pages for us.
2036            // 10 as a default in case your service doesn't return the total
2037            totalPages:10
2038        },
2039        parse:function (response) {
2040            return response;
2041        }
2042    });
2043
2044Then we ``fetch`` the collection and then ask for the right page:
2045
2046.. code-block:: javascript
2047
2048    this.collection = new ParticipantsCollection();
2049
2050    // get the participants collection from server
2051    this.collection.fetch({
2052        success:function () {
2053            this.collection.goTo(this.askedPage);
2054        }.bind(this),
2055        error:function (collection, response) {
2056            ...
2057        }
2058    });
2059
2060Once the collection retrieved, ``collection.info()`` allows to get information about current state:
2061
2062.. code-block:: javascript
2063
2064    totalUnfilteredRecords
2065    totalRecords
2066    currentPage
2067    perPage
2068    totalPages
2069    lastPage
2070    previous
2071    next
2072    startRecord
2073    endRecord
2074
2075Server side pagination
2076++++++++++++++++++++++
2077
2078Once client side pagination implemented, server adaptation is very easy:
2079
2080We set **parameters to send to server** in ``collections/participants.js``:
2081
2082.. code-block:: javascript
2083
2084    server_api:{
2085        'page':function () {
2086            return this.currentPage;
2087        },
2088
2089        'size':function () {
2090            return this.perPage;
2091        }
2092    },
2093
2094Then, in the same file, we provide a parser to get the response back and initialize collection and pager:
2095
2096.. code-block:: javascript
2097
2098    parse:function (response) {
2099        var participants = response.content;
2100        this.totalPages = response.totalPages;
2101        this.totalRecords = response.totalElements;
2102        this.lastPage = this.totalPages;
2103        return participants;
2104    }
2105
2106Finally, we change server call: this time the ``goTo`` method extend ``fetch`` and should be called instead
2107(``views/participants/list.js``):
2108
2109.. code-block:: javascript
2110
2111    // get the participants collection from server
2112    this.collection.goTo(this.askedPage,
2113       {
2114            success:function () {
2115            ...
2116            }.bind(this),
2117            error:function () {
2118                ...
2119            }
2120        });
2121
2122All other code stay inchanged but the ``collection.info()`` is a little bit thinner:
2123
2124.. code-block:: javascript
2125
2126    totalRecords
2127    currentPage
2128    perPage
2129    totalPages
2130    lastPage
2131
2132Async
2133-----
2134
2135Other recurrent problem: parallel asynchronous calls for which we want to have a
2136final processing in order to display the results of the entire process: number of errors, successes,
2137etc.
2138
2139Basically, each asynchronous call define a callback invoked at the end of his own treatment (success or error).
2140Without tools, we are thus obliged to implement a **manual count of called functions and a count
2141of callbacks called to compare**. The final callback is then called at the end of each call unit
2142but executed only if there is no more callback to call. This gives:
2143
2144.. code-block:: javascript
2145
2146    /**
2147     * Effective deletion of all element ids stored in the collection
2148     */
2149    deleteElements:function () {
2150
2151        var self = this;
2152        var nbWaitingCallbacks = 0;
2153
2154        $.each(this.collection, function (type, idArray) {
2155            $.each(idArray, function (index, currentId) {
2156                nbWaitingCallbacks += 1;
2157
2158                $.ajax({
2159                    url:App.Config.serverRootURL + '/participant/' + currentId,
2160                    type:'DELETE'
2161                })
2162                .done(function () {
2163                    nbWaitingCallbacks -= 1;
2164                    self.afterRemove(nbWaitingCallbacks);
2165                })
2166                .fail(function (jqXHR) {
2167                    if (jqXHR.status != 404) {
2168                        self.recordError(type, currentId);
2169                    }
2170                    nbWaitingCallbacks -= 1;
2171                    self.afterRemove(nbWaitingCallbacks);
2172                });
2173            });
2174        });
2175    },
2176
2177    /**
2178     * Callback called after an ajax deletion request
2179     *
2180     * @param nbWaitingCallbacks number of callbacks that we have still to wait before close request
2181     */
2182    afterRemove:function (nbWaitingCallbacks) {
2183
2184        // if there is still callbacks waiting, do nothing. Otherwise it means that all request have
2185        // been performed: we can manage global behaviours
2186        if (nbWaitingCallbacks == 0) {
2187            // do something
2188        }
2189    },
2190
2191This code works but there is **too much technical code**!
2192
2193Async_ provides a set of helpers to perform **asynchronous parallel processing** and synchronize the end of 
2194these treatments through a final callback called once.
2195
2196This lib is initially developed for nodeJS server but has been **implemented on browser side**.
2197
2198Theoretically, the method we currently need is ``forEach``. However, we faced the following problem: all of these helpers
2199are designed to stop everything (and call the final callback) when the first error occurs.
2200But if we need to perform all server calls and only then, whether successful or fail, return global results
2201to the user, there is unfortunately no appropriate option (despite similar requests on mailing lists) ...
2202
2203You can twick a little and, instead of ``forEach``, use the ``map`` function that returns a result array
2204in which you can register successes and errors. error parameter of the final callback cannot be used without
2205stopping everything. So, the callback should always be called with a ``null`` err parameter and a custom wrapper containing the
2206returned object and the type of the result: ``success`` or ``error``. You can then globally count errors without
2207interrupting your calls:
2208
2209.. code-block:: javascript
2210
2211    /**
2212     * Effective deletion of all element ids stored in the collection
2213     */
2214    deleteElements:function () {
2215
2216        ...
2217
2218        async.map(elements, this.deleteFromServer.bind(this), this.afterRemove.bind(this));
2219    },
2220
2221    deleteFromServer:function (elem, deleteCallback) {
2222        $.ajax({
2223            url:App.Config.serverRootURL +'/' + elem.type + '/' + elem.id,
2224            type:'DELETE'
2225        })
2226        .done(function () {
2227            deleteCallback(null, {type:"success", elem:elem});
2228        })
2229        .fail(function (jqXHR) {
2230           ...
2231
2232            // callback is called with null error parameter because otherwise it breaks the
2233            // loop and top on first error :-(
2234            deleteCallback(null, {type:"error", elem:elem});
2235        }.bind(this));
2236    },
2237
2238    /**
2239     * Callback called after all ajax deletion requests
2240     *
2241     * @param err always null because default behaviour break map on first error
2242     * @param results array of fetched models: contain null value in cas of error
2243     */
2244    afterRemove:function (err, results) {
2245
2246        // no more test
2247        ...
2248    },
2249
2250Keymaster
2251---------
2252
2253Keymaster_ is a micro library allowing to define listeners on keyboard shortcuts and propagate them. 
2254The syntax is elegant, it is very simple while very complete:
2255
2256* Management of multiple hotkeys
2257* Chaining through an important number of "modifiers"
2258* Source DOM element type filtering
2259* ...
2260
2261It is so simple that the doc is self sufficient - see `here <http://github.com/madrobby/keymaster>`_
2262
2263Backbone Relational
2264-------------------
2265
2266`Backbone Relational`_ provides one-to-one, one-to-many and many-to-one relations between models for Backbone. To use relations, extend Backbone.RelationalModel (instead of the regular Backbone.Model) and define a property relations, containing an array of option objects. Each relation must define (as a minimum) the type, key and relatedModel. Available relation types are Backbone.HasOne and Backbone.HasMany.
2267
2268Backbone-relational features:
2269    * Bidirectional relations, which notify related models of changes through events.
2270    * Control how relations are serialized using the includeInJSON option.
2271    * Automatically convert nested objects in a model's attributes into Model instances using the createModels option.
2272    * Lazily retrieve (a set of) related models through the fetchRelated(key<string>, [options<object>], update<bool>) method.
2273    * Determine the type of HasMany collections with collectionType.
2274    * Bind new events to a Backbone.RelationalModel for:
2275    * addition to a HasMany relation (bind to add:<key>; arguments: (addedModel, relatedCollection)),
2276    * removal from a HasMany relation (bind to remove:<key>; arguments: (removedModel, relatedCollection)),
2277    * reset of a HasMany relation (bind to reset:<key>; arguments: (relatedCollection)),
2278    * changes to the key itself on HasMany and HasOne relations (bind to update:<key>; arguments=(model, relatedModel/relatedCollection)). 
2279
2280Moment
2281------
2282
2283`Moment`_ is a date library for parsing, validating, manipulating, and formatting dates.
2284
2285Moment.js features:
2286 * Parse and format date with custom pattern and internationalization
2287 * Date manipulation (add, substract)
2288 * Durations (eg: 2 hours)
2289
2290Guidelines
2291==========
2292
2293Collection View
2294---------------
2295
2296If you need to render a simple list of elements, just make a single view with an each loop in the template:
2297
2298.. code-block:: html
2299
2300    <h1>My TodoList</h1>
2301    <ul>
2302        {{#each this}}
2303            <li>{{title}}</li>
2304        {{/each}}
2305    </ul>
2306
2307But if each element of your collection requires a separate view (typically when you listen on some events on it or if it contains a form), in order to comply with separation of concerns and encapsulation principles, you should create separate views for the collection and the model. The model view should be able to render itself.
2308
2309You can see more details on the `Todo example <https://github.com/resthub/todo-backbone-example>`_ (have a look to TodosView and TodoView).
2310
2311Always use listenTo instead of on
2312---------------------------------
2313
2314In order to allow automatic cleanup when the View is removed, you should always use listenTo function instead of on
2315
2316.. code-block:: javascript
2317
2318    // BAD: no context specified - event bindings won't be cleaned when the view is removed
2319    Todos.on('sync', this.render);
2320
2321    // GOOD: context will allow automatic cleanup when the view is removed
2322    this.listenTo(Todos, 'sync', this.render);
2323
2324Static versus instance variables
2325--------------------------------
2326
2327If you want to create different View instances, you have to manage properly the DOM element where the view will be attached as described previously. You also have to use instance variables.
2328
2329Backbone way of declaring a static color variable:
2330
2331.. code-block:: javascript
2332
2333    var MyView = Resthub.View.extend({
2334
2335        color: '#FF0000',
2336
2337        initialize: function(options) {
2338            this.$root = options.root;
2339            this.$root.html(this.$el);
2340        }
2341    });
2342
2343    return MyView;
2344
2345Backbone way of declaring an instance color variable:
2346
2347.. code-block:: javascript
2348
2349    var MyView = Resthub.View.extend({
2350
2351        initialize: function(options) {
2352            this.$root = options.root;
2353            this.$root.html(this.$el);
2354
2355            this.color = '#FF0000';
2356        }
2357    });
2358
2359    return MyView;
2360
2361Use this.$() selector
2362---------------------
2363
2364this.$() is a shortcut for this.$el.find(). You should use it for all your view DOM selector code in order to find elements within your view (i.e. not in the whole page). It follows the encapsulation pattern, and will make it possible to have several instances of your view on the same page. Even with a singleton view, it is a good practice to use this pattern.
2365
2366Events
2367------
2368
2369Backbone default event list is available `here <http://backbonejs.org/#Events-catalog>`_.
2370
2371Inheritance
2372-----------
2373
2374As described by `k33g <https://twitter.com/#!/k33g_org>`_ on his `Gist Use Object Model of BackBone <https://gist.github.com/2287018>`_, it is possible to reuse Backbone.js extend() function in order to get simple inheritance in Javascript.
2375
2376.. code-block:: javascript
2377
2378    // Define an example Kind class
2379    var Kind = function() {
2380        this.initialize && this.initialize.apply(this, arguments);
2381    };
2382    Kind.extend = Backbone.Model.extend;
2383
2384    // Create a Human class by extending Kind
2385    var Human = Kind.extend({
2386        toString: function() { console.log("hello: ", this); },
2387        initialize: function (name) {
2388            console.log("human constructor");
2389            this.name = name
2390        }
2391    });
2392
2393    // Call parent constructor
2394    var SomeOne = Human.extend({
2395        initialize: function(name){
2396            SomeOne.__super__.initialize.call(this, name);
2397        }
2398    });
2399
2400    // Create an instance of Human class
2401    var Bob = new Human("Bob");
2402    Bob.toString();
2403
2404    // Create an instance of SomeOne class
2405    var Sam = new SomeOne("Sam");
2406    Sam.toString();
2407
2408    // Static members
2409    var Human = Kind.extend({
2410        toString: function() { console.log("hello: ", this); },
2411        initialize: function (name) {
2412            console.log("human constructor");
2413            this.name = name
2414        }
2415    },{ //Static
2416        counter: 0,
2417        getCounter: function() { return this.counter; }
2418    });
2419
2420Cache buster
2421------------
2422
2423In order to avoid caching issues when updating your JS or HTML files, you should use the `urlArgs RequireJS attribute <http://requirejs.org/docs/api.html#config>`_. You can filter the ${buildNumber} with your build tool at each build.
2424
2425main.js:
2426
2427.. code-block:: javascript
2428
2429    require.config({
2430        paths: {
2431            // ...
2432        },
2433        urlArgs: 'appversion=${buildNumber}''
2434    });
2435
2436main.js after filtering:
2437
2438.. code-block:: javascript
2439
2440    require.config({
2441        paths: {
2442            // ...
2443        },
2444        urlArgs: 'appversion=738792920293847'
2445    });
2446
2447In order to avoid bugs (like no change displayed after an update) due to Internet Explorer agressive caching strategy, Ajax request cache is disable at jQuery level when using IE.
2448
2449.. _Require: http://requirejs.org
2450.. _Handlebars: http://handlebarsjs.com
2451.. _Backbone Validation: http://github.com/thedersen/backbone.validation
2452.. _Twitter Bootstrap: http://twitter.github.com/bootstrap/
2453.. _Backbone Datagrid: http://loicfrering.github.com/backbone.datagrid/
2454.. _Backbone Paginator: http://addyosmani.github.com/backbone.paginator/
2455.. _Backbone Query Parameters: http://github.com/jhudson8/backbone-query-parameters
2456.. _Async: http://github.com/caolan/async/
2457.. _Keymaster: http://github.com/madrobby/keymaster
2458.. _Backbone: http://backbonejs.org/
2459.. _Backbone Relational: https://github.com/PaulUithol/Backbone-relational
2460.. _Moment: http://momentjs.com/