Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
A datetime
type represents a single moment in time in a platform-independent format.
When performing a postback, the WEM runtime expects the datetime
to be in ISO date format. You can use the FormatDate()
function with the Iso8601
constant as the second argument to convert the datetime
to this format.
2025-12-31
2025-12-31 23:59
2025-12-31 23:59:59
2025-12-31 23:59:59.999999
An optional "T" can be included between the date and time parts, as in "2025-12-31T23:59". Additionally, you can add timezone-specific parts such as "Z", "+2:00", or "-5:00", as in "2025-12-31T23:59-5:00". Be aware that if you include a timezone part, the WEM runtime will convert the time to local time. For example, a datetime
of "2025-01-01T01:00:00-2:00" will be converted to "2025-01-01T03:00:00". You can test this with the text datetime input field widget below.
This is straightforward. The only requirement is to convert the datetime
value of @DateTimeProperty
into the correct format using FormatDate(@DateTimeProperty, Iso8601)
.
This example is almost identical to the text datetime input field example. The only difference is that we set type="datetime-local"
on the input field. Since the browser adheres to the same datetime specifications, we can again use FormatDate(@DateTimeProperty, Iso8601)
.
Unfortunately, the browser is a bit stricter in this case. Since we do not need to consider time, we set type="date"
on the input field. However, we cannot use Iso8601
as the second argument for FormatDate()
. The reason is that the browser we tested does not accept a value that includes time. For instance, if you have value="2025-01-01T23:59:59"
, the entire value will be ignored. Fortunately, we can provide our own format in the FormatDate()
function, as demonstrated in the widget example above: FormatDate(@DateProperty, "yyyy-MM-dd")
.
Using a datetime
type within a JavaScript context is quite simple. By printing it with the JavaScript encode <?js
, you will receive a new Date()
object in return. For example, if we have a @DateTimeProperty
and we want to log the year from that datetime in JavaScript:
This will result in:
A conceptset
type holds a set of items of type concept
. This conceptset
has no predefined range and accepts any concept
, which distinguishes it from a concept data field that does have a range.
In this example, we register input
for our @ConceptSetProperty
, and we store the output ID in a variable called @id
. This is important because all checkboxes must share the same name according to HTML specifications. Next, we loop
through the range of @ConceptSetProperty
to generate the checkboxes. We set the name to @id
, the value to ConceptId(concept)
, and check if @ConceptSetProperty contains concept
. If it does, we add the "checked" attribute to the checkbox. After rendering the checkbox input, we display the name of the concept using <?= concept ?>
.
In the background, during a postback, we send back a list of IDs that will update the data model bound to @ConceptSetProperty
.
The following example demonstrates a less user-friendly UX for updating a concept set. However, it illustrates the concept of updating a concept data model with a postback using a single input field—in this case, a text field.
This widget renders all the concepts along with their concept IDs from the range of @ConceptSetProperty
. When @ConceptSetProperty contains concept
, that concept will be included in the value of the text field. The result will be a comma-separated list of IDs, e.g., 1,23,400
. To update the concept set, you simply modify the text value, which holds the IDs that are part of this concept set. Note that the format is very strict; for instance, the value 3,70,
with an additional trailing comma at the end is not accepted by the WEM runtime and will result in an empty concept set.
Creating a widget to upload a file is relatively straightforward. It can be as simple as the following two lines of code, along with including a @File
data model property field:
However, it becomes a bit trickier when working with dynamically created binary data instead of a static file on the desktop. Nowadays, in JavaScript, you can only create a binary file programmatically because browsers have become more restricted due to security measures. Additionally, you can only assign a file to a file input through user interaction, such as a button click.
Let’s first look at how to do it the traditional way, and then follow that up with a more modern approach.
If you examine the code below, you may wonder where the base64
option in the register input
comes into play. The reason for this is legacy. Previously, JavaScript did not have an out-of-the-box solution for creating native files; it only offered emulation using a string or an array of numbers. This is how WEM could provide a way to upload files in base64 format. Let’s examine our script below using base64
as an option:
Looks pretty cool, right? You simply encode the "Hello" text, and you're good to go. However, be aware that several issues can arise if you're not careful; we are using a simple "Hello" text as an example. Let’s walk through the process.
First, we add base64 = true
to our register input
, which is straightforward. However, note that we changed our input to a text field using <input type="text">
. We are no longer working with files but rather with text that simulates a file. This can become cumbersome when dealing with large files, especially when using the btoa()
function to encode the string into base64 format. Additionally, encoding data into base64 increases the size of the contents by approximately 35%.
Another important point is that we are using a string to hold our "Hello" data. In JavaScript, this string is in Unicode. However, Base64, by design, expects binary data as its input. In terms of JavaScript strings, this means that each character's code point should occupy only one byte. If you pass a string like "a Ā 𐀀 文 🦄"
into btoa()
, which contains characters that occupy more than one byte, you will encounter an error.
Finally, what happens when we call clickHandler()
directly without user interaction? Since we are using a regular text field to set its contents with a string value, there is no error in that case.
What does a modern version look like without using the base64
option?
In this version, we have removed the base64
option from our register input
, and we now have a <input type="file">
again. However, our JavaScript code now utilizes several new classes that we didn't have before, such as Uint8Array
, Blob
, File
, and DataTransfer
, each serving its own purpose. By reading the comments in the code above, it becomes clear how to programmatically create a file containing binary data and how to set the file input with that file.
The concept is a special kind of type. The values of a concept are not known within the context of a widget. However, if you create a generic widget, you don't need to know them. Let’s take a look at the following WEMscript to illustrate this.
Throughout the examples, we will assume we have a concept data model property called ConceptProperty
.
Notice that everything works except for the assignment of the 'Colors'.'Red'
concept literal, which is unknown in the context of a widget and only recognized within a project. To assign a concept data model property a new value, we use arbitrary IDs by calling the ConceptId()
function. Fortunately, these IDs are just integers and can be easily passed around in the code.
Now, let’s look at the following dropdown example widget that uses a range of concepts to set the @ConceptProperty
:
In the above example, we have created a writable concept data model property called @ConceptProperty
. The context of this concept is unknown to us; it could represent a range of colors, animals, or the status of a ticket. However, in this case, we do not need to know the specific context. We obtain the range of @ConceptProperty
using the range of
keyword and enumerate through it with the loop
. We create the <option>
elements where the values are generated using the ConceptId(concept)
function, with concept
being a special keyword that refers to the current enumerated concept within the range of @ConceptProperty
. By comparing concept
with @ConceptProperty
, we can determine if the latter holds the value of concept
, allowing us to select the appropriate option.
A duration
type represents a measure of the interval between two events or points in time in a platform-independent format.
Let's assume we declared var @d: duration
, which holds a duration of '1d23h45m6s'
—representing 1 day, 23 hours, 45 minutes, and 6 seconds.
Be cautious! As you can see, when printing a variable of type duration
, only the time part is displayed, not the date part. Although we specified 1 day in our variable @d
, it is not printed.
There is no native "duration" type in JavaScript. However, when printing the variable as a JavaScript number, it is represented as a Number
type in ticks, which is very precise. One tick represents 100 nanoseconds, and there are 10,000 ticks in a millisecond. Be careful when converting ticks to other units:
Depending on the situation, you may want to use Math.round()
on the numbers.
In the example above, we register input
for @DurationProperty
with unit = day
. This informs the WEM runtime that during a postback, a number representing the number of days will be sent. To convert our duration
to days, we use FormatDuration(@DurationProperty, "days")
, specifying "days"
as the second argument to indicate the desired unit. If we want the input field to hold minutes instead of days, we change it to register input @DurationProperty unit = minute
and use FormatDuration(@DurationProperty, "minutes")
.
There are numerous ways to format a duration. The following outputs are generated when we have a var @d: duration
with a value of '12d34h56m1s'
:
Be aware that in some situations, the duration may be truncated. For instance, when using "clock"
, only the hours and minutes are shown. However, when formatting to "seconds"
, the entire duration is represented in seconds.
<?= @d ?>
23:45:06
<?attr @d ?>
23:45:06
<?raw @d ?>
23:45:06
<?js @d ?>
50250000000
FormatDuration(@d, "days")
13
FormatDuration(@d, "hours")
322
FormatDuration(@d, "minutes")
19376
FormatDuration(@d, "seconds")
1162561
FormatDuration(@d, "long")
13 dagen, 10 uur, 56 minuten en 1 seconde
In Dutch language
FormatDuration(@d, en_US)
13:10:56:01
FormatDuration(@d, nl_NL)
13:10:56:01
FormatDuration(@d, "clock")
10:56
FormatDuration(@d, "time")
10:56
FormatDuration(@d, "stopwatch")
10:56:01
A boolean variable can be set using the keywords true
, false
, unknownboolean
, or a WEM expression that returns a boolean type.
When performing a postback, it is essential to send the text "true", "false", or an empty string "" to update the property to the respective values of true
, false
, and unknownboolean
. This requires careful attention when sending data back.
Let’s examine some examples and analyze them.
All examples feature a writable boolean data model property called BooleanProperty
.
Aside from the poor user experience, this approach does not work because when the portal language is set to English, <?attr @BooleanProperty ?>
will output "yes" when @BooleanProperty
is true
and "no" when it is false
. These outputs are invalid boolean values for a postback. As a workaround, you could use <?js @BooleanProperty ?>
instead, as it will output true
, false
, and unknownboolean
as "true", "false", and "null" respectively. However, "null" is an invalid value, so it will be interpreted as unknownboolean
. But please keep this workaround confidential.
Since we typically do not create a boolean text field, let’s explore more common boolean input fields.
In this example, we register the input @BooleanProperty
and create three radio buttons with the text values "true", "false", and an empty string "" to set the value to unknownboolean
. We check the current state of @BooleanProperty
and add the checked
attribute if it corresponds to the option value. Note that if @BooleanProperty
is unknown, checking whether it is true will always return false
, so we do not need to use IsKnown(@BooleanProperty)
for the "true" and "false" options.
This checkbox will set @BooleanProperty
to either true
or unknownboolean
. This behavior is due to how the browser sends post data. If the checkbox is not checked, the browser will ignore it, resulting in the property assigned to this checkbox being set to unknownboolean
. This may not always be the desired outcome, so let’s look at an improved version to address this issue.
In this example, we introduce a hidden input field that will be used to send the data back, along with an intermediate checkbox to read the current checked state in the submitscript
. Depending on whether the checkbox is checked, we set the hidden input field to "true" or "false".
Regardless of the type of widget you are building, even a simple boolean can become complicated if not carefully considered.
A text
type variable is used to represent and manipulate a sequence of characters.
Writing a widget that uses a text
property is quite straightforward and is likely the easiest type to implement.
You don't need to do anything special when using the text
type in JavaScript, as it is similar to the String
type in JavaScript.
Depending on the encoding format, different outputs are generated. Let's assume a variable var @s: text
holds the value ">>> Stan Laurel & Oliver Hardy <<<"
. The following outputs will be generated:
A number
type variable holds a numeric value in literal forms such as 255
or 3.14159
.
To create a widget that performs a simple postback, you would write something like this:
In the example above, we created a simple text field that outputs the value of @NumberProperty
. This example is culture-independent, and the WEM runtime checks the culture settings of the portal, using the appropriate number format for validation. For instance, if you set the culture settings to Dutch, the number will be formatted as 200.000,00
. The WEM runtime will use that format for output and validation.
However, this can lead to issues in other contexts, such as JavaScript, where numbers are written in an invariant culture format like 200000.00
. We will discuss this further shortly.
If you have a number that requires exact precision, you can easily achieve this with the precision
option:
There are cases where you may want to work with an invariant culture, especially in JavaScript. This can be accomplished by adding the invariant culture
option. This way, the WEM runtime knows to expect number values that are posted back in the format 200000.00
(using the dot as the decimal separator), making it easier to work with in JavaScript.
However, note that encoding with <?=
, <?attr
, and <?raw
will still output according to the current culture settings, ignoring the invariant culture setting. To clarify: The invariant culture
option is only for postback, so the WEM runtime knows to expect a number value in that format.
The table below provides an overview of the results of different kinds of encoding when outputting a variable declared with var @n: number
, which holds the value 123.45
.
When you have a widget number property @NumberProperty
, which you register with register input
, and you want to perform a postback with the value 123.45
, the format you need to use will depend on the culture settings:
A richtext
type variable is used to represent and manipulate a sequence of characters that holds rich content.
When you concatenate a text value to a richtext variable, implicit encoding is applied to the text value. This is not always desired; therefore, the ToRichText()
function can be used. Let’s dissect the example above.
This line declares the variable @rt
as richtext
.
Since the left-hand side @rt
is of type richtext
and the expression on the right-hand side is a literal text, the text is treated as richtext. The value of the variable @rt
is now <strong>This text is very
.
Once again, the left-hand side @rt
is of type richtext
, but the expression on the right-hand side is a concatenation of richtext
+ richtext
, resulting in the value <strong>This text is very strong</strong>
.
What happens if we don't use the ToRichText()
function?
In this case, the expression is of richtext
+ text
. When evaluating the right text
operand, it will be coerced into richtext
. During this conversion, the text value will be HTML encoded, resulting in <strong>This text is very strong</strong>
. This is not what we wanted in this example, as it generates malformed HTML.
So, be cautious when concatenating with richtext
.
Fortunately, performing a postback with richtext is quite straightforward. You don't have to deal with localization, and updating the property with a new value is simply a matter of sending it back as is.
Since we are dealing with richtext
, the encoding is mostly ignored, except for JavaScript encoding. Let’s assume we have a variable var @rt: richtext
that holds the value <p>I am a paragraph.</p>
. The following encoding (or lack thereof) will produce the corresponding outputs:
<?= @s ?>
>>> Stan Laurel & Oliver Hardy <<<
<?attr @s ?>
>>> Stan Laurel & Oliver Hardy <<<
<?js @s ?>
">>> Stan Laurel & Oliver Hardy <<<"
<?raw @s ?>
>>> Stan Laurel & Oliver Hardy <<<
<?= @n ?>
123.45
123,45
123,45
<?attr @n ?>
123.45
123,45
123,45
<?js @n ?>
123.45
123.45
123,45
<?raw @n ?>
123.45
123,45
123,45
123.45
123,45
123,45
123.45
<?= @rt ?>
<p>I am a paragraph.</p>
<?attr @rt ?>
<p>I am a paragraph.</p>
<?js @rt ?>
"<p>I am a paragraph.</p>"
<?raw @rt ?>
<p>I am a paragraph.</p>