An introduction to WEM Widgets and WEMscript, used to create 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.
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. WEM Widgets are little bits of custom coded elements.
WEM comes with a wide range of widgets that you can freely use in your project. If you need a specific widget that is not part of WEM, you can create that widget yourself - if you understand javascript at least.
A widget is written in WEMScript for hooking it into the rest of the WEM functionality. The intended functionality is typically written in JavaScript to run in the browser. 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.
If you want to create your own custom widgets.
Go to this Forum Post to find: ExampleWidgetsLibrary.zip Library with some working example widgets, so you can review and learn the code.
BasicWEMWidgetExample.zip Basic WEM Widget Example with explanation of the structure and the code.
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; these are files that will be included in the package and also in the published resources when projects using the widget are published. So files need to be as small as possible;
Styles: CSS style definitions specific to the elements in the widget. LESS can also be used if you are familiar with it. Be smart and use unique identifiers and limited scope;
Editor script: define the display of the widget (informative, not interactive) in the Modeler Template Editor when user places widget onto a template.
scriptreferences
: referring to local or remote JavaScript files - local files are preferred, because remote files may become unavailable in future or may conflict with other widgets after updates - local files will keep all resources you need for the widget to work available and under control;
register inputs
: to enable widget properties to be handled as input and linked to fields in the Data Model;
register events
: to enable your defined events on the Events tab as operational events in the script;
script module
block: JavaScript that will be added to the html once to be re-used;
startup script
block: JavaScript that is executed just after html is rendered, to initialize an instance of a widget;
submitscript
block: JavaScript that is executed on submit;
unloadscript
block: JavaScript that will be invoked after the request/post gets new response from server but before the new HTML result is rendered, use to cleanup JavaScript instances;
Html
(to present visible user interaction and presentation, also the hidden input elements to store and update values linked to fields in data model).
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)
.
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 ?>" />
Update the value of this input-field linked to WEM Field using JavaScript in the widget-code.
Create an event (name and event script);
In Widget Script tab, add:
register event @EventName
;
In the JavaScript widget-code 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 like “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;
Register event (only when it is not chosen as DoNothing):
Trigger event:
If an EventType is selected by user (like a FollowExit or ExecuteFlowchart) but there is no Exit specified to follow, or a Flowchart to Execute, there will be a Page Refresh.
Using the Editor Script, you can define the display of the Widget when a User places the Widget onto the Template Canvas in the WEM Template Editor.
One good option would be to create an example with your widget, run it in Preview, make an image of the rendered result, add the image to the Resources (as PreviewImage) and use that Resource in this Editor Script:
The widgetPreview CSS classname is defined in the Styles tab, for example:
Coming soon...
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.
General usage:
The elseif
and else
parts are optional. The expressions of the if
and elseif
statements should return a boolean (true/false).
General usage:
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.
There are two ways to declare a variable:
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
.
Array variables are available in WEMScript. Array variables are declared as follows:
The type array<T>
is generic, but T
can itself not be an array type. The following declaration is not supported:
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:
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:
You can use the remove
statement to remove values from an array:
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()
.
You can initialize an array by using the list()
function.
Use parentheses to get the value at a specific index. The index is one-based (meaning the first element has index value of 1):
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:
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:
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:
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 statements can only be used in render scripts. The general usage is:
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.
General usage:
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:
When looping through a concept set, you can use the keyword concept
to reference the current concept within the loop.
The follow
statement can only be used in the event-handler script of a Widget.
The general usage is:
This statement will terminate the script and follow the given (button) exit.
The execute
statement can only be used in the event-handler script of a Widget.
The general usage is:
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:
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).
The scriptmodule
statement can only be used in the render-script of a Widget.
The general usage is:
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).
The scriptreference
statement can only be used in the render-script of a Widget.
The general usage is:
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.
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:
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:
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:
The once
statement can only be used in the render script of a Widget.
The general usage is:
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.
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:
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.
When the custom control properties are resolved, it will become something like this:
The range of
keyword returns the range of a concept (or concept set) data-field (single-select or multi-select). Usage:
This is an expression that returns a concept set. You can use this construct as part in a bigger expression:
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:
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:
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:
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.
You can define Viewstate fields in the Viewstate tab. The type of a field is restricted to one of the following:
WEM Widgets Extension: Placeholder for content elements
The Content Placeholder in Widgets provides a way to let WEM-Users include WEM Template Editor content and elements within a Custom Widget - where the Placeholder in the Widget will be replaced with all content added to the Widget in the WEM Template. With this powerful feature you can nest Widgets and Template Content in a way which is similar to other container-elements in the Template Editor (panels, alerts, repeaters etc.).
Select the "Placeholders" tab in the Widget Editor;
Click on "New placeholder" in the tabbar;
Enter a name (which must be unique within the collection of placeholders for this specific widget);
Press "Create";
Optionally, you can provide a label and a description; this information can be displayed in the Template Editor, in the "preview" of this widget where it is added to the Template.
To display the content (that will be added in the WEM Template Editor when using this widget) of the Content Placeholder at Runtime, you need to use the render
statement in the Widget WEMScript (2nd tab). You can see the available Placeholders (and other properties) in the tree to the left. From here you can drag the placeholder onto the WEMScript canvas.
You can even use the render statement within HTML elements and even in loops, for example to create placeholders within an HTML Bullet-List or Numbered-List Element based on a number of items in a runtime-list:
If you provide NO Template Editor Script (to show your specific content when Widget is placed on a Template Canvas), then the WEM Default Widget-Preview will be used to display your Widget in the WEM Template Editor, this will actually be good enough for most standard situations. It will automatically show the Placeholder's Label and Description value if provided - and will show the specific space where custom content can be dropped:
If you want to change the way your Widget is displayed in the WEM Template Editor, you can create your own custom Widget-Preview using the Template Editor Script. In this case, the Content Placeholders need some special care, so we added the createPlaceholderElement
utility function and the placeholders
object. In the tree to the right, you can see the available utility functions and other elements that you can use in this script. The createPlaceholderElement
and the placeholder's properties can be inserted into the script at the cursor position by double-clicking on the elements in the right-hand tree (to avoid typos...).
The screenshot below misses the name, label and description properties in the tree, but that will soon be available!
With these 3 steps, you've created a Widget with a Placeholder. These are the basic steps to take to enable the Placeholder feature in your Widget.
Next screenshots show the "Simple Content Placeholder Example" widget in use on a WEM Template. An Alert Element has been added and a Label showing how many records in a list is available. In the Runtime you'll see the Alert and the actual number of records in the Products list.
Below we provide a collection of basic examples (sources) of Widgets with Placeholders, a "simple" example like described on this page, and examples that mimic the standard Alert, Datagrid, Panel and Repeater elements, just to give a few basic ideas of how this feature can be used.