Expression Reference
Hibiki HTML features a full expression language to bind your HTML to the frontend data model.
Some attributes like bind
use expressions directly. Other attributes, and style declarations
are just plain strings and are not evaluated by default. Any attribute or style property can be
evaulated by using a *
as it’s first character.
Here are some examples:
Assuming that $.data = {x: 5, y: 20}
# bind expressions are always evaluated:
<h-text bind="$.data.x"></h-text>: => 5
# normal attributes and style properties are not evaluated
<div a="1" style="font-size: 14px"> => <div a="1" style="font-size: 14px">
# using a * will turn any attribute or style property into an expression
# like React, Dashborg will add "px" to numbers for CSS attributes where that makes sense.
<td colspan="*$.data.x"> => <td colspan="5">
<div style="font-size: *$.data.y"> => <div style="font-size: 20px">
<div style="height: *$.data.x + '%'"> => <div style="height: 5%">
<div style="font-weight: *$.data.x > 2 ? 'bold' : 'normal'"> => <div style="font-weight: bold">
# inside of a foreach loop, $local is bound to each value in sequence
<h-foreach bind="[1,2,3,$.data.x]"> => 2
<h-text bind="$local * 2"></h-text> 4
</h-foreach> 6
10
All data paths start with “$” or “@”. $
is used for data roots
and @
is for special contextual values.
$
- Accesses data from the data root (e.g.$.data.rows
or$.state.open
).@
- Binds to contextual data (e.g.@rownum
or@value
).$c
- Binds to component data (data local to a component)$state
- Binds to Hibiki HTML state data (url params, page title, other internal Hibiki HTML data).
After you specify the root, you can access array elements by using square brackets with numeric
values ($.array[0], $.array[4], $.array[$.data.index]
) and you can access map fields using dot notation or square
brackets with string values ($.data.map.x, $.data.map.bar, $.data.map["y"]
).
- Strings - can be either single or double quoted. Remember normal HTML entity encoding applies before any Dashborg processing so
"
becomes a quote character.- Double Quoted - Allow escapes using
\
. Use\n
for newlines (n, b, f, r, t, v are valid escapes). Can also be used to escape any other character (most useful for escaping double quotes"
). - Single Quoted - No escapes allowed.
- Double Quoted - Allow escapes using
- Numbers - integers and floating point numbers, base 10 format, no exponents. (regexp:
/[-+]?[0-9]*\.?[0-9]+/
). - Arrays - Use square brackets, comma separated values, trailing comma is okay. (
[2, "hello", 5, ]
). - Maps - Use braces, colon after keys, commas between key-values, trailing comma is okay. Use quotes for keys that are not identifier safe. (
{x: 5, "abc#foo": 10}
). true
,false
- for boolean values.null
- no valuenoattr
- special null value that when passed as an attribute makes it appear that the attribute was not specified (see noattr/isnoattr below).- Note that true, false, and null, are keywords (not allowed for identifiers, dot-notation, or unquoted map keys).
These are the supported Dashborg operators, listed in increasing precedence order:
Operator Type | Op | Notes |
---|---|---|
Ternary Conditional | ? : | [conditional-expression] ? [true-value] : [false-value] |
NullishCoalescing | ?? | Like ``` |
Logical Or | || | Short circuits. Returns first truthy value, or false |
Logical And | && | Short circuits. Returns first value if falsey, otherwise returns second value. |
Equality | ==, != | Shallow equality tests |
Relational | >=, <=, >, < | |
Comparison | <=> | returns -1, 0, or 1, if first argument is less than, equal to, or greater than second element (respectively) |
Additive | +, - | + will work with strings for concatenation |
Multiplicative | *, /, % | |
Unary | !, -, + |
To call a function you use fn:[function-name]([arguments])
,
e.g. fn:len($.data.expr)
. Function names are not case sensitive.
Function | Notes |
---|---|
fn:len | arrays: length, objects/maps: number of keys, scalars: 1, null: 0 |
fn:indexof | calls arg1.indexOf(arg2, [arg3]). Works for both strings and arrays. See String.indexOf |
fn:min | minimum of all arguments |
fn:max | maximum of all arguments |
fn:floor | Math.floor |
fn:ceil | Math.ceil |
fn:slice | Like the JavaScript slice function. First argument is an array, 2nd is start element (inclusive), 3rd element is the end element (exclusive). See Array.slice. If you pass a named argument ‘makerefs’ it will return an array of references to the original array. |
fn:splice | Like the JavaScript splice function, but does not modify the first argument, will return the newly spliced array. See Array.splice |
fn:push | Pushes values to the end of an array. Does not modify the input array, will return the new array. |
fn:pop | Pops a value off the end of an array. Does not modify the input array, will return the new array. |
fn:setadd | First argument is an array, will conditionally the rest of the arguments to the array if they are not already in the array (checked with “=="). Returns a new array. Useful for manipulating checkbox groups, or multi-select values. |
fn:setremove | First argument is an array, will conditionally remove the rest of the arguments from the array if they are present. Returns a new array. |
fn:sethas | First argument is an array, will check if the 2nd argument is inside the array (checked with “=="). Returns true/false. |
fn:int | converts argument to integer (truncates floats and converts strings to integer or NaN). See parseInt |
fn:float | converts argument to floating point number (converts strings to float or NaN). See parseFloat |
fn:str | converts its argument to a string |
fn:bool | converts argument to boolean |
fn:js | call an external js function. 1+ arguments, first argument is a string, rest of the args get converted to a parameter list. window[arg1].apply(null, args2-n) |
fn:jseval | evaluates its argument as javascript. calls eval(arg1). |
fn:merge | merges two objects together. Keys from righmost arguments replace keys from leftmost arguments |
fn:split | splits a string into an array. 1st argument is string to split, 2nd argument is string to split on. See split |
fn:ts | equivalent to javascript Date.now() (unix timestamp in milliseconds) |
fn:now | equivalent to javascript Date.now() (unix timestamp in milliseconds) |
fn:substr | arg1.substr(arg2, arg3). See substr |
fn:sprintf | sprintf(arg1, …) |
fn:startswith | arg1.startsWith(arg2) |
fn:endswith | arg1.endsWith(arg2) |
fn:uppercase | returns string value to uppercase |
fn:lowercase | returns string value to lowercase |
fn:match | creates a regex with arg2 and and arg3 as options. tries to match arg1. e.g. arg1.match(new Regexp(arg2, arg3)) |
fn:trimindent | fn:trimindent(string), removes constant left indentation from a string |
fn:replace | fn:replace(string, find-string, replace-string), replaces the first occurance of ‘find-string’ in ‘string’ with ‘replace-string’ (no regex support) |
fn:replaceall | fn:replaceall(string, find-string, replace-string), replaces all the occurances of ‘find-string’ in ‘string’ with ‘replace-string’ (no regex support) |
fn:uuid | creates a UUIDv4 |
fn:json | converts its argument to JSON |
fn:jsonparse | parses argument from JSON to an object |
fn:objkeys | returns an array of all non-@-keys in the object |
fn:objatkeys | returns an array of all @-keys in the object |
fn:objallkeys | returns all keys in object (both regular and @-keys) |
fn:filter | fn:filter(array, lambda) calls lambda on each element (@elem, @index), removes items where lambda is not true |
fn:map | fn:map(array, lambda) maps an array by calling lambda on each element (@elem, @index) |
fn:find | fn:find(array, lambda, [fromindex]), calls lambda (@elem, @index) on each element starting from fromindex (default 0), returns first element that returns true |
fn:findindex | same as fn:find, but returns index, or -1 if not found |
fn:reduce | fn:reduce(array, lambda), reduces array using lambda (left reduce) |
fn:reverse | fn:reverse(array, makerefs=false), returns a reversed array. if makerefs is true, will create references to the original array (not copying values) |
fn:every | fn:every(array, lambda), returns true if calling lambda (@elem, @index) on every element returns true. calling on empty array or null returns true. calling on non-array (e.g. string or object) returns false. |
fn:some | fn:some(array, lambda), returns true if at least one element in the array has the lambda (@elem, @index) evaluate to true. empty arrays will return false (no element matches) |
fn:concat | concatenates arrays together. all arguments must be arrays or null. null values are skipped. |
fn:join | fn:join(array, string), joins array elements together using string as the separator |
fn:shift | returns a new array where the first element is removed |
fn:unshift | fn:unshift(array, elem, [elem2], [elem3]…) returns a new array where the elements passed are added to the front of the array (not reversed) |
fn:bloblen | returns length of blob (deprecated, use @blob.bloblen) |
fn:blobmimetype | returns blob mimetype (deprecated, use @blob.mimetype) |
fn:blobastext | returns text version of blob (only if mimetype starts with “text/") |
fn:blobasbase64 | returns base64 encoded version of blob (deprecated, use @blob.base64) |
fn:blobname | returns the file name from the blob object (deprecated, use @blob.name) |
fn:deepequal | 2 arguments, return true/false if they are deeply equal |
fn:deepcopy | creates a deepcopy of arguments (not usually necessary since Hibiki makes a copy automatically, except when dealing with references) |
fn:compare | (see compare documentation below) |
fn:sort | (see sort documentation below) |
Requires 2 arguments, the two values to be compared. Returns -1, 0, or 1 if the first argument is less than, equal to, or greater than the 2nd argument (respectively). The named parameters affect the comparison:
Named Parameters:
- type (string) - “numeric” or “string”. if null, the comparison will be based on the types of the arguments. If both are numbers, then it will be a numeric comparison, otherwise string. nulls will always sort before any value in either type.
- locale (string) - override the locale of the comparison (otherwise uses the browser’s locale value). e.g. for french should use “fr”.
- sensitivity (string) - “base”, “accent”, “case”, or “varient”. See Intl Collator Docs. By default it is set to “varient”, but other sensitivity types allow different characters to be treated as equal.
- nocase - sets sensitivity to “accent” (sensitivity option takes precendence). this makes the comparison case-insensitive.
- field - if both arguments are objects, compares the sub-field named field within them. e.g. if you call fn:compare($.foo, $.bar, field=‘test’), it will compare $.foo.test with $.bar.test. if argument is null, or does not contain the specified field, will compare against null.
- index - if both arguments are arrays, compares the sub-index index within them. e.g. if you call fn:compare($.foo, $.bar, index=8), it will compare $.foo[8] with $.bar[8]. if argument is null, or does not contain the specified index, will compare against null.
fn:compare($.v1, $.v2, type="numeric") # force numeric comparison
fn:compare($.v1, $.v2, type="string", nocase=true) # force case-insensitive string comparison
fn:compare($.v1, $.v2, field="qty", type="numeric") # numeric comparison between $.v1.qty and $.v2.qty
# compare $.v1.name with $.v2.name, using rules from french locale.
# comparison will be case-insensitive and will ignore accents
fn:compare($.v1, $.v2, field="name", locale="fr", sensitivity="base")
Requires 1 argument, the data to be sorted. Returns a new array where the input data is sorted according to the provided arguments.
Named Parameters:
- data (array) - the data to be sorted. can also be passed as the first positional argument.
- sortexpr (lambda) - a lambda expression that overrides the built in compare function. Gets two context variables @a (first value), and @b (second value). Should return -1, 0, or 1.
- desc (boolean) - if true, will reverse the sort order
- makerefs (boolean) - if true, will create a sorted array of references that point back to the original array elements.
- slice ([number, number]) - a one or two element array to slice the final array. same arguments as fn:slice().
- nosort (boolean) - if set to true, do not sort the array (just return a copy).
- (any parameter that can be passed to fn:compare) - these parameters are passed to fn:compare() when comparing values. not used if sortexpr is provided.
fn:sort($.data) // sorts data naturally using fn:compare() function
fn:sort($.data, field="qty", type="numeric") // sort objects using field "qty" numerically
fn:sort($.data, makerefs=true) // returns array of references to original array
fn:sort($.data, field="qty", desc=true) // sort objects using field "qty", reverse order
fn:sort($.data, slice=[10,20]) // sort data naturally, return elements 10-19
Expression | Notes |
---|---|
ref(path-expr) | creates a reference to the given path. can only accept global or component paths ($) or ($c). |
isref(expr) | returns true/false if the expression is a reference |
refinfo(expr) | if expr is a reference returns a string showing what it references, otherwise null |
raw(expr) | normally references are automatically de-referenced (so they are transparent). using the expression raw() will keep them from being evaluated. useful for passing references to a sub-component. |
noattr | the special constant meaning “no attribute”. generally in most user expressions it is equivalent to null. however for some components passing an argument equal to “null” or not passing any argument is different. The “noattr” expression allows you to propagate the “no attribute” to a sub-component. |
isnoattr(expr) | returns true/false if the expr is equal to “noattr”. you cannot distinguish “noattr” with “==” because noattr == null (and vice versa). |
lambda(expr) | creates a special lambda value of the given expression. lambda values are not evaluated until they are invoked. They will only have access to the context data that is passed in during invoke. Any other data references (e.g. to global data, component data, or current context data) will be undefined. |
invoke(lambda, params) | invokes a lambda. params are context variables to be passed into the expression |
Data Root | Notes |
---|---|
$ | Alias for $data (global data root) |
@ | Alias for $context |
$c | Component root data (data local to the current component) - alias for $component |
$data | Returns root of frontend data model (global root) |
$component | Component root data (data local to the current component) |
$state | Returns Hibiki state data (used for interacting with internal Hibiki HTML state) |
$context | Returns contextual data – e.g. iteration index (@index), or input value in data (@value). $context.value is the same as @value. |
$contextstack | Returns an array (stack) of context data. Used for nested loops to access outer loop’s data. $context is the same as $contextstack[0] (enclosing contexts have larger indexes). $contextstack[0].value is the same as @value. |
Hibiki HTML handlers (handler, click.handler, change.handler, mount.handler, init.handler, etc.) execute actions. The Hibiki HTML parser will convert these action statements into Hibiki Actions. Every action that can be parsed can also be represented as JSON and vice-versa.
Statements must be separated with a ;
. Trailing ;
is allowed.
Used to set values into the data model. Note you can only set values into $data ($), $state, or $context (@). Context values are local to the handler block (just used for temporary variables).
$state.x = 5;
$.job = {jobid: 22, jobname: "Test Job"};
@temp = 5 * $state.val;
@temp2 = @temp * @temp;
$.newval = @temp2;
@foo = /test/handler;
Used to return values from a handler. Does not terminate the running handler, just sets the return value.
setreturn(5);
setreturn({x: 10, y:20});
setreturn(null);
Call handlers always start with /
. They consist of 3 parts, an optional module, an optional path, and an optional path fragment. Either module or path must be specified (path will default to /
).
/@[module-name]/[path]:[pathfrag]
Handlers can take an optional parameter list of data. Data is specified as key = value pairs separated by commas (trailing commas are allowed). Positional parameters are also allowed before any key/value pairs, and will be placed into a special data key named *args
. Some handlers allow special ‘@'-key parameter values..
Here are some example paths, with and without arguments:
# simple path, no module, no arguments
/handler/path
# 'fetch' module, 'get' pathfrag, path defaults to '/'.
# 1 positional argument, 3 key-value arguments passed. 'x', and 'y' are normal, @method is a special parameter
/@fetch:get('/api/test', @method='post', x=10, y=20)
# no module, 2 positional parameters, one key-value parameter
/complex/run-handler(5, 10, userid=$.userid);
# can assign the return value from handlers to the data model
$.index = /api/get-index(partid=22)
# using the 'call' keyword, we can construct dynamic call paths
# arguments and return values work the same.
call "/test-handler" (x=10, @method='post');
call "/test" + $state.handlername, (x = $state.x, y = $state.y);
@rtnval = call "/test/handler";
Note that handlers are not expressions, so only simple assignment works, you can’t write @foo = /test + 5
.
Fire an event that triggers event handlers. Parameters can be name-value pairs which will create context (@vars) in the callee. A single positional parameter will set the ‘value’ key. Adding the @bubble at-param will make the event bubble.
fire->click;
fire->custom1();
fire->submit(itemid=22, price=100, @bubble=true)
fire->custom2(@bubble=true);
fire->custom3(88)
Throws an error. Takes one argument which will be evaluated as a string (the error message):
throw("Invalid User Id");
throw("No value is set");
Logs a sequence of positional arguments to the JavaScript console. If @debug is true, it will output extra debugging information and a Hibiki/JavaScript call stack. If @alert is true, it will cause a browser alert.
log("the value is", $.value);
log("show stack trace", @debug=true);
log("Not Logged In!", @alert=true);
if/then/else implementation. Parenthesis around the conditional expression,
Braces are required around the then/else statement blocks, else statement is optional.
If statements require a ;
to separate them from subsequent statements;
if ($.primary) { $.openpopup = true; };
if ($.color == 'blue') { $.blue = true } else { $.red = true };
Does nothing.
nop;
nop();
Raw expressions can be statements. This is only useful if your expression has side effects. Useful in conjunction with fn:js. There is no “expr” JSON action, instead you can use a “setdata” action with a null setpath.
expr fn:js('external_fn_name', 55);
expr fn:js('alert', 'hello');