Skip to main content

Form Expressions

Outdated documentation page

This is an old version of the documentation for Flowable until version 3.13 and for the Angular-based Flowable Design 3.14/3.15. If you are running the latest version of Flowable please check out the current version of this page.

Expressions are calculations that flowable forms can do for you. You can use expressions in most of the attributes used in form components. This allows you to bind components together and define form behavior.

Expressions are wrapped in double curly braces {{ and }} and the engine will try to calculate them.

For example, you can hide or disable a component when a check is checked:

Loading...

Valid expressions

You can use any value from the payload, literals (numbers, strings and booleans) and (additional data) inside an expression.

To access attributes of objects you can use the dot operator object.attribute.

To access items in an array you can use brackets array[index].

{{true}}
{{5}}
{{"hello"}}
{{disabled}}
{{$currentUser.id}}
{{$currentUser.memberGroups.has('flowableUser')}}

You can use logical operators

{{!disabled}}
{{age > 5}}
{{length == 5}}
{{length <= 2}}
{{disabled || invisible}}
{{alive && kicking}}

Mathematical operators

{{age + 5}}
{{age - 1}}
{{age / 2}}
{{age * 2}}
{{age % 2}}

String concatenation

Hello {{name}} {{surname}}, have a nice {{dayphase}}
{{name + " " + surname}}

Conditionals

{{age < 16 ? 'child' : 'adult'}}

You can also use Ecmascript methods and attributes of the objects resolved in expressions. For example:

{{case.dueDate.toLocaleDateString()}}
{{tweet.length}}
{{names.join(", ")}}
{{colors.indexOf['red']}}

Pipe operator |> (It executes the function on the right side passing the parameter from the left side)

{{date2 |> flw.formatTime}}
{{date2 |> flw.formatDate}}

And all of them mixed

{{user.age + 1 == 21 && !user.admin ? "Next year you can become an admin" : user.admin ? "admin" : "go away " + user.name }}

Scope

Input components assign their value into the payload. For example, if a text component has value {{name}} when the users writes Jordan in that input, the payload will get an attribute name with value Jordan. The expression in the value attribute of an input component it's a special expression that doesn't allow operators.

Most container components, like panels and subforms allow you to define a value too. This scopes all the values in the components inside the container. For example, if you define this form:

panel: value={{address}}
text: value={{name}}
text: value={{number}}

When the user fills in the name with Baker st and the number with 221B the payload will become:

address: {
name: "Baker st",
number: "221B"
}

Components can write data in the payload in their scope and their children's scope but not in their ancestors'.

To access and edit data from its ancestors' scope, components can use the special keyword $payload that provides the whole payload. See this complete example:

checkbox: label="Show surname" value={{showSurname}}
panel: value={{client}}
text: value={{name}}
text: value={{surname}} visible={{$payload.showSurname}}

In some cases is necessary to access to the parent scope.

In the following example we want to ensure children have +14yo when their families didn't contract the nursery service. To do that we use the $itemParent keyword to access the parent scope of the component.

subform: value={{families}} [[multipleEntries=true]]
boolean: label=Nursery service value={{nursery}}
subform: value={{children}} [[multipleEntries=true]]
number: label=Age value={{age}} [[min={{$itemParent.nursery ? undefined : 14}}]]

$itemParent is read-only, so it can't be used to alter parent scope variables.

$temp variables

While designing a form it's sometimes useful to use variables that we don't want to store with the payload. In the previous example, we might not want to keep the showSurname variable. So we could do:

checkbox: label="Show surname" value={{$temp.showSurname}}
panel: value={{client}}
text: value={{name}}
text: value={{surname}} visible={{$payload.$temp.showSurname}}

$temp variables are kept with the payload and can be used as any other attributes in the payload.

note

When integrating Flowable Forms there is a utility offered to remove the $temp variables. Please see the developer guide for further information.

Special expression values

There are some special expression:

{{$payload}}

Root path used to access any payload value (e.g. {{$payload.mySubform.myText}})

{{$formValid}}

Readonly boolean value containing whether the form has errors or is valid.

Component specific

Some components provide more data that you can use, for example, $item and $searchText in the select component.

flw functions

There are some utility functions also available inside expressions.

In the following examples we will use data from this payload:

{
order: [
{ name: "Macbook Pro", amount: 11, price: 2631.32, category: "laptop" },
{ name: "Samsung 27''", amount: 4, price: 304.12, category: "peripheral" },
{ name: "Pack keyboard + mouse", amount: 11, price: 124.01, category: "peripheral" }
],
extraLines: [
{ name: "Handling", amount: 1, price: 100.0, category: "extra" },
{ name: "Shipping", amount: 1, price: 120.0, category: "extra" }
]
}

List

NameDescriptionExample
mapAttrExtracts an attribute from every object in the listflw.mapAttr(order, 'name') = ["Macbook Pro", "Samsung 27''", "Pack keyboard + mouse"]
findGets the first object from the list with an attribute valueflw.find(order, 'category', 'laptop') = {name: "Macbook Pro", amount: 11, price: 2631.32, category: "laptop"}
findAllGets every object from the list with an attribute valueflw.findAll(order, 'category', 'peripheral') = [{ name: "Samsung 27''", ... }, { name: "Pack keyboard + mouse", ... }]
mergeConcatenates two listsflw.merge(order, extraLines) = [ { name: "Macbook Pro", ... }, { name: "Samsung 27''", ... }, { name: "Pack keyboard + mouse", ... }, { name: "Handling", ... }, { name: "Shipping", ... } ]
addAdds an element to a listflw.add(order, extraLines[0]) = [ { name: "Macbook Pro", ... }, { name: "Samsung 27''", ... }, { name: "Pack keyboard + mouse", ... }, { name: "Handling", ... } ]
remove.byAttrRemoves all elements with an attribute valueflw.remove.byAttr(order, 'category', 'peripheral') = [ { name: "Macbook Pro", ... } ]
remove.byPosRemoves the element by it's position in the listflw.remove.byPos(order, 0) = [{ name: "Samsung 27''", ... }, { name: "Pack keyboard + mouse", ... }]
remove.byObjRemoves the object from the listflw.remove.byObj(order, order[2]) = [ { name: "Macbook Pro", ... }, { name: "Samsung 27''", ... } ]
remove.nullsRemoves the null elements from the listflw.remove.nulls([0, NaN, undefined, null, "", "by"]) = [ 0, "by" ]
inReturns true if the element is in the listflw.in(flw.mapAttr(order, 'category'), 'laptop') = true
keysReturns an array of strings where each item is a key of the provided objectflw.keys(order[0]) = name,amount,price,category
valuesReturns an array where each item is a value of the provided object attributesflw.values(order[0]) = Macbook Pro,11,2631.32,laptop
exists v3.10.3+Returns true if value is not nullflw.exists(order[0]) = true
notExists v3.10.3+Returns true if value is nullflw.notExists(order[0]) = false
forceCollectionSize v3.14+The function takes in three parameters: the collection itself, the desired size, and an optional element. If the collection has more elements than the specified size, it will be truncated to that length. Any elements beyond the size parameter will be removed. If the collection is shorter than the requested size, it will be padded with the optional element until it reaches the desired sizeflw.forceCollectionSize(order, 2) = [{ name: "Macbook Pro", ... }, { name: "Samsung 27''", ... }] flw.forceCollectionSize(order, 5, extraLines[0]) = [{ name: "Macbook Pro", ... }, { name: "Samsung 27''", ... }, { name: "Pack keyboard + mouse", ... }, { name: "Handling", ... }, { name: "Handling", ... }]

Aggregation

NameDescriptionExample
joinconcatenates every item with an optional separatorflw.join(flw.mapAttr(order, 'name'), '; ') = "Macbook Pro, Samsung 27''; Pack keyboard + mouse"
sumsums all valuesflw.sum(flw.mapAttr(order, 'amount')) = 26
dotProdsums the products of corresponding entriesflw.dotProd(flw.mapAttr(order, 'amount'), flw.mapAttr(order, 'price')) = 31525.11
countcounts entriesflw.count(order) = 3
avgAverage valueflw.avg(flw.mapAttr(order, 'price')) = 1019.8166666666667
maxMaximum valueflw.max(flw.mapAttr(order, 'price')) = 2631.32
minMinimum valueflw.min(flw.mapAttr(order, 'price')) = 124.01

Date

NameDescriptionExample
nowcurrent date in UTCflw.now() = 2020-07-29T10:10:33.654Z
currentDatestart of the current day and shifted to UTCflw.currentDate() = 2020-07-29T00:00:00.000Z
secondsOfDaySeconds passed from the beginning of the dayflw.secondsOfDay(flw.now()) = 46354
parseDateConverts a string with a format into a dateflw.parseDate('01_13_18__11_31', 'MM_DD_YY__HH_mm') = 2018-01-13T11:31:00.000Z
formatDate1Formats a date into a stringflw.formatDate(flw.now(), 'DD/MM/YYYY') = 29/07/2020
formatTime2Formats a date using the default time formatflw.formatTime(flw.now()) = 10:10
startOfReturns the start of the date period (year, month, day)flw.startOf(flw.now(), 'month') = 2022-02-01T00:00:00.000Z
dateAddSums an amount of time (years, months, days, minutes, seconds) to the dateflw.dateAdd(flw.now(), 1, 'months') = 2020-08-29T10:10:33.654Z
dateSubtractSubtracts an amount of time (years, months, days, minutes, seconds) to the dateflw.dateSubtract(flw.now(), 1, 'months') = 2020-06-29T10:10:33.654Z
isBeforeReturns true if the first date is before the second one. It admits a third parameter with the precission to useflw.isBefore(flw.currentDate(), flw.now()) = TRUE flw.isBefore(flw.currentDate(), flw.now(), 'day') = FALSE
isAfterReturns true if the first date is after the second one. It admits a third parameter with the precission to useflw.isAfter(flw.now(), flw.currentDate()) = TRUE flw.isAfter(flw.now(), flw.currentDate(), 'day') = FALSE
sameDateReturns true if the first date is the same as the second one. It admits a third parameter with the precission to useflw.sameDate(flw.now(), flw.currentDate()) = FALSE flw.sameDate(flw.now(), flw.currentDate(), 'day') = TRUE

Math

NameDescriptionExample
roundround number to n decimalsflw.round(25.2345, 2) = 25.23
floorround number downwards to the nearest integerflw.floor(26.91) = 26
ceilround number upwards to the nearest integerflw.ceil(23.2345) = 24
absgets the absolute (positive) value of a numberflw.abs(-25.5) = 25.5
parseIntParses a text value into an integerflw.parseInt("14") = 14 (as number)
parseFloatParses a text value into a numberflw.parseFloat("14.8") = 14.8 (as number)
numberFormat3 v3.10.0+Parses a number value into a human-readable string, optionally given a locale and formatflw.numberFormat(123, "es-ES") = 123,00

String

NameDescriptionExample
encodeURIComponentEncodes special characters and characters that have special meanings in URIs like , / ? : @ & = + $ #flw.encodeURIComponent(josé@flowable.com) = jos%C3%A9%40flowable.com
encodeURIEncodes special characters and none of , / ? : @ & = + $ #flw.encodeURI('http://google.com?q=josé@flowable.com') = http://google.com?q=jos%C3%A9@flowable.com
JSON.stringifyGenerates a json string from an objectflw.JSON.stringify([1, 2]) = "[1, 2]"
JSON.parseGenerates a json object from a JSON objectflw.JSON.parse("[1, 2]") = [1, 2]
encode v3.10.0+Serializes a query object to a safe params in the URL
  • flw.encode({ q: "h", a: "b" }) = q=h&a=b
  • flw.encode({ q: "h", a: "b" }, "?") = ?q=h&a=b
  • flw.encode({ q: "h", a: "b" }, "&") = &q=h&a=b
sanitizeHtml v3.10.3+Given a HTML string cleans every possible XSS attackflw.sanitizeHtml("<img src='x' onError='javascript:alert(1)' />") = "<img src='x' />"
escapeHtml v3.10.3+Given a HTML string, escapes the closing brackets for displaying the HTML as stringflw.escapeHtml("<img src='x' />") = "&lt;img src=&quot;x&quot;&gt; "

  1. Format supports Moment.js string format. When it's not provided, it will use locale.timeFormat from translations file or HH:mm
  2. Format supports Moment.js string format. When it's not provided, it will use locale.dateFormat from translations file or YYYY-MM-DD
  3. Accepts locale, styles, fraction digits and currency options defined in the specification of the Intl.NumberFormat API.