Skip to main content
Version: 4.2 (beta)

Value Bindings

Overview​

Value bindings allow you to attach dynamic expressions to certain attributes of UI objects, enabling automatic updates based on changes in values or calculations. This feature enhances the flexibility and interactivity of your UI objects without requiring server-side code.

Key Features​

  • Dynamic Expressions: Use a subset of JavaScript syntax to create expressions, including if, switch and return.
  • Automatic Updates: Expressions are re-evaluated whenever the device, UI, field or attribute values they read change.
  • Local Variables: Define variables within the scope of each value binding.
  • Predefined Functions: Utilize a set of predefined functions for various operations.
  • Client-Side Data Manipulation: Enable UI behaviors and data manipulation without writing server-side code.

Syntax​

Value bindings are written using a subset of JavaScript syntax and can span multiple lines. Statements are separated by a semicolon ; or by a new line, and the attribute takes the value of the last statement that runs, or the value given by return. Variables in value bindings are untyped, meaning they can hold any type of value. The syntax is simply <varname> = <value>, without var, let or const.

Text is written between double or single quotes, and comments start with # and run to the end of the line.

Example:​

a = 5 * 2
"hey" + a # Result: "hey10"

In the project's index.xml file, a value binding is saved in an attribute named after the bound attribute with the -bind suffix, e.g. label-bind.

Operators​

OperatorsDescription
+ - * / %Arithmetic; % is the remainder. + joins text when either side is text: "a" + 1 is "a1"
^Power: 2 ^ 3 is 8
== != < > <= >=Comparison. Values of different types are converted before comparing, so '1' == 1 is true
&& || !Logical and, or, not. As in JavaScript, a || b gives a when it is true, otherwise b, so x || "none" gives a default value; a && b gives a when it is false, otherwise b. ! gives true or false
condition ? a : ba when the condition is true, otherwise b
& | ^| ~ << >> >>>Bitwise and, or, xor, not and shifts

There is no logical xor operator: use the xor(a, b) function. The words and, or, not and mod are not operators: write &&, ||, ! and %. Projects created with earlier versions of HSYCO, which used these words, are converted automatically when they are loaded.

Methods can't be called on values: instead of text.trim(), use the predefined function trim(text). Lists are written as [1, 2, 3]; list[0] reads the first element and list.length the number of elements.

Statements​

Value bindings support the JavaScript if/else, switch and return statements:

  • if (test) ... else ..., where each branch is one statement or a block of statements in braces, and else if chains.
  • switch (value) { case a: ... default: ... } runs from the first matching case, or from default, up to a break or the end of the switch. Cases are compared without converting types, as with JavaScript's ===: device and UI values are text, so write case "1":, not case 1:.
  • return value ends the binding with that value.
level = device("dimmer.1")
if (level == 0) {
return "Off"
}
switch (ui("mode", "value")) {
case "eco": "Eco " + level + "%"; break
default: level + "%"
}

An if whose branch doesn't run, and a switch where no case matches and there is no default, give no value, so the attribute is cleared.

JavaScript Syntax That Isn't Supported​

Some JavaScript syntax is not part of value bindings, and using it is a syntax error that says what to write instead:

JavaScriptIn a value binding
let x = 1, var, constx = 1
===, !====, !=
x += 1, x++x = x + 1
// comment# comment
Math.round(x)round(x)
`Total: ${x}`"Total: " + x

Arrow functions, loops, ??, ?., typeof and new are not supported either.

Using Value Bindings​

To add a value binding to a UI object attribute:

  1. Select the UI Object: Choose the UI object within the visual editor.
  2. Choose the Attribute: Right-click the attribute you want to bind.
  3. Add Value Binding: Choose "Add Value Binding" and type your expression in the attribute field, or click Σ to open the binding editor. The editor lists the available operators and functions by category, moving the categories that don't fit into a More menu, and its Help button opens this page.

To remove a value binding, right-click the attribute and choose "Remove Value Binding". Adding, editing and removing a value binding can be undone like any other change.

The Binding Editor​

While you type, the binding editor shows what the binding does:

  • The calls that recalculate the binding are underlined, with a refresh icon next to the line number.
  • The lines whose value can become the attribute value are shaded green, with a return icon next to the line number.
  • Errors are marked with a red icon and a wavy underline; hover over them to read the message.

The summary bar below the editor lists the calls that recalculate the binding as written ("Recalculated on") and the numbers of the lines that give its value ("Value from line"), or every error with its line number. A binding without these calls is calculated once, when its page opens ("Calculated when the page opens"). Click an entry to move the cursor to it. The Summary checkbox in the footer hides the bar; errors stay marked in the editor.

The Preview row of the summary bar shows the value the binding gives the attribute. Devices and UI attributes have their current values from the server, other attributes have the values set in the project, and ds() reads the datasource; the preview refreshes as these values change. A device or object that doesn't exist reads undefined, as on a page. Table columns and form items read the first row of their table or form. When the binding reads a value that only a page has, such as a row of a table that has none yet, the preview shows "Not available".

Supported Attributes​

Value bindings can be used with most attributes of UI objects, except for "static" attributes that cannot be changed after the object is created.

Variables and Reserved Words​

Variables declared within a value binding are local to that binding and only exist while it is evaluated. true, false, null and undefined are reserved and cannot be used as variable names, and so are if, else, switch, case, default, return and break. A variable with the name of a predefined function replaces that function in the binding.

Predefined Functions​

The framework supports a set of predefined functions to enhance the functionality of value bindings, grouped in the binding editor under Math, Strings, Arrays and System, plus Data and Table for form fields and table columns. For example:

  • device(<address>): Fetches the value of an HSYCO device.
  • ui(<id>, <attr>): Fetches the attribute value of a UI object.
  • value(<id>): Retrieves the current value of a field object.
  • attr(<name>): Reads another attribute of the same object.
  • round(<value>, <decimals>): Rounds a number, optionally to a number of decimals.
  • upperCase(<text>), trim(<text>), replace(<text>, <a>, <b>): Work on text.
  • cellValue(<column>): Fetches a value of the current table row.
  • pageId(): Returns the ID of the current page.
  • consoleLog(<str>): Logs a message to the console for debugging purposes.

Value bindings can't read EVENTS variables directly. To use a variable, set it on a UI attribute with UISET in EVENTS, e.g. $temp : UISET vars.temp = $temp, then read it with ui("vars", "temp").

A function name can't be used without calling it: round(x) is valid, round alone is an error.

Error Handling​

The binding editor marks every error while you type, as described above, and doesn't accept a binding with an error: on OK it shows the error and moves the cursor to it. An object whose binding has errors also gets a warning in the Project Editor, listing each error with its line and column. An expression that still uses the old word operators shows the replacement, for example Unexpected "and", use && instead.

Errors that happen while the binding is evaluated, such as a reference to an undefined variable, leave the attribute as it is and are displayed in the browser console, except when the project runs inside the Manager. The function consoleLog(<str>) can be used within the expression to help with debugging.

Dynamic Updates​

A value binding is recalculated when a value read by one of these functions changes:

FunctionRecalculates when
device(<address>)the device value changes
ui(<id>, <attr>)the UI attribute changes
value(<id>)the value of the field changes
attr(<name>)the attribute of the same object changes, also when another binding sets it
ds(<datasource>, <key>, <column>)the datasource value changes
pageId()the page changes

Their arguments can be held in a variable or computed, as in addr = "light.1"; device(addr) or ui("lamp" + sel, "text"): each time the binding runs, it watches what these calls read. A number or true/false is read as text. An empty, null or undefined argument reads undefined and watches nothing, as a device that doesn't exist does. An object or a list makes the binding fail, leaving the attribute as it is.

The first time a binding reads a datasource id or column, the client queries the server for it once. The binding keeps it connected for as long as the object exists, so reading it again uses the rows already loaded.

In table columns, form items and form footer items, a computed argument can come from the row or the form data, as in device(cellValue("address")): the table or form redraws when what its rows read changes. List items have no row, so an argument computed from cellValue, cellLabel, rowIndex or rowKey is an error there.

Example​

Here’s how a button object might use a value binding for its label attribute:

  1. Select Button Object: In the visual editor, select the button object.

  2. Choose Label Attribute: Right-click the "label" attribute and choose "Add Value Binding".

  3. Enter the Expression:

    count = ui("my","count")
    count > 0 ? "Count: " + count : "Empty"

    This example retrieves a value from the server using UISet("my","count",<value>) and builds the label of the button. Whenever a new UISet("my","count",<value>) is executed, the button label updates automatically.

Summary​

Value bindings provide a powerful way to create dynamic and responsive UI interfaces by allowing expressions to be attached to object attributes. With automatic updates and predefined functions, you can implement complex logic directly in the client, resulting in a responsive UI. This approach minimizes the need for server-side code and keeps it agnostic to the structure of the UI.