Skip to content

Commit

Permalink
Widget expressions Add docs for # shortcut
Browse files Browse the repository at this point in the history
Refs openhab/openhab-webui#2441.

Signed-off-by: Florian Hotze <[email protected]>
  • Loading branch information
florian-h05 committed Mar 29, 2024
1 parent 0b81ce2 commit 3e7c8ad
Showing 1 changed file with 41 additions and 22 deletions.
63 changes: 41 additions & 22 deletions ui/widget-expressions-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ When designing pages and widgets, you might want a config property of a widget (
Variables are a way to allow more complex scenarios in pages & personal widget development.

The widget expression system uses a JavaScript-like expression parser, [jse-eval](https://github.com/6utt3rfly/jse-eval).
In order to remain light-weight and responsive, this is not a complete JavaScript library, but nearly all of the basic function is provided along with some more advanced features.
In order to remain light-weight and responsive, this is not a complete JavaScript library, but nearly all the basic functionality is provided along with some more advanced features.

## Expressions Overview

Expressions are string literals beginning with the symbol `=` and everything after it is evaluated using a syntax very similar to JavaScript.
You can use arithmetic or string operations etc., the [conditional (ternary) operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator), as well as the following objects (subject to evolutions):

- `items` is a dynamic key/value dictionary allowing you to retrieve the state of any Item:
The result of `items.Item1` will be an object like `{ state: '23', displayState: '23 °C', type: 'Decimal' }` (`displayState` may be omitted).
The result of `items.Item1` will be an object like `{ state: '23', displayState: '23 °C', numericState: 23, unit: '°C', type: 'Decimal' }` (`displayState`, `numericState` and `unit` may be omitted).
You can therefore use `items.Item1.state` to use the current state of `Item1` in your expression, and if the state changes, the expression will be reevaluated.
- `props` is a dictionary of the key/values of self-defined props for the current personal widget, or page (pages, like any root UI components, may indeed have props).
It is indispensable to use props in expressions when developing a personal widget so you can pass configuration from the page to the widget.
It is indispensable to use props in expressions when developing a personal widget to pass configuration from the page to the widget.
- `config` is a dictionary of the key/values of the configuration of the current component/widget.
- `vars` is a dictionary of [variables](#variables) that are available in the component's context
- `loop` is a dictionary containing iteration information when you're repeating components from a source collection.
Expand All @@ -36,12 +36,12 @@ You can use arithmetic or string operations etc., the [conditional (ternary) ope
This allows you to access various information about the current screen, e.g. the available width and height.
The two properties `viewAreaWidth` and `viewAreaHeight` are added on top.
It's recommended to use CSS [`calc()`](css-pages-widgets.html) for dynamic positioning and styling.
- `user` returns an object with information about the logged in user:
- `user` returns an object with information about the logged-in user:
The name (`user.name`) and an array of the assigned roles for the user (`user.roles`).

## Variables

Variables can be used using several methods:
Variables can be used by several ways:

- The `variable` config parameter of an `oh-gauge` (read-only), `oh-input`, `oh-knob`, `oh-slider`, `oh-stepper`, `oh-toggle` will accept a variable name and control it instead of sending commands to an Item if set.
The `item` parameter can still be set to set the widget to the Item state if the variable has no value.
Expand All @@ -56,18 +56,37 @@ This is useful when "validating" a variable, e.g. send the variable value as com

The `@` symbol can be used in front of an Item name string as a shortcut to the `displayState` from the `items` dictionary with a fallback to the raw state:

```js
```javascript
footer: =@'Switch1'
```

is the same as

```js
```javascript
footer: =items['Switch1'].displayState || items['Switch1'].state
```

Similarly, `@@` can be used as a shortcut for just the Item state.

The hashtag `#` symbol is a shortcut to the `numericState` with no fallback:

```javascript
footer: =#'Temperature1'
```

is the same as

```javascript
footer: =items['Temperature1'].numericState
```

These shortcuts have two major benefits over directly accessing `displayState`, `state` and `numericState`:
Expressions become shorter when they are used, and if the Item name is a prop which is undefined, these shortcuts avoid that a request with Item name `undefined` is sent to the server, which would cause this log message:

```
[WARN ] [se.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: undefined
```

Expressions are particularly useful in cases where one wants to combine the states of more than one Item, or use the state of more than one Item in a single widget element.
For example, the icon of an Item can be based on the state of a different Item.

Expand All @@ -80,7 +99,7 @@ The expression parser can parse arrow functions as the parameters of these metho

Example:

```js
```javascript
title: =someItemList.find((x) => x.name === 'KitchenSwitch').label
```

Expand All @@ -95,7 +114,7 @@ Inside string templates, variable values can be inserted with the `${variable}`

Example:

```js
```javascript
text: =`This button opens the ${props.page} page`
```

Expand All @@ -108,7 +127,7 @@ Many of the JavaScript string methods accept regex parameters expressed as the r

Example:

```js
```javascript
label: =props.item.match(/_(.*)_/)[1]
```

Expand All @@ -120,15 +139,15 @@ When creating a regex, consider using tools like [regex101](https://regex101.com
### Objects

The variable action allows components in widgets to pass information back and forth when there is user interaction.
Often this informtation is simple, such as a single string or input value.
Often this information is simple, such as a single string or input value.
Sometimes, however, it is helpful to add more information to a variable and for these instances JavaScript objects are useful.

The widget expression system can create objects in two different ways:

- JavaScript object syntax:

Objects can be defined within the expression system using the standard JavaScript syntax: `{'key1':'value1','key2':'value2'}`.
If a key doesn't contain special characters such as spaces, the quotes around keys can usually be ommited: `{key1:'value1',key2:'value2'}`.
If a key doesn't contain special characters such as spaces, the quotes around keys can usually be omitted: `{key1:'value1',key2:'value2'}`.

::: tip

Expand Down Expand Up @@ -166,49 +185,49 @@ Object expression can also be used to simulate a `switch` control statement.

The most common flow control statement in expressions is the [conditional (ternary) operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator), which is very efficient for selecting from two options based on a single boolean criterion.
If you have a list of possible options, you can combine multiple ternary operators together, but this grows cumbersome very quickly.
For example, if there is an HVAC with a mode Item that can be set to `heat`, `cool`, `auto`, and `off` modes, it requires 4 nested ternary operators to set a component's background color to match the current HVAC mode (with a fall back option if the Item has some other state, e.g. `null`).
For example, if there is an HVAC with a mode Item that can be set to `heat`, `cool`, `auto`, and `off` modes, it requires 4 nested ternary operators to set a component's background color to match the current HVAC mode (with a fallback option if the Item has some other state, e.g. `null`).

```js
```javascript
background: =(@@hvacModeItem == 'heat')?'orange':(@@hvacModeItem == 'cool')?'blue':(@@hvacModeItem == 'auto')?'green':(@@hvacModeItem == 'off')?'white':'red'
```

To use an object instead, simply create an object with keys for each of the Item's expected states, and give each key the desired output value.
Referencing that object using the Item's state will return the desired value.
Adding a simple `OR` statement to that expression will provide the fallback condition if the object reference is undefined.

```js
```javascript
background: =({heat:'orange',cool:'blue',auto:'green',off:'white'})[@@hvacModeItem] || 'red'
```

## Examples

Translates the third part of the HSB state (brightness) of an `Color` Item to "On" or "Off":

```js
```javascript
=(@@'Color1'.split(',')[2] !== '0') ? 'On ' + '(' + @@'Color1'.split(',')[2] + '%)' : 'Off'
```

Use a filled icon of a lightbulb but only if the state of the Item passed in the prop `item` is ON:
Use a filled icon of a light bulb but only if the state of the Item passed in the prop `item` is ON:

```js
```javascript
icon: =(@@props.item === 'ON') ? 'f7:lightbulb_fill' : 'f7:lightbulb'
```

Stacked ternary statements to translate the state of Item `xxx` to a description:

```js
```javascript
=(items.xxx.state === '0') ? 'Off' : (items.xxx.state === '1') ? 'Heat' : (items.xxx.state === '11') ? 'Economy Heat' : (items.xxx.state === '15') ? 'Full Power': (items.xxx.state === '31') ? 'Manual' : 'Not Set'
```

Do the same using an object and the Item state shortcut:

```js
```javascript
={0:'Off',1:'Heat',11:'Economy Heat',15:'Full Power',31:'Manual'}[@@xxx] || 'Not Set'
```

Substract one week from the state of `DateTime` and return a relative time representation in the current locale ("3 weeks ago"):
Subtract one week from the state of `DateTime` and return a relative time representation in the current locale ("3 weeks ago"):

```js
```javascript
=dayjs(items.DateItem.state).subtract(1, 'week').fromNow()
```

Expand Down

0 comments on commit 3e7c8ad

Please sign in to comment.