Skip to content

Expressions ​

bind: attributes, ui: directives and {{ }} interpolations hold an expression: a small language for reading your data and combining it. It is not ThicketScript. It has no calls, no assignment and no indexing, on purpose: logic lives in the controller, and the template only reads.

xml
<Panel layout="vbox" spacing="8">
    <Label bind:text="name ? 'Hello, ' + name + '!' : 'Type your name'" />
    <Button text="Buy" bind:enabled="gold >= price" />
    <Panel ui:show="!loading &amp;&amp; items" />
</Panel>

Values ​

KindWritten as
Number42, 3.5
String'single' or "double" quotes. Inside an attribute that uses double quotes, use single. Escapes: \n, \t, \\, and a backslash before any other character keeps that character (\').
Booleantrue, false
Nothingnil
A namecount, user.name, item.price

Names ​

A name is looked up in this order:

  1. Loop variables from an enclosing ui:for, innermost first. A dotted name whose first part is a loop variable walks into that value: map keys, a ReactiveRecord's fields, or plain object fields.
  2. The controller: lookupRef(path) is called with the whole name, dots included ("user.name"). Your controller decides what that means.

If the answer is a Ref (or a computed), its current value is used and the binding subscribes to it: when the ref changes, the expression runs again. That is the whole reactive story. Return refs from lookupRef for anything that changes.

A name nobody answers is nil.

Operators ​

From lowest to highest precedence:

OperatorsMeaning
c ? a : bConditional. Right-associative.
a || bOr. Short-circuits. The result is true or false.
a && bAnd. Short-circuits. In XML write &amp;&amp;.
a == b, a != bEquality. Two strings compare as text, anything else as numbers.
<, <=, >, >=Comparison, as numbers. In XML write &lt; for <.
+, -Add and subtract. + joins strings if either side is a string.
*, /, %Multiply, divide, remainder. Dividing by zero gives 0. % works on whole parts.
!a, -aNot, negate.
( )Grouping.

Truthiness ​

!, &&, ||, ? : and ui:show treat these as false: nil, false, 0 and the empty string "". Everything else is true. So ui:show="items" hides a panel while items is nil, and name ? ... : ... tests for an empty string.

Comparisons ​

When both sides of == or != are strings, they compare as text: name == 'Bob' is true only for Bob, and name != '' is true for any name that is not empty. Otherwise both sides are converted to numbers first: booleans become 0 and 1, numeric strings parse, and any other string becomes 0. <, <=, > and >= always compare as numbers.

Interpolation ​

Inside element text, {{ }} marks an expression:

xml
<Label>Level {{level}}: {{hp}} / {{maxHp}} HP</Label>

The text becomes a concatenation of literal parts and expressions. An unclosed {{ or a piece that does not parse is kept as literal text.

Errors ​

A syntax error in bind:, ui:if, ui:show, ui:for or ui:key fails the compile with a TemplateError naming the attribute. A runtime problem (a missing name, a wrong type) never throws: the value becomes nil, 0 or "", and a binding whose evaluation fails simply skips that update.

In code ​

The expression API is public, which is handy for tests or for your own widgets: parseExpression(src) returns an ExprNode, evalExpression(node, new EvalContext(provider)) evaluates it against any DataProvider.