PageRenderTime 69ms CodeModel.GetById 25ms RepoModel.GetById 0ms 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. Backbone.js Stack
  4. =================
  5. RESThub 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 ...
  6. In 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.
  7. .. contents::
  8. :depth: 3
  9. The Backbone.js 2.1.1 stack includes the following librairies:
  10. * jQuery 1.9.1 (`documentation <http://docs.jquery.com/Main_Page>`_)
  11. * Backbone.js 1.0 (`documentation <http://documentcloud.github.com/backbone/>`_) and its `localstorage adapter
  12. <http://documentcloud.github.com/backbone/docs/backbone-localstorage.html>`_
  13. * Underscore.js 1.4.4 (`documentation <http://documentcloud.github.com/underscore/>`_)
  14. * Underscore.String 2.3.0 (`documentation <https://github.com/epeli/underscore.string#readme>`_)
  15. * Require.js 2.1.5 with `i18n <http://requirejs.org/docs/api.html#i18n>`_ and `text <http://requirejs.org/docs/api.html#text>`_ plugins
  16. (`documentation <http://requirejs.org/docs/api.html>`_)
  17. * Handlebars 1.0-rc3 (`documentation <http://handlebarsjs.com>`_)
  18. * A console shim + client logging to server mechanism
  19. * Twitter Bootstrap 2.3 (`documentation <http://twitter.github.com/bootstrap/>`_) and its JS plugins
  20. * Form Validation: `Backbone Validation`_
  21. * Parameters support on view routing: `Backbone Query Parameters`_
  22. * Datagrid: `Backbone Datagrid`_
  23. * Paginated lists: `Backbone Paginator`_
  24. * Asynchronous calls: Async_
  25. * Dispatching keyboard shortcuts: Keymaster_
  26. * Get and set relations (one-to-one, one-to-many, many-to-one) for Backbone models: `Backbone Relational`_
  27. * Parsing, validating, manipulating, and formatting dates: `Moment`_
  28. Before 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.
  29. Changelog
  30. =========
  31. * 2013-03-26: `RESThub Backbone.js stack 2.1.0 has been released <https://github.com/resthub/resthub-backbone-stack/blob/master/CHANGELOG.rst>`_
  32. * 2012-12-04: `RESThub Backbone.js stack 2.0.0 has been released <http://pullrequest.org/2012/12/04/resthub-2.html>`_!
  33. * 2012-11-13: RESThub Backbone.js stack 2.0-rc4 has been released
  34. * 2012-10-24: RESThub Backbone.js stack 2.0-rc3 has been released
  35. * 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
  36. * 2012-10-01: `RESThub 2.0-rc1 <https://github.com/resthub/resthub-backbone-stack/issues?milestone=3&state=closed>`_ has been released
  37. * 2012-08-29: `RESThub 2.0-beta2 <https://github.com/resthub/resthub-backbone-stack/issues?milestone=1&state=closed>`_ has been released
  38. Bootstrap your project
  39. ======================
  40. There are 2 ways to use it in your project:
  41. * 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>`_
  42. * 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
  43. The `Todo RESThub example <https://github.com/resthub/todo-backbone-example>`_ project is the reference example project using this stack.
  44. Tutorial
  45. ========
  46. You should follow `RESThub Backbone Stack tutorial <tutorial/backbone.html>`_ in order to learn step by step how to use it.
  47. Project layout
  48. ==============
  49. Directories and filename conventions
  50. ------------------------------------
  51. Here is the typical RESThub Backbone.js stack based application directories and filename layout:
  52. .. code-block:: text
  53. /
  54. img
  55. css
  56. style.css
  57. bootstrap.css
  58. bootstrap-responsive.css
  59. template
  60. project
  61. projects.hbs
  62. project-edit.hbs
  63. user
  64. users.hbs
  65. user-edit.hbs
  66. js
  67. lib
  68. async.js
  69. backbone.js
  70. ...
  71. resthub
  72. backbone-resthub.js
  73. backbone-validation-ext.js
  74. ...
  75. model
  76. user.js var User = Backbone.Model.extend(...); return User;
  77. project.js var Project = Backbone.Model.extend(...); return Project;
  78. collection
  79. users.js var Users = Backbone.Collection.extend(...); return Users;
  80. projects.js var Projects = Backbone.Collection.extend(...); return Projects;
  81. view
  82. project
  83. projects-view.js var ProjectsView = Resthub.View.extend(...); return ProjectsView;
  84. project-edit-view.js var ProjectEditView = Resthub.View.extend(...); return ProjectEditView;
  85. user
  86. users-view.js var UsersView = Resthub.View.extend(...); return UsersView;
  87. user-edit-view.js var UserEditView = Resthub.View.extend(...); return UserEditView;
  88. router
  89. app-router.js var AppRouter = Backbone.Router.extend(...); return AppRouter;
  90. app.js
  91. main.js
  92. index.html
  93. index.html
  94. ----------
  95. index.html is provided by RESThub Backbone stack, so you don't have to create it.
  96. .. code-block:: html
  97. <!DOCTYPE html>
  98. <html lang="en">
  99. <head>
  100. <meta charset="utf-8">
  101. <title>RESThub Backbone.js Bootstrap</title>
  102. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  103. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  104. <meta name="description" content="">
  105. <meta name="author" content="">
  106. <link href="css/bootstrap.css" rel="stylesheet">
  107. <!--[if lt IE 9]>
  108. <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
  109. <![endif]-->
  110. </head>
  111. <body>
  112. <div id="main"> </div>
  113. <!-- Placed at the end of the document so the pages would load faster -->
  114. <script data-main="js/main" src="js/lib/require.js"></script>
  115. </body>
  116. </html>
  117. main.js
  118. -------
  119. This 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.
  120. Here's the default main.js file:
  121. .. code-block:: javascript
  122. //Set the require.js configuration for your application.
  123. require.config({
  124. shim: {
  125. 'underscore': {
  126. exports: '_'
  127. },
  128. 'underscore-string': {
  129. deps: [
  130. 'underscore'
  131. ]
  132. },
  133. 'handlebars-orig': {
  134. exports: 'Handlebars'
  135. },
  136. 'backbone': {
  137. deps: [
  138. 'underscore',
  139. 'underscore-string',
  140. 'jquery'
  141. ],
  142. exports: 'Backbone'
  143. },
  144. 'backbone-queryparams': {
  145. deps: [
  146. 'backbone'
  147. ]
  148. },
  149. 'backbone-datagrid': {
  150. deps: [
  151. 'backbone'
  152. ],
  153. exports: 'Backbone.Datagrid'
  154. },
  155. 'backbone-paginator': {
  156. deps: [
  157. 'backbone'
  158. ],
  159. exports: 'Backbone.Paginator'
  160. },
  161. 'bootstrap': {
  162. deps: [
  163. 'jquery'
  164. ]
  165. },
  166. 'backbone-relational': {
  167. deps: [
  168. 'backbone'
  169. ]
  170. },
  171. 'keymaster': {
  172. exports: 'key'
  173. },
  174. 'async': {
  175. exports: 'async'
  176. }
  177. },
  178. // Libraries
  179. paths: {
  180. jquery: 'lib/jquery',
  181. underscore: 'lib/underscore',
  182. 'underscore-string': 'lib/underscore-string',
  183. backbone: 'lib/backbone',
  184. resthub: 'lib/resthub/resthub',
  185. localstorage: 'lib/localstorage',
  186. text: 'lib/text',
  187. i18n: 'lib/i18n',
  188. pubsub: 'lib/resthub/pubsub',
  189. 'bootstrap': 'lib/bootstrap',
  190. 'backbone-validation-orig': 'lib/backbone-validation',
  191. 'backbone-validation': 'lib/resthub/backbone-validation-ext',
  192. 'handlebars-orig': 'lib/handlebars',
  193. 'handlebars': 'lib/resthub/handlebars-helpers',
  194. 'backbone-queryparams': 'lib/backbone-queryparams',
  195. 'backbone-datagrid': 'lib/backbone-datagrid',
  196. 'backbone-paginator': 'lib/backbone-paginator',
  197. 'backbone-relational': 'lib/backbone-relational',
  198. async: 'lib/async',
  199. keymaster: 'lib/keymaster',
  200. hbs: 'lib/resthub/require-handlebars',
  201. moment: 'lib/moment',
  202. template: '../template',
  203. console: 'lib/resthub/console'
  204. }
  205. });
  206. // Load our app module and pass it to our definition function
  207. require(['console', 'app']);
  208. **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.
  209. **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.
  210. RESThub 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)
  211. app.js
  212. -------
  213. app.js is where your application begins. You should customize it in order to initialize your routers and/or views.
  214. Here's the default app.js file:
  215. .. code-block:: javascript
  216. define(['router/app-router'], function(AppRouter) {
  217. new AppRouter();
  218. // ...
  219. });
  220. Resthub.View
  221. ============
  222. RESThub Backbone stack provides an enhanced Backbone View named Resthub.View with the following functionalities:
  223. * Default render() with root and context attributes
  224. * Automatic view dispose + callbacks unbind when a view is removed from DOM
  225. * View model population from a form
  226. Default render() with root and context attributes
  227. -------------------------------------------------
  228. Backbone 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.
  229. In 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.
  230. .. code-block:: javascript
  231. new MyView({root: this.$('.container'), collection: myCollection});
  232. In 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.
  233. .. code-block:: javascript
  234. new MyView({root: '#container', collection: myCollection});
  235. RESThub provides a default implementation that will render your template with **model**, **collection** and **labels** as template attributes context if these properties are defined.
  236. .. code-block:: javascript
  237. define(['underscore', 'resthub', 'hbs!template/my'], function(_, Resthub, myTemplate){
  238. var MyView = Resthub.View.extend({
  239. template: myTemplate,
  240. initialize: function() {
  241. _.bind(this.render, this);
  242. this.collection.on('reset', this.render, this);
  243. }
  244. });
  245. });
  246. A sample template with automatic collection provisionning:
  247. .. code-block:: html
  248. <ul>
  249. {{#each collection}}
  250. <li>{{this.firstname}} {{this.name}}</li>
  251. {{/each}}
  252. </ul>
  253. Or with automatic model and labels provisionning:
  254. .. code-block:: html
  255. <p>{{labels.user.identity}}: {{model.firstname}} {{model.name}}</li>
  256. After 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:
  257. * replace: replace the content of $root with $el view content
  258. * append: append the content of $el at the end of $root
  259. * prepend: prepend the content of $el at the beginning of $root
  260. .. code-block:: javascript
  261. var MyView = Resthub.View.extend({
  262. template: myTemplate,
  263. tagName: 'li',
  264. strategy: 'append'
  265. });
  266. You can customize the rendering context by defining a context property:
  267. .. code-block:: javascript
  268. var MyView = Resthub.View.extend({
  269. template: myTemplate,
  270. context: {
  271. numberOfElemnts: 42,
  272. collection: this.collection
  273. }
  274. });
  275. Or by passing a function if you need dynamic context:
  276. .. code-block:: javascript
  277. var MyView = Resthub.View.extend({
  278. template: myTemplate,
  279. labels: myLabels,
  280. context: function() {
  281. var done = this.collection.done().length;
  282. var remaining = this.collection.remaining().length;
  283. return {
  284. total: this.collection.length,
  285. done: done,
  286. remaining: remaining,
  287. labels: this.labels
  288. };
  289. }
  290. });
  291. Or by passing the context as a render parameter when you call it explicitely:
  292. .. code-block:: javascript
  293. this.render({messages: messages, collection: this.collection});
  294. If 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:
  295. .. code-block:: javascript
  296. var MyView = Resthub.View.extend({
  297. render: function() {
  298. // Call super render function with the same arguments
  299. MyView.__super__.render.apply(this, arguments);
  300. // Add child views
  301. this.collection.each(function(child) {
  302. this.add(child);
  303. }, this);
  304. },
  305. add: function(todo) {
  306. var childView = new ChildView({
  307. model: child,
  308. root: this.$('.childcontainer')
  309. });
  310. }
  311. });
  312. .. _backbone-dispose:
  313. Automatic view dispose + callbacks unbind
  314. -----------------------------------------
  315. RESThub 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.
  316. i.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**.
  317. .. warning::
  318. 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.
  319. View model population from a form
  320. ---------------------------------
  321. `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.
  322. RESThub 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()``):
  323. .. code-block:: javascript
  324. Resthub.View.extend({
  325. ...
  326. saveUser:function () {
  327. this.populateModel();
  328. // save model if it's valid, display alert otherwise
  329. if (this.model.isValid()) {
  330. this.model.save(null, {
  331. success:this.onSaveSuccess.bind(this),
  332. error:this.onSaveError.bind(this)
  333. });
  334. }
  335. }
  336. });
  337. ``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:
  338. .. code-block:: javascript
  339. /** utility method providing a default and basic handler that
  340. * populates model from a form input
  341. *
  342. * @param form form element to 'parse'. Form parameter could be a css selector or a
  343. * jQuery element. If undefined, the first form of this view el is used.
  344. * @param model model instance to populate. If no model instance is provided,
  345. * search for 'this.model'
  346. */
  347. populateModel:function (form, model);
  348. So you can use it in multiple ways from your view:
  349. .. code-block:: javascript
  350. // take the first el form element and copy values into 'this.model' instance
  351. this.populateModel();
  352. // get the form element matching the provided selector (form with id "myForm") and copy values into 'this.model' instance
  353. this.populateModel("#myForm");
  354. // get the provided jquery form element and copy values into 'this.model' instance
  355. this.populateModel(this.$("#myForm");
  356. // take the first el form element and copy values into provided myModel instance
  357. this.populateModel(null, myModel);
  358. // get the form element matching the provided selector (form with id "myForm") and copy values into provided myModel instance
  359. this.populateModel("#myForm", myModel);
  360. // get the provided jquery form element and copy values into provided myModel instance
  361. this.populateModel(this.$("#myForm"), myModel);
  362. As 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).
  363. .. _templating:
  364. Templating
  365. ==========
  366. Handlebars
  367. ----------
  368. Client-side templating capabilities are based by default on Handlebars_.
  369. Templates are HTML fragments, without the <html>, <header> or <body> tag:
  370. .. code-block:: html
  371. <div class="todo {{#if done}}done{{/if}}">
  372. <div class="display">
  373. <input class="check" type="checkbox" {{#if done}}checked="checked"{{/if}}/>
  374. <div class="todo-content">{{content}}</div>
  375. <span class="todo-destroy"></span>
  376. </div>
  377. <div class="edit">
  378. <input class="todo-input" type="text" value="{{content}}" />
  379. </div>
  380. </div>
  381. RequireJS Handlebars plugin
  382. ---------------------------
  383. Templates 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:
  384. .. code-block:: javascript
  385. require.config({
  386. paths: {
  387. // ...
  388. text: 'lib/text',
  389. hbs: 'resthub/handlebars-require'
  390. }
  391. });
  392. Sample usage in a Backbone.js View:
  393. .. code-block:: javascript
  394. define(['jquery', 'resthub', 'hbs!template/todo'],function($, Resthub, todoTmpl) {
  395. var TodoView = Resthub.View.extend({
  396. //... is a list tag.
  397. tagName: 'li',
  398. // Resthub.View will automtically Handlebars template with model or collection set in the context
  399. template: todoTmpl;
  400. });
  401. Helpers
  402. -------
  403. Resthub provide some usefull **Handlebars helpers** included by default:
  404. ifinline
  405. ++++++++
  406. This helper provides a more fluent syntax for inline ifs, i.e. if embedded in quoted strings.
  407. As with Handlebars ``#if``, if its first argument returns ``false``, ``undefined``, ``null``
  408. or ``[]`` (a "falsy" value), ``''`` is returned, otherwise ``returnVal`` argument is rendered.
  409. e.g:
  410. .. code-block:: html
  411. <div class='{{ifinline done "done"}}'>Issue number 1</div>
  412. with the following context:
  413. .. code-block:: javascript
  414. {done:true}
  415. will produce:
  416. .. code-block:: html
  417. <div class='done'>Issue number 1</div>
  418. unlessinline
  419. ++++++++++++
  420. Opposite of ifinline helper.
  421. As with Handlebars ``#unless``, if its first argument returns ``false``, ``undefined``, ``null``
  422. or ``[]`` (a "falsy" value), ``returnVal`` is returned, otherwise ``''`` argument is rendered.
  423. e.g:
  424. .. code-block:: html
  425. <div class='{{unlessinline done "todo"}}'>Issue number 1</div>
  426. with the following context:
  427. .. code-block:: javascript
  428. {done:false}
  429. will produce:
  430. .. code-block:: html
  431. <div class='todo'>Issue number 1</div>
  432. ifequalsinline
  433. ++++++++++++++
  434. This helper provides a if inline comparing two values.
  435. If the two values are strictly equals (``===``) return the returnValue argument, ``''`` otherwise.
  436. e.g:
  437. .. code-block:: html
  438. <div class='{{ifequalsinline type "details" "active"}}'>Details</div>
  439. with the following context:
  440. .. code-block:: javascript
  441. {type:"details"}
  442. will produce:
  443. .. code-block:: html
  444. <div class='active'>Details</div>
  445. unlessequalsinline
  446. ++++++++++++++++++
  447. Opposite of ifequalsinline helper.
  448. If the two values are not strictly equals (``!==``) return the returnValue argument, ``''`` otherwise.
  449. e.g:
  450. .. code-block:: html
  451. <div class='{{unlessequalsinline type "details" "active"}}'>Edit</div>
  452. with the following context:
  453. .. code-block:: javascript
  454. {type:"edit"}
  455. will produce:
  456. .. code-block:: html
  457. <div class='active'>Edit</div>
  458. ifequals
  459. ++++++++
  460. This helper provides a if comparing two values.
  461. If only the two values are strictly equals (``===``) display the block
  462. e.g:
  463. .. code-block:: html
  464. {{#ifequals type "details"}}
  465. <span>This is details page</span>
  466. {{/ifequals}}
  467. with the following context:
  468. .. code-block:: javascript
  469. {type:"details"}
  470. will produce:
  471. .. code-block:: html
  472. <span>This is details page</span>
  473. unlessequals
  474. ++++++++++++
  475. Opposite of ifequals helper.
  476. If only the two values are not strictly equals (``!==``) display the block
  477. e.g:
  478. .. code-block:: html
  479. {{#unlessequals type "details"}}
  480. <span>This is not details page</span>
  481. {{/unlessequals}}
  482. with the following context:
  483. .. code-block:: javascript
  484. {type:"edit"}
  485. will produce:
  486. .. code-block:: html
  487. <span>This is not details page</span>
  488. for
  489. +++
  490. This helper provides a for i in range loop.
  491. start and end parameters have to be integers >= 0 or their string representation. start should be <= end.
  492. In all other cases, the block is not rendered.
  493. e.g:
  494. .. code-block:: html
  495. <ul>
  496. {{#for 1 5}}
  497. <li><a href='?page={{this}}'>{{this}}</a></li>
  498. {{/for}}
  499. </ul>
  500. will produce:
  501. .. code-block:: html
  502. <ul>
  503. <li><a href='?page=1'>1</a></li>
  504. <li><a href='?page=2'>2</a></li>
  505. <li><a href='?page=3'>3</a></li>
  506. <li><a href='?page=4'>4</a></li>
  507. <li><a href='?page=5'>5</a></li>
  508. </ul>
  509. .. _sprintf-helper:
  510. sprintf
  511. +++++++
  512. This 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>`_.
  513. e.g:
  514. .. code-block:: html
  515. <span>{{sprintf "This is a %s" "test"}}</span>
  516. will produce:
  517. .. code-block:: html
  518. <span>This is a test</span>
  519. This helper is very usefull for Internationalization_, and can take any number of parameters.
  520. modulo
  521. ++++++
  522. This helper provides a modulo function.
  523. If (n % m) equals 0 then the block is rendered, and if not, the else block is rendered if provided.
  524. e.g:
  525. .. code-block:: html
  526. {{#modulo index 2}}
  527. <span>{{index}} is even</span>
  528. {{else}}
  529. <span>{{index}} is odd</span>
  530. {{/modulo}}
  531. with the following context:
  532. .. code-block:: javascript
  533. {index:10}
  534. will produce:
  535. .. code-block:: html
  536. <span>10 is even</span>
  537. formatDate
  538. ++++++++++
  539. This helper provides a date formatting tool.
  540. The date will be parsed with the inputPattern and then formatted with the outputPattern.
  541. Parameters are:
  542. * date: the date to parse and format
  543. * outputPattern: the pattern used to display the date (optional)
  544. * inputPattern: the pattern used to parse the date (optional)
  545. inputPattern and outputPattern are optionals: the default pattern is 'YYYY-MM-DD HH:mm:ss'
  546. Full documentation about date format can be found `here <http://momentjs.com/docs/#/displaying/format/>`_.
  547. e.g:
  548. .. code-block:: html
  549. <span>{{formatDate myDate pattern}}</span>
  550. with the following context:
  551. .. code-block:: javascript
  552. { myDate: new Date(), pattern: '[today] MM/DD/YYYY' }
  553. will produce:
  554. .. code-block:: html
  555. <span>today 10/24/2012</span>
  556. and:
  557. .. code-block:: html
  558. <span>{{formatDate myDate outputPattern inputPattern}}</span>
  559. with the following context:
  560. .. code-block:: javascript
  561. { myDate: '2012/17/02 11h32', inputPattern: 'YYYY/DD/MM HH\\hmm', outputPattern: 'HH:mm, MM-DD-YYYY' }
  562. will produce:
  563. .. code-block:: html
  564. <span>11:32, 02-17-2012</span>
  565. .. _backbone-pushstate:
  566. Backbone effective pushState extension
  567. ======================================
  568. Backbone_ allows ``pushState`` activation that permits usage of real URLs instead of `#` anchors.
  569. PushState offers a better navigation experience, better indexation and search engine ranking:
  570. .. code-block:: javascript
  571. Backbone.history.start({pushState:true, root:"/"});
  572. The `root` option defines the path context of our Backbone_ application;
  573. However, Backbone_ stops here. Direct access to views by URL works fine but, each link leads to
  574. **a full reload**! Backbone_ does not intercept html links events and it is necessary to implement it ourselves.
  575. Branyen 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.
  576. If ``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:
  577. .. code-block:: html
  578. <a data-bypass href="http://github.com/bmeurant/tournament-front" target="_blank">
  579. .. _backbone-form-helper:
  580. Internationalization
  581. ====================
  582. You should never use directly labels or texts in your source files. All labels should be externalized in order to prepare your
  583. application 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>`_.
  584. Please find below the steps needed to internationalize your application.
  585. 1. **Configure i18n plugin**
  586. In your main.js file you should define a shortcut path for i18n plugin and the default language for your application:
  587. .. code-block:: javascript
  588. require.config({
  589. paths: {
  590. // ...
  591. i18n: "lib/i18n"
  592. },
  593. locale: localStorage.getItem('locale') || 'en-us'
  594. });
  595. 2. **Define labels**
  596. Create 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.
  597. Sample js/nls/labels.js file:
  598. .. code-block:: javascript
  599. define({
  600. // root is mandatory.
  601. 'root': {
  602. 'titles': {
  603. 'login': 'Login'
  604. }
  605. },
  606. "fr-fr": true
  607. });
  608. Add translations in subfolders named with the locale, for instance js/nls/fr-fr ...
  609. You should always keep the same file name, and the file located at the root will be used by default.
  610. Sample js/nls/fr-fr/labels.js file:
  611. .. code-block:: javascript
  612. define({
  613. 'titles': {
  614. 'login': 'Connexion'
  615. }
  616. });
  617. 3. **Use it**
  618. Add 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).
  619. Prepending 'i18n!' before the file path in the dependency indicates RequireJS to get the file related to the current locale:
  620. .. code-block:: javascript
  621. define(['i18n!nls/labels'], function(myLabels) {
  622. // ...
  623. labels: myLabels,
  624. // ...
  625. });
  626. In your html template:
  627. .. code-block:: html
  628. <div class="title">
  629. <h1>{{labels.titles.login}}</h1>
  630. </div>
  631. 4. **Change locale**
  632. Changing locale require a page reloading, so it is usually implemented with a Backbone.js router configuration like the following one:
  633. .. code-block:: javascript
  634. define(['backbone'], function(Backbone){
  635. var AppRouter = Backbone.Router.extend({
  636. routes: {
  637. 'fr': 'fr',
  638. 'en': 'en'
  639. },
  640. fr: function( ){
  641. var locale = localStorage.getItem('locale');
  642. if(locale != 'fr-fr') {
  643. localStorage.setItem('locale', 'fr-fr');
  644. location.reload();
  645. }
  646. },
  647. en: function( ){
  648. var locale = localStorage.getItem('locale');
  649. if(locale != 'en-us') {
  650. localStorage.setItem('locale', 'en-us');
  651. location.reload();
  652. }
  653. }
  654. });
  655. return AppRouter;
  656. });
  657. 5. **sprintf to the rescue**
  658. Internalionalization 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.
  659. You can use the ``_.sprintf()`` function and the ``sprintf`` helper to have substitutions in your labels.
  660. labels.js
  661. .. code-block:: javascript
  662. 'root': {
  663. 'clearitem': "Clear the completed item",
  664. 'clearitems': 'Clear %s completed items',
  665. }
  666. RESThub also provides a ``sprintf`` handlebars helper to use directly in your templates (cf. :ref:`sprintf-helper`):
  667. .. code-block:: html
  668. {{#ifequals done 1}} {{messages.clearitem}} {{else}} {{sprintf messages.clearitems done}} {{/ifequals}}
  669. Logging
  670. =======
  671. RESThub Backbone stack include a console.js implementation responsible for
  672. * Creating console.* functions if they do not exists (old IE versions)
  673. * Optionnaly sending logs to the server, in order to make JS error tracking and debugging easier
  674. In order to send logs to the server, import console.js in your main.js (already done by default):
  675. .. code-block:: javascript
  676. // Load our app module and pass it to our definition function
  677. require(['console', 'app']);
  678. In your app.js, you can define different console.level values, which define what log level will be sent to the server:
  679. .. code-block:: javascript
  680. console.level = 'off'; // Default, no log are sent to the server
  681. console.level = 'debug'; // debug, info, warn and error logs are sent to the server
  682. console.level = 'info'; // info, warn and error logs are sent to the server
  683. console.level = 'warn'; // warn and error logs are sent to the server
  684. console.level = 'error'; // error logs are sent to the server
  685. Javascript syntax error are also sent to the server with an error log level.
  686. You can customize the log server url:
  687. .. code-block:: javascript
  688. console.serverUrl = 'api/log'; // Default value
  689. Log are sent thanks a POST request with the following JSON body:
  690. .. code-block:: javascript
  691. {"level":"warn","message":"log message","time":"2012-11-13T08:18:52.972Z"}
  692. RESThub web server provide a builtin implementation of the serverside logging webservice, see the `related documentation <spring-stack.html#client-logging>`_ for more details.
  693. Message bus
  694. -----------
  695. Since backbone now extends Events, you can use it as a message bus for your global events.
  696. In order to facilitate global events usage in Backbone Views, RESThub provides some syntactic sugar in ``Resthub.View``.
  697. Backbone 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:
  698. .. code-block:: javascript
  699. events:{
  700. // regular DOM event bindings
  701. "click #btn1":"buttonClicked",
  702. "click #btn2":"buttonClicked",
  703. // global events
  704. "!global":"globalFired",
  705. "!global1":"globalFired",
  706. "!globalParams":"globalFiredParams"
  707. },
  708. Please note that it is mandatory to prefix your global events with ``!`` to differenciate them from DOM events.
  709. Under the cover, listenTo() and stopListening() are used so events cleanup will be done automatically by the view.
  710. .. _resthub-validation:
  711. Validation
  712. ==========
  713. Since 2.1.0, RESThub comes with custom server and client validation handlers allowing to export, via a dedicated API, the
  714. server side declared validation constraints (see `Spring Stack documentation <./spring-stack.html#validation-api>`_) and
  715. to interpret these constraints on the client side.
  716. This feature allows to define once (server side) your validation constraints that will be (if configured)
  717. automatically mapped on the client side to effective `Backbone Validation`_ (see also :ref:`backbone-validation`)
  718. constraints.
  719. Server side declared constraint validations will thus be fully reused and you won't have to 'clone' these
  720. constraints on the client side.
  721. Usage
  722. -----
  723. This feature is available by default but not active unless explicit configuration.
  724. Activate synchronization
  725. ++++++++++++++++++++++++
  726. Before any server side validation constraint reuse on any of your client models, **you have to
  727. implement or customize your model** ``initialize()`` **function** to call the ``Resthub.Validation`` namespace
  728. ``synchronize`` function:
  729. .. code-block:: javascript
  730. var UserModel = Backbone.Model.extend({
  731. className: 'org.resthub.validation.model.User',
  732. initialize: function() {
  733. Resthub.Validation.synchronize(UserModel);
  734. }
  735. });
  736. This function takes the current model as a mandatory parameter. It accepts also an optional parameter
  737. ``errorCallback`` (cf. :ref:`validation-errors`).
  738. Activate Backbone Validation in views
  739. +++++++++++++++++++++++++++++++++++++
  740. RESThub Validation will be effective only if Backbone Validation is correctly configured in view
  741. (see :ref:`backbone-validation`). For instance:
  742. .. code-block:: javascript
  743. var UserView = Resthub.View.extend({
  744. // Define view template
  745. template: userTemplate,
  746. events: {
  747. 'submit form': 'onSubmitForm'
  748. },
  749. initialize: function() {
  750. // Initialize the model
  751. this.model = new User();
  752. Backbone.Validation.bind(this);
  753. this.render();
  754. },
  755. onSubmitForm: function(event) {
  756. ...
  757. this.save();
  758. },
  759. save: function() {
  760. this.populateModel();
  761. if (this.model.isValid()) {
  762. // ...
  763. } else {
  764. // ...
  765. }
  766. }
  767. });
  768. This code sample is taken from a complete validation sample that you can find
  769. `here <https://github.com/bmeurant/resthub-validation-sample>`_. Don't hesitate to checkout this sample
  770. to see working samples.
  771. .. _validation-lifecycle:
  772. Lifecycle
  773. +++++++++
  774. Doing this, all validation constraints will be **transparently synchronized from the server during a model instantiation**
  775. (i.e. ``new UserModel()``). A GET request will be thus sent to the server with the given className
  776. to get server validation constraints.
  777. Resthub Validation optimizes this process by sending the GET request **only on the first model instantiation**. So
  778. constraints validation synchronization will only be performed on the first instantiation of a given model - deduced
  779. Backbone Validation constraints will be **reused accross all instances of this model**.
  780. Note that the synchronization process will be **reset after a locale update** (see :ref:`validation-change-locale`) or
  781. could be **manually forced** (see below).
  782. Force synchronization
  783. #####################
  784. Synchronization of a given model (in fact, on a given class name) could be forced using a dedicated ``Resthub.Validation``
  785. namespace function: ``forceSynchroForClass``.
  786. .. code-block:: javascript
  787. Resthub.Validation.forceSynchroForClass("org.resthub.validation.model.User");
  788. This function must be called with a mandatory parameter *className* corresponding to the declared model
  789. className (see :ref:`validation-options`).
  790. This operation resets the synchronized information for the given className, this means that **the GET request
  791. (and constraint binding) will be sent again on the next model instantiation**.
  792. .. _validation-options:
  793. Parameters & Options
  794. ++++++++++++++++++++
  795. You can configure or parametrize RESThub Validation with a set of parameters and options.
  796. API url
  797. #######
  798. The validation **api base url can be configured in** ``Resthub.Validation`` namespace ``options.apiUrl`` :
  799. .. code-block:: javascript
  800. Resthub.Validation.options.apiUrl = 'new/url';
  801. Default value is ``'api/validation'``.
  802. className
  803. #########
  804. **Each model to be synchronized must hold a className attribute** containing the complete qualified name of the
  805. corresponding Java class (i.e. package + name. see `Spring Stack documentation <./spring-stack.html#validation-api>`_).
  806. .. code-block:: javascript
  807. var UserModel = Backbone.Model.extend({
  808. className: 'org.resthub.validation.model.User',
  809. initialize: function() {
  810. Resthub.Validation.synchronize(UserModel);
  811. }
  812. ...
  813. });
  814. messages
  815. ########
  816. You can provide an key/value pair object ``messages`` to any of your model or globally in ``Resthub.Validation`` namespace
  817. to specify custom error messages that will replace default messages from server (see :ref:`validation-messages` for details).
  818. .. code-block:: javascript
  819. var UserModel = Backbone.Model.extend({
  820. className: 'org.resthub.validation.model.User',
  821. messages: {
  822. 'validation.Min.message': 'should be greater than {value} or equals'
  823. },
  824. initialize: function() {
  825. Resthub.Validation.synchronize(UserModel);
  826. }
  827. ...
  828. });
  829. includes / excludes
  830. ###################
  831. By default, **all constraints exported by the server API are mapped** and converted into Backbone Validation constraints
  832. and then added as active validation constraints on the client side.
  833. You can configure this behaviour **for each of your model by specifying includes or excludes retrictions on it**.
  834. Only properties names found in an **includes** array will be **mapped** :
  835. .. code-block:: javascript
  836. var UserModel = Backbone.Model.extend({
  837. className: 'org.resthub.validation.model.User',
  838. includes: ['login', 'firstName', 'lastName'],
  839. initialize: function() {
  840. Resthub.Validation.synchronize(UserModel);
  841. }
  842. ...
  843. });
  844. Each property name found in an **excludes** array will be **ignored** :
  845. .. code-block:: javascript
  846. var UserModel = Backbone.Model.extend({
  847. className: 'org.resthub.validation.model.User',
  848. excludes: ['password'],
  849. initialize: function() {
  850. Resthub.Validation.synchronize(UserModel);
  851. }
  852. ...
  853. });
  854. Server constraints mapping
  855. --------------------------
  856. Once all server validation constraints retrieved from server, RESThub Validation tries to map each constraint to
  857. a valid Backbone Validation constraint, if supported.
  858. .. _validation-supported-constraints:
  859. Supported constraints
  860. +++++++++++++++++++++
  861. Supported constraints are described below. You will find in this chapter the description of the mapped constraints
  862. and the way it is mapped to a Backbone Validation constraint.
  863. If the client receive a non supported server validation constraint, it will be ignored unless you provide a specific
  864. and custom constraint validator (see :ref:`validation-add-constraint`).
  865. NotNull
  866. #######
  867. The property must not be undefined or null and, in case of String cannot be neither empty ("")
  868. nor blank (" ").
  869. NotBlank or NotEmpty
  870. ####################
  871. The property must not be undefined or null, in case of String cannot be neither empty ("")
  872. nor blank (" "), in case of array cannot be empty.
  873. Null
  874. ####
  875. The property must be null or undefined or, in case of String, empty ("") or blank (" ").
  876. AssertTrue
  877. ##########
  878. The property must be either a boolean to ``true`` or a String equals to ``"true"``.
  879. null values are considered valid.
  880. AssertFalse
  881. ###########
  882. The property must be either a boolean to ``false`` or a String different of ``"true"``.
  883. Size
  884. ####
  885. The property must be a String or an array with size between the specified boundaries (included).
  886. null values are considered valid.
  887. available parameters:
  888. - *min*: size the property must be higher or equal to
  889. - *max*: size the property must be lower or equal to
  890. Min
  891. ###
  892. The property must be an integer number whose value must be higher or equal to the specified minimum.
  893. null values are considered valid.
  894. available parameters:
  895. - *value*: value the property must be higher or equal to
  896. DecimalMin
  897. ##########
  898. The property must be floating number whose value must be higher or equal to the specified minimum.
  899. null values are considered valid.
  900. available parameters:
  901. - *value*: value the property must be higher or equal to
  902. Max
  903. ###
  904. The property must be an integer number whose value must be lower or equal to the specified minimum.
  905. null values are considered valid.
  906. available parameters:
  907. - *value*: value the property must be lower or equal to
  908. DecimalMax
  909. ##########
  910. The property must be an integer number whose value must be lower or equal to the specified minimum.
  911. null values are considered valid.
  912. available parameters:
  913. - *value*: value the property must be lower or equal to
  914. Pattern
  915. #######
  916. The property must match the specified regular expression.
  917. null values are considered valid.
  918. available parameters:
  919. - *regexp*: regular expression to match
  920. URL
  921. ###
  922. The property must represent a valid URL. Parameters allow to verify specific parts of the parsed URL.
  923. 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]*))?)/``
  924. null values are considered valid.
  925. available parameters:
  926. - *protocol*: specify the protocol the property must match. Per default any protocol is allowed.
  927. - *host*: specify the host regexp the property must match. Per default any host is allowed.
  928. - *port*: specify the port the property must match. Per default any port is allowed.
  929. options
  930. ~~~~~~~
  931. You can **customize URL validator pattern** to match by overriding ``Resthub.Validation.options.URL.pattern``:
  932. .. code-block:: javascript
  933. Resthub.Validation.options.URL.pattern = /my pattern/;
  934. Range
  935. #####
  936. The property must be numeric values or string representation of the numeric value with value between specified range.
  937. available parameters:
  938. - *min*: value the property must be higher or equal to
  939. - *max*: value the property must be lower or equal to
  940. Length
  941. ######
  942. The property must be a string with length between min and max included.
  943. available parameters:
  944. - *min*: value the property length must be higher or equal to
  945. - *max*: value the property length must be lower or equal to
  946. Email
  947. #####
  948. The property must be a valid email (see `Backbone Validation built in email pattern constraint <https://github.com/thedersen/backbone.validation#pattern>`_).
  949. CreditCardNumber
  950. ################
  951. The property must be a valid credit card number according `Lunh algorithm <http://en.wikipedia.org/wiki/Luhn_algorithm>`_.
  952. Customize constraints definition
  953. --------------------------------
  954. Model validation constraints can be customized by adding specific client validation, overriding
  955. constraints synchronized from server or adding custom constraint mapper for a specific BeanValidation server constraint.
  956. Adding client constraints
  957. +++++++++++++++++++++++++
  958. You can **provide additional client constraints** as usual in a standard Backbone Validation way. This client specific
  959. constraints **will then be merged** with synchronized server constraints:
  960. .. code-block:: javascript
  961. var UserModel = Backbone.Model.extend({
  962. className: 'org.resthub.validation.model.User',
  963. initialize: function() {
  964. Resthub.Validation.synchronize(UserModel);
  965. },
  966. validation: {
  967. confirmPassword: {
  968. equalTo: 'password'
  969. }
  970. }
  971. });
  972. Overriding constraints
  973. ++++++++++++++++++++++
  974. You can also **override a property constraint already synchronized from server** : only the client constraint will
  975. be kept:
  976. .. code-block:: javascript
  977. var UserModel = Backbone.Model.extend({
  978. className: 'org.resthub.validation.model.User',
  979. initialize: function() {
  980. Resthub.Validation.synchronize(UserModel);
  981. },
  982. validation: {
  983. email: {
  984. required: true,
  985. pattern: \my pattern\
  986. }
  987. }
  988. });
  989. .. _validation-add-constraint:
  990. Adding custom constraints
  991. +++++++++++++++++++++++++
  992. If provided a custom JSR303 compliant validation annotation on the server side, you can easily add a custom client validator
  993. for your custom constraint with a dedicated RESThub Validation API allowing to **define a new validator or override an
  994. existing one** and retrieve an existing validator:
  995. .. code-block:: javascript
  996. // add or replace the validator associated to the given constraintType.
  997. // validator parameter should be a function
  998. ResthubValidation.addValidator = function(constraintType, validator) {
  999. validators[constraintType] = validator;
  1000. };
  1001. // retrieve the validator associated to a given constraint type
  1002. ResthubValidation.getValidator = function(constraintType) {
  1003. return validators[constraintType];
  1004. };
  1005. To map your new constraint, you only have to declare a new validator associated to your constraint type (the annotation
  1006. name in server side) :
  1007. .. code-block:: javascript
  1008. Resthub.Validation.addValidator('TelephoneNumber', function(constraint, msg) {
  1009. return {
  1010. pattern: /^[+]?([0-9]*[\\.\\s\\-\\(\\)]|[0-9]+){6,24}$/,
  1011. msg: msg
  1012. };
  1013. });
  1014. .. _validation-messages:
  1015. Messages and internationalization
  1016. ---------------------------------
  1017. Internationalization can be managed in different ways : sending locale to server or providing custom messages globally
  1018. in resthub.Validation or locally in each of your model.
  1019. Default behaviour
  1020. +++++++++++++++++
  1021. By default, Resthub Validation adds a ``locale`` parameter to any validation related server call.
  1022. e.g. ``/api/validation/org.resthub.validation.model.User?locale=en``.
  1023. Error messages are thus returned from server with the asked locale and displayed client side as it.
  1024. This is the behaviour that will be applied without any specific configuration. i.e:
  1025. .. code-block:: javascript
  1026. var UserModel = Backbone.Model.extend({
  1027. className: 'org.resthub.validation.model.User',
  1028. initialize: function() {
  1029. Resthub.Validation.synchronize(UserModel);
  1030. }
  1031. ...
  1032. });
  1033. .. _validation-change-locale:
  1034. Change locale
  1035. +++++++++++++
  1036. Wihtout any further configuration, the current browser locale is taken (copied in Resthub.Validation and sent
  1037. to server). But you can easily **change locale using Resthub Validation API function** ``locale()`` :
  1038. .. code-block:: javascript
  1039. Resthub.Validation.locale("fr");
  1040. This operation will change the current active locale of Resthub Validation and, even more important, will **force
  1041. the synchronization process to send a new request** to server for next model initialization in order to **refresh
  1042. constraints** with server localized messages.
  1043. **You have to explicitely call this function with your new locale on app local update**. If you don't, no request will
  1044. be sent to server for already synchronized models (because of caching - see :ref:`validation-lifecycle`).
  1045. Client error messages customization
  1046. +++++++++++++++++++++++++++++++++++
  1047. If you want to **manage all or parts of your error messages in client side** - allowing, for instance to build your messages
  1048. uppon a common i18n mechanism such as requirejs i18n plugin - you'll have to provide specific configuration
  1049. either globally in ``Resthub.Validation`` namespace or locally in each of your model.
  1050. This means that you'll provide a dedicated ``messages`` key-value pairs object:
  1051. - **key**: contains the constraint message key built as follows: ``'validation.{ConstraintName}.message'``
  1052. where ``ConstraintName`` is the name of the contraint, **in camel case and starting by an upper case letter**.
  1053. - **value**: contains the constraint message text that could be parametrized, depending on available
  1054. parameters of each constraint (see below and :ref:`validation-supported-constraints`).
  1055. e.g. :
  1056. .. code-block:: javascript
  1057. messages: {
  1058. 'validation.Min.message': 'should be greater than {value} or equals',
  1059. 'validation.NotNull.message': 'should not be null'
  1060. },
  1061. If a messages object is provided, globally or locally (see below), RESThub Validation will check if the current
  1062. constraint exists in messages and affect this message value to the corresponding built Backbone Validation
  1063. constraint. If the key does not exist, the default message returned by server is returned.
  1064. Error messages templating
  1065. #########################
  1066. Client error message value definition can be **defined with custom messages templates** to dynamically include
  1067. constraints parameters values in the resulting message.
  1068. You can thus display, in your message, any available parameter of the current constraint
  1069. (see :ref:`validation-supported-constraints`) by using the curly brackets ``{...}`` syntax :
  1070. .. code-block:: javascript
  1071. messages: {
  1072. 'validation.Size.message': 'should be greater than {min} or equals and lower than {max} or equals'
  1073. },
  1074. Any parameter value that is not an available parameter for this constraint will be ignored.
  1075. Customize globally (Resthub.Validation)
  1076. #######################################
  1077. Custom client messages can be provided directly in ``Resthub.Validation`` messages :
  1078. .. code-block:: javascript
  1079. Resthub.Validation.messages = {
  1080. 'validation.TelephoneNumber.message': 'telephone number is not valid'
  1081. };
  1082. This allows you to define error messages that will be **global to your entire app and reused on all of your models**.
  1083. These messages will **override server error messages**.
  1084. Customize locally (Model)
  1085. #########################
  1086. You can also provide a **model specific messages object** if have specific needs for a given model:
  1087. .. code-block:: javascript
  1088. var UserModel = Backbone.Model.extend({
  1089. className: 'org.resthub.validation.model.User',
  1090. messages: {
  1091. 'validation.Min.message': 'should be greater than {value} or equals'
  1092. },
  1093. initialize: function() {
  1094. Resthub.Validation.synchronize(UserModel);
  1095. }
  1096. ...
  1097. });
  1098. These messages will **override server error messages and** ``Resthub.Validation`` **global messages**.
  1099. .. _validation-errors:
  1100. Errors management
  1101. -----------------
  1102. By default, any synchronization process error (e.g. server anavailable, className not found, etc.) will
  1103. **simply log an error message in console**.
  1104. Obviously, no validation constraint will be retrieved from server and any client side defined cosntraint will be kept
  1105. as it.
  1106. **You can provide either global or local customization of this behaviour** (for instance sending a global event
  1107. to display a user friendly alert, ...).
  1108. Customize globally (Resthub.Validation)
  1109. +++++++++++++++++++++++++++++++++++++++
  1110. You can override the error callback directly in ``Resthub.Validation`` namespace (for instance in your app.js file) :
  1111. .. code-block:: javascript
  1112. Resthub.Validation.options.errorCallback = function(resp) {
  1113. // your specific code
  1114. };
  1115. The ``resp`` parameter is the server response.
  1116. Customize locally (Model)
  1117. +++++++++++++++++++++++++
  1118. Custom error callback could be also **provided in model on synchronize call** as an optional parameter :
  1119. .. code-block:: javascript
  1120. var UserModel = Backbone.Model.extend({
  1121. className: 'org.resthub.validation.model.User',
  1122. initialize: function() {
  1123. Resthub.Validation.synchronize(UserModel, function(resp) {// your specific code});
  1124. }
  1125. ...
  1126. });
  1127. Customize locally (Model instance)
  1128. ++++++++++++++++++++++++++++++++++
  1129. You can even provide a model **instance specific callback** by customizing your model initialize method with
  1130. a custom ``errorCallback`` parameter option member (for instance, in your view in order to display the error in a
  1131. view specific zone) :
  1132. - **model**:
  1133. .. code-block:: javascript
  1134. var UserModel = Backbone.Model.extend({
  1135. ...
  1136. initialize: function (attributes, options) {
  1137. Resthub.Validation.synchronize(UserModel, options.errorCallback);
  1138. },
  1139. ...
  1140. });
  1141. - **view**:
  1142. .. code-block:: javascript
  1143. var UserView = Resthub.View.extend({
  1144. ...
  1145. initialize: function() {
  1146. // Initialize the collection
  1147. this.model = new User({}, {errorCallback: function(resp) {// your specific code}});
  1148. Backbone.Validation.bind(this);
  1149. this.render();
  1150. },
  1151. ...
  1152. });
  1153. Other librairies included in the stack
  1154. ======================================
  1155. .. _backbone-validation:
  1156. Backbone Validation
  1157. -------------------
  1158. Backbone_ does not provide natively **any tool for form or validation management**. It is not necessary
  1159. to specify model attributes or related constraints.
  1160. In terms of validation, Backbone_ provides only empty methods ``validate`` and ``isValid`` that have to be implemented by each developer.
  1161. The only guarantee that the ``validate`` method is called before a ``save`` (canceled on error). But a complete form validation is
  1162. not obvious (custom error array management ... ) and the errors are not distinguishable from inherent ``save`` errors (server communication and so on).
  1163. `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
  1164. validators** and **provides effective validators customization and extension mechanisms**.
  1165. `Backbone Validation`_ does not neither propose automatic linking between form and model and leaves us the choice to use a dedicated lib or
  1166. to implement custom behaviour (before the validation, process all form values to set to model). The behaviour of `Backbone Validation`_ perfectly matches standard
  1167. Backbone_ workflow through ``validate`` and ``isValid`` methods.
  1168. **Model**: constraints definition:
  1169. .. code-block:: javascript
  1170. define(['underscore', 'backbone'], function (_, Backbone) {
  1171. /**
  1172. * Definition of a Participant model object
  1173. */
  1174. var ParticipantModel = Backbone.Model.extend({
  1175. urlRoot:App.Config.serverRootURL + "/participant",
  1176. defaults:{
  1177. },
  1178. // Defines validation options (see Backbone-Validation)
  1179. validation:{
  1180. firstname:{
  1181. required:true
  1182. },
  1183. lastname:{
  1184. required:true
  1185. },
  1186. email:{
  1187. required:false,
  1188. pattern:'email'
  1189. }
  1190. },
  1191. initialize:function () {
  1192. }
  1193. });
  1194. return ParticipantModel;
  1195. });
  1196. **HTML5 Form**:
  1197. .. code-block:: html
  1198. {{#with participant}}
  1199. <form class="form-horizontal">
  1200. <fieldset>
  1201. <div class="row">
  1202. <div class="span8">
  1203. <div class="control-group">
  1204. {{#if id}}
  1205. <label for="participantId" class="control-label">Id:</label>
  1206. <div class="controls">
  1207. <input id="participantId" name="id" type="text" value="{{id}}" disabled/>
  1208. </div>
  1209. {{/if}}
  1210. </div>
  1211. <div class="control-group">
  1212. <label for="firstname" class="control-label">First name:</label>
  1213. <div class="controls">
  1214. <input type="text" id="firstname" name="firstname" required="true" value="{{firstname}}" tabindex="1" autofocus="autofocus"/>
  1215. <span class="help-inline"></span>
  1216. </div>
  1217. </div>
  1218. <div class="control-group">
  1219. <label for="lastname" class="control-label">Last name:</label>
  1220. <div class="controls">
  1221. <input type="text" id="lastname" name="lastname" required="true" value="{{lastname}}" tabindex="2"/>
  1222. <span class="help-inline"></span>
  1223. </div>
  1224. </div>
  1225. <div class="control-group">
  1226. <label for="email" class="control-label">email address:</label>
  1227. <div class="controls">
  1228. <input type="email" id="email" name="email" value="{{email}}" tabindex="3"/>
  1229. <span class="help-inline"></span>
  1230. </div>
  1231. </div>
  1232. </div>
  1233. </fieldset>
  1234. </form>
  1235. {{/with}}
  1236. **View**: initialization and usage:
  1237. .. code-block:: javascript
  1238. initialize:function () {
  1239. ...
  1240. // allow backbone-validation view callbacks (for error display)
  1241. Backbone.Validation.bind(this);
  1242. ...
  1243. },
  1244. ...
  1245. /**
  1246. * Save the current participant (update or create depending of the existence of a valid model.id)
  1247. */
  1248. saveParticipant:function () {
  1249. // build array of form attributes to refresh model
  1250. var attributes = {};
  1251. this.$el.find("form input[type!='submit']").each(function (index, value) {
  1252. attributes[value.name] = value.value;
  1253. this.model.set(value.name, value.value);
  1254. }.bind(this));
  1255. // save model if it's valid, display alert otherwise
  1256. if (this.model.isValid()) {
  1257. this.model.save(null, {
  1258. success:this.onSaveSuccess.bind(this),
  1259. error:this.onSaveError.bind(this)
  1260. });
  1261. }
  1262. else {
  1263. ...
  1264. }
  1265. }
  1266. You also natively beneficate of custom validation callbacks allowing to render validation errors in a
  1267. form structured with `Twitter Bootstrap`_.
  1268. Since the 2.1.0 version, Resthub provides **server to client validation bindings features** in order to define constraints
  1269. only once. See :ref:`resthub-validation` for details.
  1270. Backbone Query Parameters
  1271. -------------------------
  1272. Backbone_ routes management allows to define permet such routes:
  1273. ``"participants":"listParticipants"`` and ``"participants?:param":"listParticipantsParameters"``. But the native behaviour seems not sufficient:
  1274. * **management of an unknown number of parameters** (ex ``?page=2&filter=filter``) is not obvious
  1275. * we have to define (at least) **two routes to handle calls with or without parameters** without duplication
  1276. and without too much technical code
  1277. Expected behaviour was that the **map a single route to a method with an array of request parameter as optional parameter.**
  1278. `Backbone Query Parameters`_ provides this functionality.
  1279. With this lib, included once and for all in the main router, You 'll get the following:
  1280. **router.js**:
  1281. .. code-block:: javascript
  1282. define(['backbone', 'backbone-queryparams'], function (Backbone) {
  1283. var AppRouter = Backbone.Router.extend({
  1284. routes:{
  1285. // Define some URL routes
  1286. ...
  1287. "participants":"listParticipants",
  1288. ...
  1289. },
  1290. ...
  1291. listParticipants:function (params) {
  1292. // params contains the list of all query params of is empty if no param
  1293. }
  1294. });
  1295. });
  1296. Query parameters array is automatically recovered **without any further operation** and **whatever the number
  1297. of these parameters**. It can then be passed to the view constructor for initialization:
  1298. **list.js**:
  1299. .. code-block:: javascript
  1300. askedPage:1,
  1301. initialize:function (params) {
  1302. ...
  1303. if (params) {
  1304. if (params.page && this.isValidPageNumber(params.page)) this.askedPage = parseInt(params.page);
  1305. }
  1306. ...
  1307. },
  1308. Backbone Datagrid
  1309. -----------------
  1310. `Backbone Datagrid`_ is a powerful component, based on Backbone.View, that
  1311. displays your Bakbone collections in a dynamic datagrid table. It is highly
  1312. customizable and configurable with sensible defaults.
  1313. You will find the full documentation on its `dedicated website
  1314. <http://loicfrering.github.com/backbone.datagrid/>`_. Do not miss the examples
  1315. listed on `this page <http://loicfrering.github.com/backbone.datagrid/examples/>`_. Their sources are
  1316. available in the `examples <https://github.com/loicfrering/backbone.datagrid/tree/master/examples/>`_
  1317. directory of the repository.
  1318. * Solar: a simple and complete example with an in memory collection of planets from the Solar System.
  1319. * `Live version <http://loicfrering.github.com/backbone.datagrid/examples/solar.html>`_
  1320. * `Sources <https://github.com/loicfrering/backbone.datagrid/tree/master/examples/js/solar.js>`_
  1321. * GitHub: an example with a collection connected to GitHub's REST API.
  1322. * `Live version <http://loicfrering.github.com/backbone.datagrid/examples/github.html>`_
  1323. * `Sources <https://github.com/loicfrering/backbone.datagrid/tree/master/examples/js/github.js>`_
  1324. Note that the Backbone Datagrid handles pagination by itself and does not rely
  1325. on Backbone Paginator which is described below and should only be used to
  1326. paginate collections which are not displayed in a datagrid.
  1327. Backbone Paginator
  1328. ------------------
  1329. `Backbone Paginator`_ offers both client side pagination (``Paginator.clientPager``) and integration with server side pagination
  1330. (``Paginator.requestPager``). It includes management of filters, sorting, etc.
  1331. Client side pagination
  1332. ++++++++++++++++++++++
  1333. This lib extends Backbone_ collections. So adding options to collections is necessary:
  1334. .. code-block:: javascript
  1335. var participantsCollection = Backbone.Paginator.clientPager.extend({
  1336. model:participantModel,
  1337. paginator_core:{
  1338. // the type of the request (GET by default)
  1339. type:'GET',
  1340. // the type of reply (jsonp by default)
  1341. dataType:'json',
  1342. // the URL (or base URL) for the service
  1343. url:App.Config.serverRootURL + '/participants'
  1344. },
  1345. paginator_ui:{
  1346. // the lowest page index your API allows to be accessed
  1347. firstPage:1,
  1348. // which page should the paginator start from
  1349. // (also, the actual page the paginator is on)
  1350. currentPage:1,
  1351. // how many items per page should be shown
  1352. perPage:12,
  1353. // a default number of total pages to query in case the API or
  1354. // service you are using does not support providing the total
  1355. // number of pages for us.
  1356. // 10 as a default in case your service doesn't return the total
  1357. totalPages:10
  1358. },
  1359. parse:function (response) {
  1360. return response;
  1361. }
  1362. });
  1363. Then we ``fetch`` the collection and then ask for the right page:
  1364. .. code-block:: javascript
  1365. this.collection = new ParticipantsCollection();
  1366. // get the participants collection from server
  1367. this.collection.fetch({
  1368. success:function () {
  1369. this.collection.goTo(this.askedPage);
  1370. }.bind(this),
  1371. error:function (collection, response) {
  1372. ...
  1373. }
  1374. });
  1375. Once the collection retrieved, ``collection.info()`` allows to get information about current state:
  1376. .. code-block:: javascript
  1377. totalUnfilteredRecords
  1378. totalRecords
  1379. currentPage
  1380. perPage
  1381. totalPages
  1382. lastPage
  1383. previous
  1384. next
  1385. startRecord
  1386. endRecord
  1387. Server side pagination
  1388. ++++++++++++++++++++++
  1389. Once client side pagination implemented, server adaptation is very easy:
  1390. We set **parameters to send to server** in ``collections/participants.js``:
  1391. .. code-block:: javascript
  1392. server_api:{
  1393. 'page':function () {
  1394. return this.currentPage;
  1395. },
  1396. 'size':function () {
  1397. return this.perPage;
  1398. }
  1399. },
  1400. Then, in the same file, we provide a parser to get the response back and initialize collection and pager:
  1401. .. code-block:: javascript
  1402. parse:function (response) {
  1403. var participants = response.content;
  1404. this.totalPages = response.totalPages;
  1405. this.totalRecords = response.totalElements;
  1406. this.lastPage = this.totalPages;
  1407. return participants;
  1408. }
  1409. Finally, we change server call: this time the ``goTo`` method extend ``fetch`` and should be called instead
  1410. (``views/participants/list.js``):
  1411. .. code-block:: javascript
  1412. // get the participants collection from server
  1413. this.collection.goTo(this.askedPage,
  1414. {
  1415. success:function () {
  1416. ...
  1417. }.bind(this),
  1418. error:function () {
  1419. ...
  1420. }
  1421. });
  1422. All other code stay inchanged but the ``collection.info()`` is a little bit thinner:
  1423. .. code-block:: javascript
  1424. totalRecords
  1425. currentPage
  1426. perPage
  1427. totalPages
  1428. lastPage
  1429. Async
  1430. -----
  1431. Other recurrent problem: parallel asynchronous calls for which we want to have a
  1432. final processing in order to display the results of the entire process: number of errors, successes,
  1433. etc.
  1434. Basically, each asynchronous call define a callback invoked at the end of his own treatment (success or error).
  1435. Without tools, we are thus obliged to implement a **manual count of called functions and a count
  1436. of callbacks called to compare**. The final callback is then called at the end of each call unit
  1437. but executed only if there is no more callback to call. This gives:
  1438. .. code-block:: javascript
  1439. /**
  1440. * Effective deletion of all element ids stored in the collection
  1441. */
  1442. deleteElements:function () {
  1443. var self = this;
  1444. var nbWaitingCallbacks = 0;
  1445. $.each(this.collection, function (type, idArray) {
  1446. $.each(idArray, function (index, currentId) {
  1447. nbWaitingCallbacks += 1;
  1448. $.ajax({
  1449. url:App.Config.serverRootURL + '/participant/' + currentId,
  1450. type:'DELETE'
  1451. })
  1452. .done(function () {
  1453. nbWaitingCallbacks -= 1;
  1454. self.afterRemove(nbWaitingCallbacks);
  1455. })
  1456. .fail(function (jqXHR) {
  1457. if (jqXHR.status != 404) {
  1458. self.recordError(type, currentId);
  1459. }
  1460. nbWaitingCallbacks -= 1;
  1461. self.afterRemove(nbWaitingCallbacks);
  1462. });
  1463. });
  1464. });
  1465. },
  1466. /**
  1467. * Callback called after an ajax deletion request
  1468. *
  1469. * @param nbWaitingCallbacks number of callbacks that we have still to wait before close request
  1470. */
  1471. afterRemove:function (nbWaitingCallbacks) {
  1472. // if there is still callbacks waiting, do nothing. Otherwise it means that all request have
  1473. // been performed: we can manage global behaviours
  1474. if (nbWaitingCallbacks == 0) {
  1475. // do something
  1476. }
  1477. },
  1478. This code works but there is **too much technical code**!
  1479. Async_ provides a set of helpers to perform **asynchronous parallel processing** and synchronize the end of
  1480. these treatments through a final callback called once.
  1481. This lib is initially developed for nodeJS server but has been **implemented on browser side**.
  1482. Theoretically, the method we currently need is ``forEach``. However, we faced the following problem: all of these helpers
  1483. are designed to stop everything (and call the final callback) when the first error occurs.
  1484. But if we need to perform all server calls and only then, whether successful or fail, return global results
  1485. to the user, there is unfortunately no appropriate option (despite similar requests on mailing lists) ...
  1486. You can twick a little and, instead of ``forEach``, use the ``map`` function that returns a result array
  1487. in which you can register successes and errors. error parameter of the final callback cannot be used without
  1488. stopping everything. So, the callback should always be called with a ``null`` err parameter and a custom wrapper containing the
  1489. returned object and the type of the result: ``success`` or ``error``. You can then globally count errors without
  1490. interrupting your calls:
  1491. .. code-block:: javascript
  1492. /**
  1493. * Effective deletion of all element ids stored in the collection
  1494. */
  1495. deleteElements:function () {
  1496. ...
  1497. async.map(elements, this.deleteFromServer.bind(this), this.afterRemove.bind(this));
  1498. },
  1499. deleteFromServer:function (elem, deleteCallback) {
  1500. $.ajax({
  1501. url:App.Config.serverRootURL +'/' + elem.type + '/' + elem.id,
  1502. type:'DELETE'
  1503. })
  1504. .done(function () {
  1505. deleteCallback(null, {type:"success", elem:elem});
  1506. })
  1507. .fail(function (jqXHR) {
  1508. ...
  1509. // callback is called with null error parameter because otherwise it breaks the
  1510. // loop and top on first error :-(
  1511. deleteCallback(null, {type:"error", elem:elem});
  1512. }.bind(this));
  1513. },
  1514. /**
  1515. * Callback called after all ajax deletion requests
  1516. *
  1517. * @param err always null because default behaviour break map on first error
  1518. * @param results array of fetched models: contain null value in cas of error
  1519. */
  1520. afterRemove:function (err, results) {
  1521. // no more test
  1522. ...
  1523. },
  1524. Keymaster
  1525. ---------
  1526. Keymaster_ is a micro library allowing to define listeners on keyboard shortcuts and propagate them.
  1527. The syntax is elegant, it is very simple while very complete:
  1528. * Management of multiple hotkeys
  1529. * Chaining through an important number of "modifiers"
  1530. * Source DOM element type filtering
  1531. * ...
  1532. It is so simple that the doc is self sufficient - see `here <http://github.com/madrobby/keymaster>`_
  1533. Backbone Relational
  1534. -------------------
  1535. `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.
  1536. Backbone-relational features:
  1537. * Bidirectional relations, which notify related models of changes through events.
  1538. * Control how relations are serialized using the includeInJSON option.
  1539. * Automatically convert nested objects in a model's attributes into Model instances using the createModels option.
  1540. * Lazily retrieve (a set of) related models through the fetchRelated(key<string>, [options<object>], update<bool>) method.
  1541. * Determine the type of HasMany collections with collectionType.
  1542. * Bind new events to a Backbone.RelationalModel for:
  1543. * addition to a HasMany relation (bind to add:<key>; arguments: (addedModel, relatedCollection)),
  1544. * removal from a HasMany relation (bind to remove:<key>; arguments: (removedModel, relatedCollection)),
  1545. * reset of a HasMany relation (bind to reset:<key>; arguments: (relatedCollection)),
  1546. * changes to the key itself on HasMany and HasOne relations (bind to update:<key>; arguments=(model, relatedModel/relatedCollection)).
  1547. Moment
  1548. ------
  1549. `Moment`_ is a date library for parsing, validating, manipulating, and formatting dates.
  1550. Moment.js features:
  1551. * Parse and format date with custom pattern and internationalization
  1552. * Date manipulation (add, substract)
  1553. * Durations (eg: 2 hours)
  1554. Guidelines
  1555. ==========
  1556. Collection View
  1557. ---------------
  1558. If you need to render a simple list of elements, just make a single view with an each loop in the template:
  1559. .. code-block:: html
  1560. <h1>My TodoList</h1>
  1561. <ul>
  1562. {{#each this}}
  1563. <li>{{title}}</li>
  1564. {{/each}}
  1565. </ul>
  1566. But 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.
  1567. You can see more details on the `Todo example <https://github.com/resthub/todo-backbone-example>`_ (have a look to TodosView and TodoView).
  1568. Always use listenTo instead of on
  1569. ---------------------------------
  1570. In order to allow automatic cleanup when the View is removed, you should always use listenTo function instead of on
  1571. .. code-block:: javascript
  1572. // BAD: no context specified - event bindings won't be cleaned when the view is removed
  1573. Todos.on('sync', this.render);
  1574. // GOOD: context will allow automatic cleanup when the view is removed
  1575. this.listenTo(Todos, 'sync', this.render);
  1576. Static versus instance variables
  1577. --------------------------------
  1578. If 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.
  1579. Backbone way of declaring a static color variable:
  1580. .. code-block:: javascript
  1581. var MyView = Resthub.View.extend({
  1582. color: '#FF0000',
  1583. initialize: function(options) {
  1584. this.$root = options.root;
  1585. this.$root.html(this.$el);
  1586. }
  1587. });
  1588. return MyView;
  1589. Backbone way of declaring an instance color variable:
  1590. .. code-block:: javascript
  1591. var MyView = Resthub.View.extend({
  1592. initialize: function(options) {
  1593. this.$root = options.root;
  1594. this.$root.html(this.$el);
  1595. this.color = '#FF0000';
  1596. }
  1597. });
  1598. return MyView;
  1599. Use this.$() selector
  1600. ---------------------
  1601. this.$() 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.
  1602. Events
  1603. ------
  1604. Backbone default event list is available `here <http://backbonejs.org/#Events-catalog>`_.
  1605. Inheritance
  1606. -----------
  1607. As 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.
  1608. .. code-block:: javascript
  1609. // Define an example Kind class
  1610. var Kind = function() {
  1611. this.initialize && this.initialize.apply(this, arguments);
  1612. };
  1613. Kind.extend = Backbone.Model.extend;
  1614. // Create a Human class by extending Kind
  1615. var Human = Kind.extend({
  1616. toString: function() { console.log("hello: ", this); },
  1617. initialize: function (name) {
  1618. console.log("human constructor");
  1619. this.name = name
  1620. }
  1621. });
  1622. // Call parent constructor
  1623. var SomeOne = Human.extend({
  1624. initialize: function(name){
  1625. SomeOne.__super__.initialize.call(this, name);
  1626. }
  1627. });
  1628. // Create an instance of Human class
  1629. var Bob = new Human("Bob");
  1630. Bob.toString();
  1631. // Create an instance of SomeOne class
  1632. var Sam = new SomeOne("Sam");
  1633. Sam.toString();
  1634. // Static members
  1635. var Human = Kind.extend({
  1636. toString: function() { console.log("hello: ", this); },
  1637. initialize: function (name) {
  1638. console.log("human constructor");
  1639. this.name = name
  1640. }
  1641. },{ //Static
  1642. counter: 0,
  1643. getCounter: function() { return this.counter; }
  1644. });
  1645. Cache buster
  1646. ------------
  1647. In 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.
  1648. main.js:
  1649. .. code-block:: javascript
  1650. require.config({
  1651. paths: {
  1652. // ...
  1653. },
  1654. urlArgs: 'appversion=${buildNumber}''
  1655. });
  1656. main.js after filtering:
  1657. .. code-block:: javascript
  1658. require.config({
  1659. paths: {
  1660. // ...
  1661. },
  1662. urlArgs: 'appversion=738792920293847'
  1663. });
  1664. In 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.
  1665. .. _Require: http://requirejs.org
  1666. .. _Handlebars: http://handlebarsjs.com
  1667. .. _Backbone Validation: http://github.com/thedersen/backbone.validation
  1668. .. _Twitter Bootstrap: http://twitter.github.com/bootstrap/
  1669. .. _Backbone Datagrid: http://loicfrering.github.com/backbone.datagrid/
  1670. .. _Backbone Paginator: http://addyosmani.github.com/backbone.paginator/
  1671. .. _Backbone Query Parameters: http://github.com/jhudson8/backbone-query-parameters
  1672. .. _Async: http://github.com/caolan/async/
  1673. .. _Keymaster: http://github.com/madrobby/keymaster
  1674. .. _Backbone: http://backbonejs.org/
  1675. .. _Backbone Relational: https://github.com/PaulUithol/Backbone-relational
  1676. .. _Moment: http://momentjs.com/