WEMScript for Widgets

The WEM-Specific script language for WEM Widgets

Besides the WEMScript for Custom HTML, WEM also has WEMScript with specific features to use when creating Custom WEM Widgets.

WEMScript is a specific script created by WEM to make it possible to use specific WEM Elements (properties, fields, events, functions) available to the Script Editor in the Widgets. This is typically the low-code part of WEM.

Statements

if

General usage:

if <expression>
    statements...
elseif <expression>
    statements...
else
    statements....
end

The elseif and else parts are optional. The expressions of the if and elseif statements should return a boolean (true/false).

while

General usage:

while <expression>
    statements...
end

The expression after while should return a boolean (true/false) - all statements between while and end will be executed as long as the while-expression is true.

var

There are two ways to declare a variable:

/* declare a variable, the initial value is "unknown" */    
var @a : type

/* declare a variable and assign an initial value */
var @b := expression

The type of variable @b is derived from the expression. The following types are supported by wem:

actionflowchart, boolean , buttonexit, concept, conceptset, datetime, duration, file, hyperlink, list, multiselect, navigationitem, number, richtext and text.

arrays

Array variables are available in WEMScript. Array variables are declared as follows:

var @numbers : array<number>;
var @dates : array<date>;
var @concepts : array<concept>;
...

The type array<T> is generic, but T can itself not be an array type. The following declaration is not supported:

var @arr : array<array<number>>;

An array variable is never null (or unknown). As soon as the variable is declared, an empty array is instantiated. The following methods can be used to add or remove values from an array:

var @primes : array<number>;

/* Add the numbers 2 and 3 to @primes */
add 2 to @primes;
add 3 to @primes;

The new values will be appended to the end of the array. If you only want to add a value if it is not already in the array, then you can use the unique keyword:

/* Unique keyword ensures that the number 5 will only be added once. */
add unique 5 to @primes;
add unique 5 to @primes;

You can use the remove statement to remove values from an array:

remove 5 from @primes;

Use the count() function to get the length of an array. Other aggregation functions are also available, such as concat(), max(), min(), sum() and average().

var @cities : array<text>;

add "Amsterdam" to @cities;
add "London" to @cities;
add "Paris" to @cities;

/* Prints "3" */
print count(@cities);

/* Prints "Amsterdam, London, Paris" */
print concat(@cities);

You can initialize an array by using the list() function.

/* A list of the first 10 primes. */
var @primes := list(2,3,5,7,11,13,17,19,23,29);

Use parentheses to get the value at a specific index. The index is one-based (meaning the first element has index value of 1):

print @primes(1); /* prints "2" */
print @primes(3); /* prints "5" */

If the index is out of bounds, then the result will be unknown. You should not get an error in the script when this should happen.

You can use the in operator to check if the array contains a certain value:

var @list := list(2,3,5,8,13)

if (3 in @list)
    print "3 is in the list"
end

register

The register statement can only be used in render scripts and is used to indicate to the runtime that a form <input> field will be rendered that is bound to a Data Field item from the project Data Model. This enables two-way binding. General usage:

register input <data-field> [required = <expression>], 
    [notrim = <expression>],
    [precision = <expression>], 
    [include time = <expression>], 
    [unit = day|hour|minute|second],
    [invariant culture],
    [base64 = <expression>]

The most basic use is register input <data-field>. This can followed by some (optional) annotations:

  • required – annotates that the user is required to provide a value.

  • notrim – by default all the user input is trimmed. Use this annotation to disable trimming. The = <expression> part is optional.

  • precision – this annotation is only valid for number and duration data-fields. The precision should be a numeric value between -1 and 12 (including). If the precision is -1, or if the precision is omitted, then precision is limited by the 64bit float (double) that stores the value.

  • include time – this annotation is only valid for datetime data-fields. It states that it expects that the user specifies both the date and the time.

  • unit – this annotation is only valid for duration data-fields. Possible values are day, hour, minute, and second.

  • invariant culture – this annotation is only valid for number, duration and datetime data-fields. It states that it always expects the user input to be in an international (US English) format. For dates, it means that the input should be a valid ISO8601 format. For numeric values, it means that the . should be used as the decimal point (and comma as thousand-separator).

  • base64 - this annotation is used for File Input Properties.

The annotations required, notrim, include time and invariant culture can be used with or without an expression. If it is used without an expression, then <annotation> = true. is implied.

Examples:

register input [StartDate] required, invariant culture
register input [Length] precision = @precision, invariant culture
register input [Appointment.Duration] precision = 0, duration = minute
register input [Password] required, notrim
register input @ImageFileProperty base64 = true

The order in which the annotations are stated is not relevant.

Files that are part of the datamodel are stored in Base64 and may be prefixed with a filename and semicolon. For example if your Widget uses a canvas element you can create and store an image based on that canvas by using the Javascript toDataUrl function. That function returns a data URL containing Base64 data, the data you can use to store images. Note that WEM is only interested in the data, with an optional filename prefix.

If your data URL is: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ…ADAMBAAIRAxEAPwD/AD/6AP/Z"

you need to remove "data:image/jpeg;base64,".

A valid value with a filename prefix would be: "image.jpg;/9j/4AAQSkZJRgABAQ…ADAMBAAIRAxEAPwD/AD/6AP/Z".

print

Print statements can only be used in render scripts. The general usage is:

print [html|attr|js] <expression>

Renders the result of the given expression. If html is provided, then the result will be html encoded; if attr is provided, then the result will be html attribute encoded.

loop

General usage:

loop <expression>
    statements...
end

The result of the expression should be "iterable". That is, it should either be a (filtered) list or a concept set. This means the following examples are valid:

/* list */    
loop [Products]
    statements...
end

/* filtered list */
loop [Products] where [Price] > @MinPrice
    statements...
end

/* concept set */
loop [SelectedLocations]
    statements...
end

/* GetChildren() returns a concept set */
loop GetChildren([SelectedLocation]) 
    statements...
end

When looping through a concept set, you can use the keyword concept to reference the current concept within the loop.

follow

The follow statement can only be used in the event-handler script of a Widget. The general usage is:

follow @buttonExit

This statement will terminate the script and follow the given (button) exit.

execute

The execute statement can only be used in the event-handler script of a Widget. The general usage is:

execute @actionFlowchart

This statement will execute the given (action) flowchart. After the flowchart is executed, it will continue the execution of the event-handler script.

The navigate to statement can only be used in the event-handler script of a Widget. The general usage is:

navigate to <expression>

This statement will terminate the script and redirect the user to the given location. The given expression should return a navigationitem, hyperlink or string. If the return type is a string, it will be interpreted as a URL (hyperlink).

scriptmodule

The scriptmodule statement can only be used in the render-script of a Widget. The general usage is:

scriptmodule <expression>
    statements...
end

Use the scriptmodule statement to add JavaScript code that your widget needs. This JavaScript code will be added to the page only once: the first time that the widget is used on the page. This makes it possible to add a Widget more than once on a Template while the code being used is only added once to the output (optimizing the HTML response).

scriptreference

The scriptreference statement can only be used in the render-script of a Widget. The general usage is:

scriptreference <key-expression> <src-expression>

Use the scriptreference statement to add a JavaScript reference that your widget needs. This JavaScript reference will be added to the page only once: the first time that the widget is used on the page.

startupscript

The startupscript can only be used in the render-script of a Widget. The startupscript block will be invoked after the whole HTML result has been rendered. You can use this code block to initialize instances in JavaScript.

The general usage is:

<? startupscript ?>
    /* JavaScript code */
    MyJsLibrary().init();
<? end ?>

submitscript

The submitscript can only be used in the render script of a Widget. The submitscript block will be invoked before the new request is sent, for example immediately after a button click. You can use this code block to alter the input data before it will be send with the request.

The general usage is:

<? submitscript ?>
    /* JavaScript code */
    var myField = document.getElementById("my-field-" + <?js OutputId()?>);
    myField.value = myField.value.trim().toLowerCase();
<? end ?>

unloadscript

The submitscript can only be used in the render-script of a Widget. The unloadscript block will be invoked after the request has responded but before the new HTML has been drawn. You can use this code block to cleanup JavaScript instances before drawing the new HTML.

The general usage is:

<? unloadscript ?>
    /* JavaScript code */
    MyJsLibrary().dispose();
<? end ?>

once

The once statement can only be used in the render script of a Widget. The general usage is:

once
    statements...
end

once <expression>
    statements...
end

The once statement can be used to make sure that an html fragment is only added to the page (html-output) once. You should not use this keyword to add scripts. Use the scriptmodule, scriptreference or startupscript statements to add scripts in the correct way.

Keywords

OutputId

This keyword is only available in render scripts and will return the WEM output Id for the current custom control or for the given data-field. Usage:

/* Returns the output Id for the custom control */     
OutputId()

/* Returns the output Id for a data-field within the current context */
OutputId([MyDataFieldItem])

An example that uses the id keyword in combination with the register input field to create a custom control for text input. It assumes that the property @DataField exists and is bound to a text datafield.

<? register input @DataField required = true ?>
<input type="text" name="<?attr OutputId(@DataField) ?>" value="<?attr @DataField ?>" />

When the custom control properties are resolved, it will become something like this:

<? register input [MyDataFieldItem] required = true ?>
<input type="text" name="<?attr OutputId([MyDataFieldItem]) ?>" value="<?attr [MyDataFieldItem] ?>" />

range of

The range of keyword returns the range of a concept (or concept set) data-field (single-select or multi-select). Usage:

range of [MyConceptSet]

This is an expression that returns a concept set. You can use this construct as part in a bigger expression:

if (@MyConcept in range of [MyConceptSet]) then "Yes" else "No"

The range of keyword is very useful when making a custom control bound to a concept (set) data-field. For the following example, assume that @ConceptSet is a property that is bound to a concept set data-field:

<? loop range of @ConceptSet ?>
    var @className := concept in @ConceptSet ? "highlighted" : "dimmed";
    <div class="<?attr @className ?>"><?= concept ?></div>
<? end ?>

invalidvalue

This keyword is only available in render-scripts. The invalidvalue keyword returns the invalid value that is stored in the given data-field. It returns null if the value of the given data-field is valid. Usage:

invalidvalue [MyDossierItem]

The use case for this keyword is to get the invalid value that the user provided. For the following example, assume that @number is a property that is bound to a numeric data-field:

<? register input @number precision = 2 ?>
<? var @value := invalidvalue @number ? Format(@number, 2) ?>
<label>Enter a value:</label>
<input type="text" name="<?attr id(@number) ?>" value="<?attr @value ?>" />

Viewstate

The viewstate is accessible and modifiable in the Runtime. The WEM Runtime.js has an API that widgets can use to read and modify the viewstate. This greatly improves the possibilities of a widget.

/* Read some value from the viewstate. */
var value = Runtime.viewState(outputId).get("key");

/* Write a value to the viewstate. */
Runtime.viewState(outputId).set("key", value);

/* Remove a value from the viewstate. */
Runtime.viewState(outputId).clear("key");

/* Clear the entire viewstate for a given control. */
Runtime.viewState(outputId).clear();

You can define Viewstate fields in the Viewstate tab. The type of a field is restricted to one of the following:

Text, RichText, Number, Boolean, Date and Duration

Last updated