Creating template
Storing
Template is a simple HTML page with all basic parts, like DOCUMENT, HEAD and BODY. You can define in template tags LINK to define some CSS and SCRIPT – to define some JS files or controller. All such resource will be attached with your template.
So, from organization point of view, you can create basic folder “templates” and storage there your templates with resources.
Basic definitions
To create template, you should know only several definitions:
- Hook. {{ name }}. Hook is a mark to paste some content at marked place.
- Model. {{ ::name }}. By this mark you bind some property or attribute of node with model. For example, you can bind innerHTML of some node and get access to it within something like this: model.myLable = ‘new HTML for this label’.
- Dom. {{ $name }}. For all nodes (which was marked within {{ $name }}) flex-patterns will create special wrapper to give you easy access to such functionality like: changing styles; attaching events, checking properties and etc.
- Event. {{ @handle }}. For any node you can define name of handle for any DOM event. Flex.Patterns will make an attempt to find defined handle (by name) in your controller.
The best way to explain something – show example. So, to see how it works, let’s create simple dialog for authorization. We will need several temples (simple HTML files):
- Popup. Template of popup window;
- Textbox. Template of text area: login and password;
- Button. Template of button: logic and cancel;
- Dialog layout. Template with layout for our authorization window.
Let’s postpone HEAD and other places of HTML and paste only BODY tag to save place and your time.
<div data-type="Pattern.Login">
<p>Login</p>
{{ login }}
<p>Password</p>
{{ password }}
<div data-type="Pattern.Controls">{{ controls }}</div>
</div>
<div data-style="Popup" id="{ {id }}">
<div data-style="Popup.Container">
<div data-style="Popup.Title">
<p data-style="Popup.Title">{{ title }}</p>
</div>
<div data-style="Popup.Content">{{ content }}</div>
<div data-style="Popup.Bottom">
<p data-style="Popup.Bottom">{{ bottom }}</p>
</div>
</div>
</div>
<p>{{ ::value }}</p>
<div data-type="TextInput.Wrapper">
<div data-type="TextInput.Container">
<input data-type="TextInput" type="{{ type }}" value="{{ ::value }}" name="TestInput"/>
</div>
</div>
<a data-type="Buttons.Flat" id="{{ id }}">{{ title }}</a>
Attaching template
You can attach template by two ways:
- via JavaScript;
- via HTML
Attaching via JavaScript
Here is an example. To attach template via JavaScript you should to do this:
var id = flex.unique();
_patterns.get({
url : '/patterns/popup/pattern.html',
node : document.body,
hooks : {
id : id,
title : 'Test dialog window',
content : _patterns.get({
url : '/patterns/patterns/login/pattern.html',
hooks : {
login : _patterns.get({
url : '/patterns/controls/textinput/pattern.html',
hooks : {
type: 'text',
}
}),
password: _patterns.get({
url : '/patterns/controls/textinput/pattern.html',
hooks : {
type: 'password',
}
}),
controls: _patterns.get({
url : '/patterns/buttons/flat/pattern.html',
hooks : [{ title: 'login', id: 'login_button' }, { title: 'cancel', id: 'cancel_button' }]
}),
}
})
},
onReady: function (results) {
var instance = this,
dom = results.dom,
model = results.model;
});
},
}).render();
Parameters of method get (_patterns.get(parameters))
Name | Obligatory | Type | Description |
---|---|---|---|
url | yes | string | URL to HTML file of your template |
node | no | node || string | If you define this property your template will be mounted into defined node. You can define this property as reference to some node or as selector. For example, you can define it as: document.body (reference to node) or as ‘body’ (selector). |
replace | no | bool | Works only with property [node]. If it’s in true, your template will be mounted instead defined node. |
before | no | node || string | If you define this property your template will be mounted before defined node. You can define this property as reference to some node or as selector. |
after | no | node || string | If you define this property your template will be mounted after defined node. You can define this property as reference to some node or as selector. |
id | no | string | ID of your template. |
hooks | no | object || array | Collections of hooks. |
conditions | no | object | Collections of conditions. |
controller | no | object | You can define controller during calling template. It can be useful if you want to overwrite controller. |
onReady | no | function | This function will be called if rendering of your template was successful. |
resources | no | any | Property resources is used for exchanging of data between your application and controllers. |
remove_missing_hooks | no | bool | If flex-patterns cannot file hook, mark of hook ({{ name }} will be removed from markup. You can prevent such behavior by setting this property to false. |
Method get is initializing pattern, but not render it. This method will return caller instance and you can call method render to render pattern according parameters, which was given to method get: _patterns.get(params).render().
Pay your attention, flex-patterns works asynchrony, because it gets sources of your template (HTML, JS and CSS) within HTTP(s) requests. That’s why method render will not return any results of operation.
So, method get can be used to define nested patterns.
But we have to say, such call (as in previous example) doesn’t look very nice – there are too many definitions of pattern’s urls. So, you can easy solve such issue by mapping your template. Everything what you need – just create JS file, place it with root-template (it’s “/patterns/popup/pattern.html” in our example) and define content of new file, like next:
Do not forget attach map.js file to your root template.
_map({
content: {
url: '/patterns/login/pattern.html',
hooks: {
login: {
url: '/patterns/controls/textinput/pattern.html'
},
password: {
url: '/patterns/controls/textinput/pattern.html'
},
controls: {
url: '/patterns/buttons/flat/pattern.html'
},
}
}
});
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Flex.Template</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="pattern.css" />
<script type="text/javascript" src="map.js"></script>
</head>
<body>
<div data-style="Popup" id="{{id}}">
<div data-style="Popup.Container">
<div data-style="Popup.Title">
<p data-style="Popup.Title">{{title}}</p>
</div>
<div data-style="Popup.Content">{{content}}</div>
<div data-style="Popup.Bottom">
<p data-style="Popup.Bottom">{{bottom}}</p>
</div>
</div>
</div>
</body>
</html>
_patterns.get({
url: '/patterns/popup/pattern.html',
node: document.body,
hooks: {
id : id,
title : 'Test dialog window',
content : {
login : { type: 'text' },
password: { type: 'password' },
controls: [{ title: 'login', id: 'login_button' }, { title: 'cancel', id: 'cancel_button' }],
}
},
onReady: function (results) {
var instance = this;
}
}).render();
Now as you can see our caller of pattern looks much simpler.
Method _map (in scope of pattern) allows define paths to nested patterns.
Attaching via HTML
Here is an example. To attach template within HTML, you should define in your page next HTML fragment:
<pattern src="/patterns/popup/pattern.html" data-hooks="id, title, content, login, password, type, controls">
<id>0</id>
<title>Test dialog window</title>
<content src="/patterns/patterns/login/pattern.html">
<login src="/patterns/controls/textinput/pattern.html">
<type>text</type>
</login>
<password src="/patterns/controls/textinput/pattern.html">
<type>password</type>
</password>
<controls src="/patterns/buttons/flat/pattern.html">
<id>login_button</id><title>login</title>
<id>cancel_button</id><title>cancel</title>
</controls>
</content>
</pattern>
<pattern src="/patterns/popup/pattern.html" data-hooks="id, title, content, login, password, type, controls">
<id>0</id>
<title>Test dialog window</title>
<content>
<login>
<type>text</type>
</login>
<password">
<type>password</type>
</password>
<controls>
<id>login_button</id><title>login</title>
<id>cancel_button</id><title>cancel</title>
</controls>
</content>
</pattern>
In attribute data-hooks you have to define names of all hooks, which will be used in template. You can include into such list nested patterns too. But if you have several instances of your template, it isn’t very comfortable to define same things each time. So, you can use tag LINK to define basic parameters of pattern.
<link rel="pattern" name="login" src="/patterns/popup/pattern.html" data-hooks="id, title, content, login, password, type, controls" />
Now you can render your template a bit simpler. Pay your attention, now you use tag LOGIN (as defined in attribute NAME of LINK) instead tag PATTERN.
<login>
<id>0</id>
<title>Test dialog window</title>
<content src="/patterns/patterns/login/pattern.html">
<login src="/patterns/controls/textinput/pattern.html">
<type>text</type>
</login>
<password src="/patterns/controls/textinput/pattern.html">
<type>password</type>
</password>
<controls src="/patterns/buttons/flat/pattern.html">
<id>login_button</id><title>login</title>
<id>cancel_button</id><title>cancel</title>
</controls>
</content>
</login>
<login>
<id>0</id>
<title>Test dialog window</title>
<content>
<login>
<type>text</type>
</login>
<password">
<type>password</type>
</password>
<controls>
<id>login_button</id><title>login</title>
<id>cancel_button</id><title>cancel</title>
</controls>
</content>
</login>
If you do not want create addition JS-file with map to go away from definition of urls of nested templates, you can do it within LINK tag also:
<link rel="hook" name="login.content" src="patterns/patterns/login/pattern.html"/>
<link rel="hook" name="login.content.login" src="/patterns/controls/textinput/pattern.html"/>
<link rel="hook" name="login.content.password" src="/patterns/controls/textinput/pattern.html"/>
<link rel="hook" name="login.content.controls" src="/patterns/buttons/flat/pattern.html"/>
In this case you can mount your template without map, like here:
<login>
<id>0</id>
<title>Test dialog window</title>
<content>
<login>
<type>text</type>
</login>
<password">
<type>password</type>
</password>
<controls>
<id>login_button</id><title>login</title>
<id>cancel_button</id><title>cancel</title>
</controls>
</content>
</login>
Attributes of tag PATTERN
Name | Obligatory | Type | Description |
---|---|---|---|
src | yes | string | URL to HTML file of your template |
onReady | no | string | Name of handle, which will be called after pattern is initialized. |
id | no | string | ID of your template. |
Attributes of tag LINK
Name | Obligatory | Type | Description |
---|---|---|---|
rel | yes | string | Can be “pattern” or “hook”. If rel = “pattern” – link is definition of parameters of template; if rel = “hook” – link is definition of nested template (mapping). |
name | yes | string | Case rel = "pattern”: name of tag, which will be used for this pattern in layout. Case rel = “hook”: path (map) to nested pattern. For example, if parent template is “tabs” (which has nested template – “tab”), path to nested template will be name = “tabs.tab” |
data-hooks | no | string | Used only in cases rel = “pattern” (definition of root-template). It’s list of all used hooks inside template (include nested). |
If your template will be in layout during page are loading, in this case you should not call any JavaScript code – flex-patterns automatically find template, build it and mount instead tag PATTERN.
But if you will add your template into layout after page was loaded, or if you will define attribute onReady, in this case your template will not be rendered automatically to prevent situation, when callback functions aren’t ready (defined), but flex-pattern is ready to render. So, to render template from HTML with defined callbacks, you should do this (after your callback-functions will be ready):
_patterns.layout();
Multiply values
To repeat some template within different data several times you should define value of hook as an array of values. Let’s see how it works on an example of table. We will need two templates.
<table data-type="Demo.Table">
<tr>
<th>{{ titles.column_0 }}</th>
<th>{{ titles.column_1 }}</th>
<th>{{ titles.column_2 }}</th>
<th>{{ titles.column_3 }}</th>
</tr>
{{ rows }}
</table>
<tr>
<td>{{ column_0 }}</td>
<td>{{ column_1 }}</td>
<td>{{ column_2 }}</td>
<td>{{ column_3 }}</td>
</tr>
Let’s render our table. Here is an example of this template – link.
var data_source = [];
for (var i = 0; i < 100; i += 1) {
data_source.push({
column_0: (Math.random() * 1000).toFixed(4),
column_1: (Math.random() * 1000).toFixed(4),
column_2: (Math.random() * 1000).toFixed(4),
column_3: (Math.random() * 1000).toFixed(4),
});
}
_patterns.get({
url: '/patterns/table/container/pattern.html',
node: document.body,
hooks: {
titles: {
column_0: 'Column #0',
column_1: 'Column #1',
column_2: 'Column #2',
column_3: 'Column #3',
},
rows: _patterns.get({
url: '/patterns/table/row/pattern.html',
hooks: data_source,
})
}
}).render();
Pay your attention, that we can define name of hook as a chain of properties – titles.column_x. It allows us to use an understandable structure of a hook’s object and group properties by their meaning.
But also you have more simple way. You can create only one template of table (with row template included). In this case you have to mark repeated element with next mark: {{ [name_of_propery] }}. Let’s see with example.
var data_source = [];
for (var i = 0; i < 100; i += 1) {
data_source.push({
column_0: (Math.random() * 1000).toFixed(4),
column_1: (Math.random() * 1000).toFixed(4),
column_2: (Math.random() * 1000).toFixed(4),
column_3: (Math.random() * 1000).toFixed(4),
});
}
_patterns.get({
url : '/patterns/table/single/pattern.html',
node : document.body,
hooks : {
titles: {
column_0: 'Column #0',
column_1: 'Column #1',
column_2: 'Column #2',
column_3: 'Column #3',
},
rows: data_source
}
}).render();
<table data-type="Demo.Table">
<tr>
<th>{{ titles.column_0 }}</th>
<th>{{ titles.column_1 }}</th>
<th>{{ titles.column_2 }}</th>
<th>{{ titles.column_3 }}</th>
</tr>
<tr {{ [rows] }}>
<td>{{ column_0 }}</td>
<td>{{ column_1 }}</td>
<td>{{ column_2 }}</td>
<td>{{ column_3 }}</td>
</tr>
</table>
Here is example of such way. Or you can take a look on a simple drop down list example.
Also, you have seen, that in previous example (authorization window) we used multiple values for buttons, but pay your attention, in case of JavaScript attaching to define multiply value of hook we used an array to define several instances of template:
controls: _patterns.get({
url : '/patterns/buttons/flat/pattern.html',
hooks : [{ title: 'login', id: 'login_button' }, { title: 'cancel', id: 'cancel_button' }]
})
But in HTML to do same we have to define hooks just several times:
<controls src="/patterns/buttons/flat/pattern.html">
<id>login_button</id><title>login</title>
<id>cancel_button</id><title>cancel</title>
</controls>
Here you can discover an example of table, mounted into layout.
External data sources
In addition, you can use as hooks values source some URL. Sure, by defined URL should be returned some JSON object. This JSON object will be used as hooks values. Let’s see next example.
_patterns.get({
url : '/patterns/table/container/pattern.html',
node : document.body,
hooks : {
titles : {
column_0: 'Column #0',
column_1: 'Column #1',
column_2: 'Column #2',
column_3: 'Column #3',
},
rows :_patterns.get({
url : '/patterns/table/row_con/pattern.html',
hooks : _patterns.url('/examples/json/table.json'),
})
},
}).render();
To define external source of data you should use method url of _patterns: _patterns.url(url, parameters). Let’s see on parameters closer.
Name | Default | Type | Description |
---|---|---|---|
url | STRING | url, where data can be received | |
parameters | OBJECT | Parameters of data request | |
parameters.parser(data) | null | FUNCTION | You can define your parser which will be applied to received data |
parameters.method | GET | STRING | Method of request: POST or GET |
parameters.parameters | null | STRING || ARRAY | Parameters of your request. In GET case from ARRAY will be converted to STRING automatically |
parameters.callbacks.success(response, request) | null | FUNCTION | Handled in success |
parameters.callbacks.fail(response, request) | null | FUNCTION | Handled in fail |
parameters.callbacks.before(request) | null | FUNCTION | Handled before do request |
parameters.callbacks.reaction(response, request) | null | FUNCTION | Handled on each reaction of request |
parameters.callbacks.timeout(response, request) | null | FUNCTION | Handled in timeout case |
parameters.callbacks.headers(response, request) | null | FUNCTION | Handled when HEADERS are gotten |
parameters.settings | OBJECT | Addition configuration of request | |
parameters.settings.id | some string | STRING | ID of current request |
parameters.settings.timeout | 20000 | NUMBER | Timeout in ms |
parameters.settings.doNotChangeHeaders | false | BOOLEAN | In true headers will not be validate and parsed |
parameters.settings.doNotChangeParameters | false | BOOLEAN | In true parameters of request (defined as object or array) will not be validate and parsed. For POST requests |
parameters.settings.doNotEncodeParametersAsURL | false | BOOLEAN | In true parameters of request (defined as string) will not be validate and parsed. For GET requests |
parameters.settings.headers | null | STRING || ARRAY | Collection of headers |
As URL you can define some specific hook (like in previous example) or all hooks, like here.
_patterns.get({
url : '/components/table.simple/component.html',
node : document.body,
hooks : _patterns.url('/examples/json/table.all.json', {
parser: function (res) {
res.titles.column_0 = 'Column 0 (UPD)';
return res;
}
}),
}).render();