PageRenderTime 9ms CodeModel.GetById 4ms app.highlight 52ms 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

Large files files are truncated, but you can click here to view the full 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.. co…

Large files files are truncated, but you can click here to view the full file