WEMscript

WEM Widgets

For demos and documentation on existing widgets, see widgets.live.wem.io.

Visit our forum for more discussions on widgets: WEM Forum > WEM Modeler > Widgets.

WEM comes with a wide range of widgets that you can use in your project. If you need a specific widget that is not part of WEM, you can create that widget yourself.

A Widget is a way to extend the standard User Interaction capabilities in WEM. They are only used on templates/pages (user interaction nodes in WEM) and will run and present their functionality in the browser at the client.

A widget is written in WEMscript for hooking it into the rest of the WEM functionality. The widget’s intended functionality is typically written in javascript.
Javascript version ECMAScript ES5 is supported, that version is supported by all browsers. ES6 (ECMAScript2015) and newer versions are not (yet) supported by all browsers (particularly IE), so please use javascript syntax from ES5.

This article further describes the structure of WEM Widgets and syntax of WEMScript.

Widget Development Tab/Elements

  • Script: the actual functionality that will be added to the interaction node/template/page where widget is added to the template;
  • Properties: properties to be used by widget, parameters to be set on an instance of the widget; possible links to fields in data model;
  • View state: fields that will be added to and accessible from view state;
  • Events: definition of events and eventhandler scripts (refresh screen, execute flowchart, follow exit);
  • Resources: files (scripts or images) to be included;
  • Styles: Css style definitions specific to the elements in the widget. Be smart and use unique identifiers and limited scope;
  • Editor script: control the display of the widget (informative, not interactive) in the template editor when user places widget onto a template.

Typical widget script outline

  1. scriptreferences (local or remote javascript files);
  2. register inputs (to enable widget properties to be handled as input and linked to fields in the Data Model);
  3. register events (to enable your defined events on the Events tab as operational events in the script);
  4. script module block (javascript that will be added to the html once to be re-used);
  5. startup script block (javascript that is executed just after html is rendered, to initialize an instance of a widget);
  6. submitscript block (javascript that is executed on submit);
  7. unloadscript block (will be invoked after the request/post gets new response from server but before the new HTML has been rendered, use to cleanup JavaScript instances);
  8. Html (to present visible user interaction and presentation, also the hidden input elements to store and update values linked to fields in data model).

Unique ID

A widget can be placed on a page more than once by actually placing it more than once or having it inside a repeating element. To distinguish between different instances of a widget, it needs a unique identifier within the html-DOM.

  • A page-level unique id can be created using the function OutputId().
  • A specific unique id for instance of a property on page (useful for input fields): OutputId(@Property).

Linking input fields in Widget to WEM Data model

  • add property of type Data Field;
  • Enable option ‘Data field must be writable’;
  • Register as input: register input @DataField;
  • Add html input field (hidden or other), like: <input type="hidden" name="<?attr OutputId(@DataField) ?>" id="<?attr OutputId(@DataField) ?>" value="<?attr @DataField ?>" />
  • Change value by javascript or user input.

Events

  1. Create an event (name and event script);
  2. In Widget Script tab, add register event @EventName;
  3. In the javascript where this event should be triggered, add <?raw @EventName ?>

Typical WEM-events are:

  • execute @Flowchart
  • follow @Exit
  • navigate @NavigationItem

Typical example for an event, using properties:

  • @EventType: Dropdown options “FollowExit”, “ExecuteFlowchart”, “NavigateTo”, “RefreshScreen”, “DoNothing”
  • @Flowchart: user can select a flowchart when configuring the widget
  • @Exit: user can select an exit when widget is on a template which has exits

Eventhandler script:

if @EventType = "ExecuteFlowchart" 
    execute @Flowchart 
elseif @EventType = "FollowExit" 
    follow @Exit 
elseif @EventType = "NavigateTo" 
    navigate @NavigationItem 
end

Register event (only when it is not chosen as DoNothing):

if @EventType <> "DoNothing" 
    register event @EventName 
end

Trigger event:

function triggerEvent() {
    <?raw @EventName ?>
}

Editor script

Example to create an element to display information about the widget on the Template:

var element = utility.createElement("div"); 
var icon = utility.createElement("img"); 
icon.src = resources.Icon; 
icon.style.margin = "0px 4px 0px 1px"; 
element.appendChild(icon); 
element.appendChild(utility.createTextNode("Some information about the widget here.")); 
element.style.color = "black"; 
element.style.backgroundColor = "#eee"; 
element.style.border = "solid 1px #ccc"; 
element.style.fontSize = "0.9em"; 
element.style.padding = "5px"; 
element.style.cursor = "pointer"; 
return element;

WEMScript for Widgets

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.

while

General usage:

while <expression>
    statements...
end

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.

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 dossieritem. This enables two-way binding. General usage:

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

The most basic use is register input <dossieritem>. This can followed by some of the following (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 is annotation is only valid for number and duration dossieritems. 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 dossieritems. It states that it expects that the user specifies both the date and the time.
  • unit – this annotation is only valid for duration dossieritems. Possible values are day, hour, minute, and second.
  • invariant culture – this annotation is only valid for number, duration and datetime dossieritems. 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.

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

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

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.

follow

The follow statement can only be used in an event handler script. 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 an event handler script. 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.

navigate to

The navigate to statement can only be used in an event handler script. 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 an url.

scriptmodule

The scriptmodule statement can only be used in render scripts. 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.

scriptreference

The scriptreference statement can only be used in render scripts. 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 block will be invoked after the whole HTML has been drawn. You can use this code block to initialize instances in JavaScript. It can only be used in render scripts.

The general usage is:

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

submitscript

The submitscript block will be invoked before the request has been made. For example after a button click. You can use this code block to alter the input data before it will be send with the request. It can only be used in render scripts.

The general usage is:

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

unloadscript

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. It can only be used in render scripts.

The general usage is:

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

once

The once statement can only be used in render scripts. 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 once. You should not use this keyword to add scripts. Use the scriptmodule, scriptreference or startupscript statement instead.

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 dossieritem. Usage:

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

/* Returns the output Id for a dossieritem within the current context */
OutputId([MyDossierItem])

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 @DossierItem exists and is bound to a text dossieritem.

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

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

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

range of

The range of keyword returns the range of a concept (or concept set) dossieritem. 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) dossieritem. For the following example, assume that @ConceptSet is a property that is bound to a concept set dossieritem:

<? 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 dossieritem. It returns null if the value of the given dossieritem 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 dossieritem:

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

arrays

Array variables are now 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 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:

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

If the index is out of bounds, then the result will be unkown. You will never get an error.

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

viewstate

The viewstate is now accessible and modifiable in the browser. Runtime.js now has an API that widgets can be use to read and modify the viewstate. This will greatly improve 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 now define fields in the widget editor. The type of a field is restricted to one of the following types:

Text, RichText, Number, Boolean, Date and Duration

The options "Store in view state" and "Default value" are obsolete do not change the behavior of a field. These options will be removed in the next version. A field is always stored in the view state and can not have a default value.

 +31 (0)20 240 21 20       

Videos and images courtesy of WEM Modeler. © 2019. WEM Modeler.
SitemapPrivacy - Terms - Company

 +31 (0)20 240 21 20       


Videos and images courtesy of WEM Modeler.
© 2019 WEM Modeler.
SitemapPrivacy - Terms - Company