D a m o . j s User Guide
Howto
Basics
The idea of Damo is : the properties of a json data model shall have a direct conterpart into the HTML template, both related by a two-way data binding. Consequently all the data used inside the application shall be defined as a part of the application json datamodel.
For instance the following datamodel
//JSON applicative data model { "books" : [ { "buyers" : [ { "name" : "Albert" }, ] } ] }
will instanciate the object damo.dm, and will relate to the DOM tag :
<tagName damo-id="books[0].buyer[0].name">...</tagName>In this last tag, the attribute damo-id explains to damo that the value of the tag shall be related to the value of the datamodel element books[0].buyer[0].name, in a two-way data binding fashion. A change of the value of books[0].buyer[0].name on the DOM will automatically change its value in the datamodel of the javascript code.
For those who are familiar with Angular.js, and its way to tag the DOM, the way Damo does things is pretty similar, but simpler. For instance the tag attributes will be damo-if, damo-filter, damo-loop, damo-trigger, .... Damo is designed to be much simpler and open than Angular, the integration of external libraries being very easy.
Damo is thought for faster and simpler development, this is its aim.
Get started
Here is the simplest code that displays "Hello World" :
<!DOCTYPE html> <html> <head> <title>Damo Hello World</title> <meta charset="utf-8"> <script src="http://code.jquery.com/jquery-1.10.2.js"></script> <script src="damo.min.js"></script> <script> damo.dm.myText = "Hello World !"; </script> </head> <body> <div damo-id="myText"></div> </body> </html>
However this example does not display all the possibilities of Damo, so we propose to explain Damo by using the elementary application damoSeed (that you can download here). This is a good idea to start any Damo development from this seed.
Here is the content of the seed :
The damo object
damo is an object with different properties :
var damo = { dm : {}, fn : {}, routing : {}, directive : {}, filter : {}, method : {}, config : {} };
- damo.dm is intended to load the JSON datamodel of the application. It is automatically done at start if the file data/datamodel.json is present in the application's directory (see damoSeed).
- damo.fn is intended to connect any user function that the application needs to share among pages.
- damo.routing is an object describing the routing of the application, i.e. the description of the url, template, and more, for each page. It is automatically loaded at start if the file js/routing.js is present in the application's directory (see damoSeed).
- damo.directive is an object describing the different directives that are used by the application. For instance a tag <myVideo></myVideo> in the DOM could be replaced by an embeded video inside a particular template. This notion is very similar to the same one in Angular.It is automatically loaded at start if the file js/directives.js is present in the application's directory (see damoSeed).
- damo.filter is an object describing the different filters that are used by the application. For instance a tag <span damo-filter="boldify">Hello</span> will boldify its content, providing the filter boldify() has been described in the file js/filters.js. It is automatically loaded at start if the file js/directives.js is present in the application's directory (see damoSeed).
- damo.method is a property intended for internal purpose, and you should not modify it, unless you want to modify the behavior of the whole damo.
- damo.config gives some facilities to tune the performance and aspect of the app, as animation times.
The datamodel
The datamodel is a JSON file located in data/datamodel.json. Here is a typical datamodel :
{ "user" : { "firstName" : "John", "lastName" : "Doe", "email" : "john@doe.com", "tel" : "0123456789", "address" : "36 Baker Street, NY, USA", "avis" : "", "shoppingCart" : { "list" : [ { "name" : "Bread", "price" : 1.49 }, { "name" : "Fruits", "price" : 3.30 }, { "name" : "Salad", "price" : 0.95 }, { "name" : "Rice", "price" : 2.18 } ], "total" : 0.0 } } }
A datamodel is a JSON tree that shall describe the entire hierachy of the informations used by the application. There is only one datamodel, and every data must have a precise place in the json tree. At start the object damo.dm is loaded with the data found in the file data/datamodel.json. So with the above example, the variable damo.dm.user.shoppingCart[2].name for instance has the value "salad" and is available in all the controllers of the application.
Routing
The routing is described in the file js/routing.js. Here is a typical routing file :
damo.routing = { home : { url : 'home', template : 'index.html', controller : 'controller_home', needLogin : false }, page1 : { url : 'page1', template : 'templates/page1.html', controller : 'controller_page1', needLogin : false }, page2 : { url : 'page2', template : 'templates/page2.html', controller : 'controller_page2', needLogin : false } }
A routing item has 4 properties :
- url : this is the url to which the application will refer to display the page
- template : the template used to build the page
- controller : the javascript code used by the page, usually described in the file js/controllers.js, or in any other file.
- needLogin : true/false. If true and the variable damo.dm.logged is set to false, the user will be redirected on the home page. If true and the variable damo.dm.logged is set to true, the user will be allowed to display the page. If false, no login check is performed.
Page controllers
The controller of each page is usually described in the file js/controller.js, but it could be any other file. Here is a typical file :
function controller_home() { console.log('in controller_home'); damo.dm.captainAge = calculateAgeOfCaptain(); } function controller_page1() { console.log('in controller_page1'); } function controller_page2() { console.log('in controller_page2'); } function controller_shopCart() { console.log('in controller_shopCart'); var total = 0.0; var arr = damo.dm.user.shoppingCart.list; for(var i=0;i<arr.length;i++) { total += arr[i].price; } damo.dm.user.shoppingCart.total=total+' €'; }
All damo is accessible in all the the controllers, so damo.dm, damo.fn, damo.filters, damo.routing, .... If a value is changed inside damo.dm, it is automatically changed for the whole application, so all the controllers.
Damo config
here is the definition of the config property of damo :
config : { /** * @property {integer} securityBreak * @type integer * @extends damo.config * @description * Damo uses some "while true" loops. This property ensures that * these loops can not get out of control. All these kind of * loops will break after securityBreak iteretions, if not * broken before. * */ securityBreak : 10, /** * @property {integer} switchPageDelay * @type milliseconds * @extends damo.config * @description * This parameter set the delay that damo will apply to animate * a page change. * */ switchPageDelay : 300, /** * @property {integer} showHideDelay * @type milliseconds * @extends damo.config * @description * This parameter set the delay that damo will apply to animate * the show/hide of an HTML element. * */ showHideDelay : 200 }
damo.config.switchPageDelay and damo.config.showHideDelay can be setup to animate more or less smoothly the application behavior.
Two-way data binding
The elements of the DOM are related to the datamodel damo.dm by the tag attribute damo-id. For instance the following input will display the value of damo.dm.user.firstName :
<input type="text" damo-id="user.firstName">
If the value of this input is changed by the user, it is automatically changed also in the datamodel damo.dm.
However if a value of the datamodel is changed inside a javascript controller, the coder shall use damoRefresh(); to propagate the change to the DOM.
Display the data model
The tags of the html template are connected to the applicative data model, and the variables of the scope, by their attribute damo-id. All tags having this attribute will be watch for the two-way data binding, others will be ignored.
<tagName damo-id="dataModelPropertyName">
In these expressions dataModelPropertyName is of type : books[4].buyer[6].price and is the counterpart of the property damo.dm.books[4].buyer[6].price of the datamodel.
- If the tag is an input, a textarea or a select, its value is set to the corresponding datamodel element.
- If the tag is either a div or a span, its inner HTML is replaced by the value of the corresponding datamodel element.
- the option tag can not include any html tag, but just text. So we can not use something like this :
//bad way of settig up an option <option value="books[4].buyer[6].name"><span damo-id="books[4].buyer[6].name"></span></option>
In this case we use the {{...}} pattern like this :
//good way of settig up an option <option value="{{books[4].buyer[6].name}}">{{books[4].buyer[6].name}}</option>
Conditions
The display of an HTML element might be conditional, depending on a scope variable, function or a data model variable. The syntax to use is the following :
<tagName damo-if="condition">...</tagName>
The evaluation is made on the truth of the condition. This last may be the result of a function, an expression using ! == != < <= > >= ==, or the simple presence of a datamodel variable damo.dm.myVariable. Some examples :
//corresponding to what the function damo.fn.myFunction() returns <tagName damo-if="myFunction()">...</tagName> //corresponding to $scp.myVar <tagName damo-if="myVar">...</tagName> <tagName damo-if="myVar >= 26">...</tagName> <tagName damo-if="myVar != myOtherVar">...</tagName> //corresponding to $scp.$dm.books[4].buyer[6].name <tagName damo-if="books[4].buyer[6].name.length>20">...</tagName>
Loop
You might have lists to display, and furthermore lists with a changing length along the application, and that could be fastidious to code. Damo provides a tag attribute that will handle this kind of problem. Here is an example :
<ul> <li damo-loop="book in books"><span damo-id="book.name"></span></li> </ul>
In this example the span tag will be fed with damo.dm.books[i].name.
Any sort of html tag can be repeated this way.
Note that the string $index in a loop will be replaced by the current index of the loop.
Trigger
This is an equivalent of the click() function of Jquery, except that only the name of the function is needed (no parenthesis). Here is an example :
<div damo-trigger="myTriggerFunction"></div>
This syntax will trigger the function when the html tag is clicked. The function must be a property of damo.fn, i.e. damo.fn.myTriggerFunction = function() {...}
Filters
The values of the data model, and the variables of the scope, can be first filtered before being displayed. This is made this ways :
<tagName id="dataModelPropertyName" damo-filter="filterName">
However this is not possible inside an option tag, which requires only text as content, therefore in this unique case the filter must be specified like this :
<option value="whatever">{{dataModelPropertyName | filterName}}</option>
In these expressions filterName is the name of the function that will be used to filter the value, i.e. damo.filter.filterName().
The filters can only be a method of the object damo.filter, it is prferable to described it in the file js/filters.js. Here is an example of the content of such a file :
damo.filter = { boldify : function(str) { return '<b>'+str+'</b>'; }, setPriceInEuro : function(price) { return parseFloat(price).toFixed(2)+' €'; } }
Directives
A directive is a non standard html tag that stands for an entire template. The file js/directives.js contains the directive definitions. Here is an example of this file :
damo.directive = { shoppingList : { templateUrl : 'templates/shoppingList.html', controller : 'controller_shopCart' }, carousel: { templateUrl : 'templates/carousel.html', } }
A directive is described by a template and optionnaly by a controller that controls the template's behavior. In the above example, the HTML tag <shoppingList></shoppingList> found in the DOM will be replaced by the content of the file templates/shoppingList.html.
Language
Damo comes with a language facility. The choosen language is define from the browser's language.A directive is a non standard html tag that stands for an entire template. The file js/directives.js contains the directive definitions. Here is an example of this file :
damo.directive = { shoppingList : { templateUrl : 'templates/shoppingList.html', controller : 'controller_shopCart' }, carousel: { templateUrl : 'templates/carousel.html', } }
A directive is described by a template and optionnaly by a controller that controls the template's behavior. In the above example, the HTML tag <shoppingList></shoppingList> found in the DOM will be replaced by the content of the file templates/shoppingList.html.
Syntax
HTML template
Word | Pattern | Usage |
---|---|---|
<tagName damo-key="value"> ... </tagName>or for option tags : <option value="{{dataModelId}}"> {{dataModelId}} </option> |
This is the reference format for all the Damo's Html tags. The words in italic have to be replaced by the significant words. | |
page |
<div damo-page="page"> ... </div> |
This tag is made to enclose a page of the application. The page is described in the file js/routing.html as damo.routing.page |
id |
<tagName damo-id="varId"> ... </tagName> |
Each tag having the attribute damo-id will be watch for the two-way data binding. |
filter |
<tagName damo-id="varId" damo-filter="filterName"> ... </tagName>or for option tags : <option value="varId"> {{varId | filterName}} </option> |
This pattern applies a filter to the concerned variable having the data model id varId, e.g. shop.books[2].promo.price. |
loop |
<tagName damo-loop="item in arrayName"> ... </tagName> |
This pattern calls a loop based on the content of the array which name is arrayName. The tagName can be of varous type : div, td, tr, option, li, or anything else. |
seed |
<directiveName damo-seed="seedName"> ... </directiveName> |
This pattern indicates which word will be considered as the root data of the directive (see Directives). |
trigger |
<tagName damo-trigger="functionName"> ... </tagName> |
This pattern calls the function which name is functionName, when the concerned tagName is clicked. It is preferable to setup these functions inside the object damo.fn, i.e. damo.fn.functionName = function() {...} |
lang |
<tagName damo-lang="expression"> ... </tagName> |
This pattern will fill the inner HTML of the tag with the value of the object damo.dm.lang.expression, defined in the file lang/browserslang.js. |