Main goal of the Plus is to provide declarative data binding facilities - data (model) can be bound with its HTML representation in various ways.
Any DOM element-container can be linked with some namespace (object) that contains data (a.k.a. model) to be viewed inside the container. Such a link is established by special model attribute:
<body model="MyDataNamespace"> ... </body>
And if you have in your script this:
namespace MyDataNamespace { ... }
then content of the body element (in this case) can be linked (bound) with the data in MyDataNamespace.
Topmost element that has such model attribute is known as a data model view - elements bound with the model data namespace / object reflect state of the data.
Element's value can be bound with some variable in the model. +plus framework uses attribute name that shall contain name or expression to bind with.
<body model="Data"> <p>Whom to greet: <input|text(greeting) /></p> <p>Greeting: Hello <output(greeting) />!</p> </body>
Two elements above are bound with the same variable Data.greeting
in the model. Changes in the <input> element are reflected in the <output> element.
Normally data variable is bound with the value
property of linked element directly. But if the element has setBoundValue(newVal)
defined (on it or its behavior class) then the function will be called to set the value.
Sciter also suppports attribute value binding with variables and object properties. To define bound attribute simply add '@' character in front of its name and provide variable name or expression as a value of the attribute:
<li><picture @src="customer.image" /> <output(customer.name)/></li>
Here src
attribute will be bound with customer.image
variable and the ouput will contain value of customer.name
variable.
There are bound attributes that are mapped to state flags:
@enabled="expression"
- boolean expression is bound as element.state.disabled = !expression;@disabled="expression"
- boolean expression is bound to element.state.disabled = expression;@expanded="expression"
- boolean expression is bound as element.state.expanded = expression;@collapsed="expression"
- boolean expression is bound as element.state.collapsed = expression;each
repeatableAny container may have attribute each="item in items" defined. In this case initial content of the container is used as a template markup that will be repeated for each element in items collection. Format of the each attribute:
[{index},]{item} in {items-expression} [ | {filter-expression}
where:
{item}
- arbitrary name of variable that represents current item in repeatable section;{index}
- optional, variable name that receives integer - index of the item in items array;{items-expression}
- name of variable in model or expression that resolves to some array - list of objects to render by this repeatable.{filter-expression}
- optional, literal or nametoken that is resolved to a filter. Filter can be either:null
(or simply omitted) - no filter is set, all elements are rendered;object
(including object literal) - all elements matching that object are included into output;"string"
- if any string property in the item contains the string - the item gets rendered.function
having signature function(item, index)
. That function to be called for each item, if it returns true - record gets rendered;
The function is called with its this
variable set to the Repeatable container element (the one that has each attribute defined). This allows filter function to access DOM environment.
Example of the list that renders myCustomers items as list items:
<ol each="customer in myCustomers"> <li><picture @src="customer.image" /> <output(customer.name)/></li> </ol>
Example of list with filter function:
<ol each="customer in myCustomers|myFilterFunction"> <li><picture @src="customer.image" /> <output(customer.name)/></li> </ol>
Note that myFilter name gets resolved to myFilter variable / function defined in closest namespace to myCustomers
.
repeat
repeatableThis kind of repeatable uses repeat
attribute on the element that itself needs to be replicated:
<ol> <li repeat="customer in myCustomers"> <picture @src="customer.image" /> <output(customer.name)/> </li> </ol>
Where repeat
attribute uses the same syntax as each
above.
In some cases repeat form of the repeatable is more convenient than each.
The Plus defines @observing
decorator that can be used to subscribe functions on particular changes in model data tree. Syntax of declaring such subscriptions:
@observing "path" ["path2"[, ... "pathN" ]] function(path) { }
Where path is a string separated by .
(dot) and []
(if element on the path is an array).
The function gets called with this set to rightmost object or array on the path.
Example:
namespace Data { var items = [ { name:"Alan", state: true }, { name:"Winston", state: false } ]; @oberving "items[].state" function() { // .state property was changed in one of items // 'this' here is the item } }
Elements may contain one of event attributes defining function to be executed on corresponding action. The handler function is called with this
set to the DOM element and first parameter set to bound object.
List of event attributes: "click", "dblclick", "change", "enter", "escape", "focusin", "focusout".
Example:
<b click="items.removeByValue(item)">x</b>
Click on such <b>
element will call items.removeByValue()
function passing it current item object.
Main data model view element may have inner elements with model attribute - sub-views that are looking on parts of data tree.
namespace MyDataNamespace { var records = [ {name: "first", ...},
{name: "second", ...}, {name: "third", ...} ]; var currentRecord = {}; // used as sub-model; // function to set current record function setCurrentRecord(index) { currentRecord = records[index]; } }
Having such a model we can define sub-view element looking on sub-object or namespace inside the model ():
<body model="MyDataNamespace"> <!-- records list --> <ol each="index,record in records"> <li><output(record.name) click="setCurrentRecord(index)" /></li> </ol> <!-- current record view --> <section #record-details-view model="currentRecord"> <input(name) /> ... </section> </body>
Sub-view elements are observing change of [sub-] model variable, therefore as soon as the variable changes all observing elements get rebound with new data.
In the example above click on the output(name) element in the list will cause currentRecord to be set to new object by calling setCurrentRecord()
on click. See demos/L-list-detail.htm example.