Autosync
work with data, not with DOM
Download
autosync.jsStandard version: IE 9+, Firefox 4.0+, Opera 12.1+, Chrome 5.0+
autosync-compat.jsExtended version: IE 6+, Firefox 1.0+, Opera 8.0+
Templates

Templates are strings including data from the model, into which the data is substituted on DOM updates. There are two types of templates in Autosync: string and functional ones.

String templates

A string template is a string which contains values for interpolation. These values arre {$value}, {$value['field']}, {$value[expression]}, where expression is a computed JavaScript expression. Autosync will try to evaluate everything that is not included into single or double quotes.


<span class="email" asmodel="user.profile_data.email" astemplate="Email: {$value}"></span>
               

If you need to use a string {$value} as is, add a single backslash before $: {\$value}, {\$value['something']}. This backslash will not be displayed in the final output.

You can set the template either in the astemplate attribute, or straight inside the element between the opening and the closing HTML tag. In the second case Autosync will look for substrings like {$value[...][...]}, and only if it finds at least one the contents will be processed as a template. Otherwise (if the asmodel attribute is present) the contents will be replaced with model value on the update.

Functional templates

A functional template is any user function defined as the field of the model context that returns the required HTML element contents (innerHTML) as a string. It can take an arbitrary number of arguments, one of which can be a special argument $value, which will be replaced with the value from the model before the call. To create such template use the func: prefix:


<span class="email" asmodel="user.profile_data.email" astemplate="func:setEmail($value)"></span>
               

A functional template must be a field of the model context object, or it will not be found.

A functional template is called with the execution context of the model context (this is a reference to the parent object). This gives you the ability to access data of other models in the same context container. Sometimes this can be useful.

You can use functional templates inside of the string ones - either with the same model or with another one.

Conditional rendering

With asif attribute you can render part of content only when the given condition is fulfilled. You can use model fields or any other available JS variables in the condition.


<!-- Will render control links only inside current user's messages -->
<div class="links" asif="$value.author_id == user_id">
    <span class="edit">Edit</span><span class="remove">Remove</span>
</div>
               
Collections output

If you set up the aslist attribute on the element, and the model has an integer length property, the provided template will be applied to each element from 0 to length and the result will be concatenated into one string. You can also adjust the output range (including borders) using the colon:


<!-- Will output the e-mail of the first 3 users in the array -->
<div id="emails" class="list" asmodel="users" aslist="0:2">
    <div class="item" data-id="{$value['id']}">{$value['email']}</div>
</div>
               

On data array modification using push, pop, shift, unshift, and splice methods Autosync will perform selective insertions and deletions of HTML child elements rather than doing a full content re-render from scratch.

You can use the {$index} expression to output the index of the element inside the collection. You can output the string {$index} as normal text by adding a single backslash before $: {\$index}.

Placeholders

For models whose value is null or undefined, and for empty arrays output with aslist you can define the placeholder content with asplaceholder attribute. Its value is either HTML in string form or CSS-selector of HTML element whose inner HTML content will be used as placeholder HTML (in this case you should use the ($[ selector ])) format. Note that the CSS selector should return a single element, because all that Autosync will do is pass the content inside braces to document.querySelector function, which will return the first match.

If you pass the CSS selector, but none of the elements on the page is matched, the empty string will be used as the placeholder value.

The placeholder will be put inside the element in the template corresponding to the model that was currently empty, but will not replace it: this method allows to get a more correct display without serious styles modification.

If the element with asplaceholder attribute is a text input, the placeholder value will be used as its placeholder and its current value will be cleared.

Inner templates

One of the most powerful feature of Autosync templates is the ability to nest elements with their own templates into outer ones. If such nested element does not have an asmodel attribute, it will be looked for up the ancestors chain. It will be eventually found in the element whose template was started to render first if it is not found until then.

Here is the example of nesting:


<div id="task_list" asmodel="data.tasks" aslist>
   <div class="item task_item" asplaceholder="<center><em>Empty</em></center>" data-id="{$value['id']}">
      <div class="select select_user" data-param="user_id" data-value="{$value['user_id']}">
         <div class="user">
            <div class="avatar" astemplate="func:firstLetters($value['user'])">AA</div>
            <div class="name">{$value['user']}</div>
         </div>
         <select asmodel="data.users" aslist>
            <option value="{$value['id']}">{$value['name']}</option>
         </select>
         </div>
      <div class="title">{$value['title']}</div>
      <div class="description">{$value['description']}</div>
      /* ... */
   </div>
</div>
               

From the Autosync's viewpoint there are two nested templates in this markup code: one is functional and is using the same data model as its parent template, and the other one is a string one and is using is own data.users model (because it has asmodel attribute set). It is the asmodel attribute that tells Autosync that it is a nested template in this case and not part of the parent one.

Inner templates that render collections can be bound either to independent collection model, or to the property of the current element of outer collection. For example, a task can have a list of milestones that is unique to each task. In such case it is impossible to write the path to milestones list in absolute form, because it depends on the task index in the tasks array. To solve this issue Autosync allows relative model paths. A relative path starts with a dot symbol and is equal to the current rendering object path concateneted with the given string.


<div id="task_list" asmodel="data.tasks" aslist>
   <div class="item task_item" asplaceholder="<center><em>Empty</em></center>" data-id="{$value['id']}">
      /* ... */
      <div class="title">{$value['title']}</div>
      <div class="description">{$value['description']}</div>
      <ul class="milestones" asmodel=".milestones" aslist>
         <li>{$value['text']}</li>
      </ul>
      /* ... */
   </div>
</div>
               
Templates compilation

For maximum performance all templates regardless of their type are compiled only once. That means that you won't see any changes if you just change the astemplate attribute value in JS code. You will have to remove and add once more the corresponding model in order for the changes to take place.