6. Binding Expressions
Introduction
Runtime expressions are Jitsu's mechanism for retrieving data at runtime. In markup, they appear as the right hand side of attribute assignments:
<Label text="#bind(item.name)"/>.
Runtime expressions may contain javascript expression syntax, so you can perform calculations against data that is retrieved dynamically. You can even combine multiple dynamic data elements in these calculations:
<Label text="#bind(item.lastName) + ', ' + #bind(item.firstName)"/>
You can also use runtime expresssions within a Jitsu event handler (e.g. a button's click event handler), and you can even perform assignments:
<j:Button click="#set(item.firstName) = #get(item.firstName).toLowerCase();"/>
Expression Parts
The dynamic components of a runtime expression, or expression parts, come in several forms:
#bind | Creates a synchronized binding between the property referenced by the given path and the property being assigned to. When values in the source object change, the #bind expression is updated automatically. If this runtime expression consists only of a single #bind expression part, the synchronization will be bidirectional, i.e. changes in the property on either end of the connection will cause the other value to be updated. |
#get | Retrieves the value a single time from the given path. This is significantly more efficient mechanism than #bind, for retrieving read-only data values. #get may also used within event handler expressions. |
#set | Used only in event handler expressions, on the left-hand-side of an assignment operator, to set a property with a given path. |
#param | Represents a parameter passed in to a subclassed control. #param provides a mechanism working with parameterized controls in markup, a significantly more efficient mechanism than bindings, useful for specifying values that are known to the control at creation time. See Extending Controls for more. |
#res | This represents a resource that has been associated with a control.
Used in particular to reference string constants for internationalized text. |
Examples
<j:TextBox text="#bind(item.age)"/> <j:Label text="#get(//data.people.fred.firstName)"/> <j:Label text="#param(label).toUpperCase()"/>
Paths
For #bind and #get, you specify a path, similar to a simple xpath, that describes how to reach the given data from the location of the runtime expression.
Paths can contain dotted property references:
item.age person.firstName
There are three prefixes you can prepend to a path:
//data.people ./item.name ../selectedIndex
These prefixes tell the binding engine how to locate a binding container (a control that contains binding definitions) to resolve the binding against.
The '//' prefix tells the binding engine to jumpy to the root App on the page and follow the path from there.
Using './' tells the binding engine to start with the control containing the binding.
Using '../' tells the binding engine to start with the parent of the control containing the binding, then search upwards until it hits a control whose isBindingContainer property is true. More than one '../' may be prepended to navigate up through multiple binding containers.
Here's a simplified BNF notation for the runtime expression syntax:
RuntimeExpression ::= ( ExpressionPart, Javascript )* ExpressionPart ::= '#' [ 'bind', 'get', 'param', 'res' ] (' Path ')' Path ::= [ Prefix ] name [ Accessors ] [ Options ] Prefix ::= ( '//' | './' | '../') Accessors ::= ( ArrayAccessor | PropertyAccessor )* ArrayAccessor ::= '[' digits ']' PropertyAccessor ::= '.' name Options ::= ( ',' Option ':' argument )* name ::= /[a-zA-Z_][a-zA-Z0-9_]*/ digits ::= /[0-9]+/
Expression Part Options
The different expression parts may be further tailored by specifying options. The options available, and their associated expression parts, are described below:
Option | Description | Applies To |
---|---|---|
convert | Converts the inbound value returned by the expression part using a javascript expression. The argument is a javascript fragment that is evaluated. Use the variable name 'val' to refer to the value at the path. | #get, #bind, #param, #res |
convertBack | Converts the value currently in the associated property before updating it in the associated path target. The argument is a javascript fragment that is evaluated. Use the variable name 'val' to refer to the value at the path. | #bind |
instant | Overrides any submitters that are associated with the control, causing updates to take place immediately. | #bind |
defaultValue | Provides a default value for the part, in the event that it is never successfully connected or specified. | #bind, #get, #param |
Debugging Bindings
For controls that use #bind() bindings, the Jitsu Inspector shows small icons next to each control to indicate their binding status.
The following icons are used:
indicates that the control has no #bind() bindings.
means all the bindings of the object are satisfied.
shows that the control (or one of its children) has one or more bindings that failed to resolve to an object - i.e. a binding path refers to an object that doesn't exist.
means the control (or one of its children) has a binding that resolved to an object, but the the property specified specified as the last element in the binding path is undefined.
If you see a control with a question or exclamation mark icon, first click on the control in the insector, then expand its "bindings" node in the right hand pane (circled in red) to see which binding is failing.
To inspect the data hierarchy on the page, click on the root App control, then expand the data node in the right hand pane:
Two terms used in the binding code are source and sink. These are slightly misleading terms, since bindings operate bidirectionally, but at the time of connection, the sink is the object the binding was defined on, and the source is the object being connected to. The naming is to emphasize whose property is the source (i.e. who wins) when the connection is first made.
The core of the binding system is the Binding class, which defines the sinkObject and sinkPropertyKey that are bound, the path that the property is bound to, and (if the binding is live) the sourceObject that is currently connected.
On each control, bindings
and calculatedBindings
contain a list of
Binding (or CalculatedBinding) objects that have been registered on that control. Each
entry in bindings corresponds to a simple #bind(...) expression. Each entry
in calculatedBindings corresponds to a property using multiple #bind expressions.
You may also notice bindingBeads. A BindingBead is used for housekeeping purposes. It is a little object added to a parent object to indicate that one of its descendents has a binding that needs to be managed.
On each container control, you will also see a bindingBeads property, which contains a list of bindings that are being managed for children of the control.