### Xone MVC Pattern
#### MVC: Assign Route
*app/js/route/record.js*
```javascript
ROUTE['v1/record/:id'] = {
// only the fields 'to' and 'view' are required, the other ones have values by default
to: 'record_show',
view: 'view/record/show',
type: 'GET',
cache: true,
params: {
'id': function(){
// ...
return id;
}
}
};
```
#### MVC: Assign Controller
*app/js/controller/record.js*
```javascript
CONTROLLER['record_show'] = function(record){
// render is a controller built-in (connects to the view controller)
// this will render the data into the DOM
// if no template was given as 2nd parameter, the default template view is used instead (defined in routes)
// in this example the passed template is same as the default template from this route
CONTROLLER.render({
target: '#dom-element-id',
route: 'view/record/show',
data: record
});
// or use the short form:
CONTROLLER.render('#dom-element-id', 'view/record/show', record);
};
```
#### MVC: Assign View
*app/view/user/show.shtml*
```html
{{ user.name }}
{{ user.email }}
```
*app/view/media/show.shtml*
```html
{{ title }}
```
*app/view/record/show.shtml*
```html
{{ title }}
{{ date }}
{{ time }}
{{ if(user && user.media) }}
{{ include(view/user/show) }}
{{ endif }}
{{ for(user.media) }}
{{ include(view/media/show) }}
{{ endfor }}
```
Since the Template Compiler is working recursively, you can embed multiple dimensions in the same way of this dead simple templating mechanism.
#### MVC: Implement Model (OOP)
*app/js/model/record.js*
```javascript
// Model Constructor:
function Record(data){
/** @export */
this.id = data.id;
/** @export */
this.title = data.title;
/** @export */
this.date = data.date;
/** @export */
this.user = data.user;
}
// Model Functions:
MODEL.Record = {
loop_through_all_records: function(records){
for(var i = 0; i < records.length; i++){
console.log('Record found: ', /** @type {Record} */ (records[i]));
}
}
};
```
#### MVC: Final Usage
```javascript
CONTROLLER.request('v1/records/:id', { id: 1 }, function(records){
// data is in json format by default
// do some extra work with data or use as callback
APP.MODEL.Record.loop_through_all_records(records);
});
```
#### Request Routes VS. Event Routes
All routing requests follows an built-in chain: the controller perform the request through the route specification and calls the view with the served data. Connecting an event handler to a specific request route is very simple, but keep in mind you also connecting to the controller chain.
Sometimes you don't need the default controller chain, e.g. if you bind a scroll listener. In this case you can define an event handler in *APP.HANDLER* to keep the controller code clean.
A request route is meant to be connected to the controller (aka the "request controller"). An event route is able to connects to either: to a request route with its default chain, or to its corresponding event handler (within you are also able to initiate controller chain).
#### Route Events through the Controller
NOTE: *Event Delegation* is used for multiple events of same functionality, e.g. list of nodes.
Simple Event Binding (used for unique events / elements, e.g. a node by id):
```javascript
// Register/routes a click event to a dom element
EVENT['dom-element-id'] = {
on: 'click',
to: 'v1/records/:id', // routes to the request controller and to its chain
params: function(){ // inline defined function which creates the payload dynamically
id: this.dataset['user_id']
}
};
```
Alternatively you can assign a payload externally in *APP.PAYLOAD* to keep the event router clean.
Event Delegation by Class:
```javascript
EVENT['dom-element-id'] = {
on: 'click',
if: '.row', // the leading dot indicates a classname
// instead of using a specific controller like above you can assign a custom handler function
// the default controller chain will not executed
// not extra handler definition is required
do: function(event){
alert('Clicked node with id: ', this.id);
}
};
```
Delegate Multiple Event on the same Target:
```javascript
EVENT['dom-element-id'] = [{
on: 'click',
if: '.row',
do: 'my_callback_row'
},{
on: 'click',
if: '.column',
do: 'my_callback_column'
}];
```
Event Delegation by Tag:
```javascript
EVENT['dom-element-id'] = {
on: 'click',
if: 'td', // no leading dot indicates a tagname
to: 'v1/records/:id'
};
```
NOTE: when you are using 'to'-shortcut to a route which requires dynamic params and no params field was set inline, you have to assign a payload respectively on *APP.PAYLOAD*.
Event Delegation by Tag with Classname:
```javascript
EVENT['dom-element-id'] = {
on: 'click',
if: 'td.classname', // indicates a tag with a specific classname
do: 'record_message' // assign an event route to an event handler
};
```
NOTE: In handy to use this 'do'-shortcut you need to register a event handler explained below.
Control Event Delegation:
```javascript
EVENT['dom-element-id'] = {
on: 'scroll',
preventDefault: true,
stopBubble: true,
returnValue: false
};
```
#### Register Handlers for Non-Request-Controller Events (optionally)
```javascript
HANDLER['record_message'] = function(event){
console.log("You clicked on element node with ID: ", this.id);
};
```
#### Register Payloads for Event Routes (optionally)
A payload definitions are used to create request parameters from dynamic data. Once a payload was defined it is automatically connected to the event router. If you provide both: a params field in the event routings (inline payload) as well as a payload function, the params field in the event routing was skipped (it is "shadowed").
```javascript
PAYLOAD['record_message'] = function() {
return {
id: this.dataset['user_id']
};
};
```
Alternatively you can assign payloads to the even router inline by assigning the field 'params'.
#### Register Data Mappings (optionally)
You can assign mappings to each model which transforms data into different representations:
```javascript
MAPPER['Record'] = {
// mapping definition from model data to view data
// (client model --> view model
mapToView: {
'date': function(value){
return value ? value.substring(0, 10) : value;
},
'image': function(value){
return value || 'img/placeholder.jpg';
}
},
// mapping definition from model data to server payload
// client model --> server model
mapToPayload: {
'username': 'name',
'date': 'today'
},
// mapping definition from server payload to model
// server model --> client model
mapToModel: {
'name': 'username',
'today': 'date'
}
};
```
Each mapping definition is bind automatically to the corresponding model and/or controller.
#### Create Specs (Jasmine)
*app/test/main_spec.js*
```javascript
describe("Validate Record Methods", function() {
it("Check if record has method 'loop_through_all_records'", function() {
expect(APP.MODEL.Record.loop_through_all_records).toBeDefined();
});
});
```
#### Compile Views (Pre-Build)
```bash
>node compile # compiles show.html to show.json
```
#### Build Dynamic Patterns and Templates
```javascript
document.getElementById('my-div').appendChild(
// template
CORE.buildPattern({
tag: "div",
attr: {
"id": "test_id"
},
child: [{
tag: "span",
attr: {
"class": [
"test_class1"
],
"style": {
"display": "block"
}
},
data: {
"title": user.name
}
},{
tag: "div",
attr: {
"id": "user_id"
},
child: [{
tag: "span",
attr: {
"class": [
"test_class1"
],
"style": {
"display": "block"
}
},
data: {
"username": user.name
}
}]
}]
})
);
```
Another Usage:
```javascript
CORE.buildPattern(document.getElementById('my-div'), {
// template
});
```
#### Build Dynamic Templates By Data
```javascript
CORE.buildData(
// target dom:
document.getElementById('my-div'),
// template:
function(data){ return {
tag: "img",
attr: {
"src": data.src,
"alt": data.title
}
}},
// data:
[{
title: 'Image A',
src: 'image_a.png'
},{
title: 'Image B',
src: 'image_b.png'
},{
title: 'Image C',
src: 'image_c.png'
}]
);
```
### Xone Dev Tools
#### Build Project (All-In-One):
```bash
>app build
```
Update file contents only (skips compiler):
```bash
>app refresh
```
Compile views (pre-build):
```bash
>app compile
```
Extract source packackes (during first installation):
```bash
>app install
```
#### Create documentation (JSDoc):
```bash
>app docs
```
#### Run test suites:
```bash
>app specs
```