In ZK bind annotation, we adopt EL expression to specify a binding target and reference an implicit object. The binding target is mostly a ViewModel's (nested) properties. You can use EL expression in a ZUL which is described in the section ZK Developer's Reference/UIComposing/ZUML/EL Expressions. But using EL in ZK bind annotation is a little bit different in format and evaluation.
All ZK bind annotation has the general format:
@[Annotation](value=[EL-expression], [arbitraryKey]=[EL-expression])
It starts from a "@" symbol followed by an annotation's name like “id” or “bind”. Inside parentheses we can write multiple key-value pairs separated with a comma. The key is a self-defined name (not an EL expression), and it's like a key in a Map. The value is an EL expression but is not enclosed with "${" and "}". The default key name is “value”. If you only write a EL expression without specifying its key name, it's implicit set to key named “value”. Hence we usually omit this default key name when writing ZK bind annotation. In most case, we can just write a annotation as follows:
@[Annotation]( [EL-expression])
We just need to write one key-value pair and omit default key name. We often use multiple key-value pairs for passing parameters to command, converter, and validator.
Although all ZK bind annotation has the general format, the way how a binder parse and evaluates the EL expression is different among different annotations. We'll describe the differences in the following sections.
A binder usually evaluates an EL expression each time when it wants to access the target object. Hence The evaluation result might be different from time to time.
<button label="Cmd" onClick="@command(vm.checked ? 'command1' : 'command2')" />
<groupbox visible="@load(not empty vm.selected)" />
- When clicking the button, the binder executes a command upon value of “vm.checked”.
<label value="@bind(vm.person[vm.currentField])"/>
- If evaluation result of
vm.currentField
is firstName, the binder loadsvm.person.firstName
. So which property ofvm.person
that a binder loads depends onvm.currentField
's evaluation result.
You can use EL expression to call a method in a ViewModel.
<label value="@load(vm.concat(vm.foo, 'postfix'))"/>
We could call tag library's methods in a zul with EL expression equally, we can also call them in a data binding expression and this calling can be nested. The syntax is as follows:
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
...
<label value="@load(c:cat('A-', vm.value))" />
<label value="@load(c:cat3('$', c:formatNumber(vm.price, '00'), ' per person'))" />
- Line 1: Declare the tag library for core methods.
- Line 3: core's cat method.
- Line 4: Nested calling with core's cat3 method and formatNumber.
The xel method allows developers to call a Java class's static method in EL expression without defining a tag library. You can also use this feature in data binding expression
<?xel-method prefix="x" name="max"
class="java.lang.Math"
signature="int max(int,int)"?>
...
<intbox value="@bind(vm.value1)"/>
<intbox value="@bind(vm.value2)"/>
<label value="@load(x:max(vm.value1, vm.value2))"/>
Some EL characters are illegal in XML attribute or ZK annotation, you should replace them with other EL operators.
Character | Replacement |
---|---|
= | eq |
!= | ne |
&& | and |
< | lt |
<= | le |
> | gt |
>= | ge |
For example: |
<image src="@load(vm.picture ne null ? 'images/'.concat(vm.picture) : 'images/NoImage.png')"/>
<label value="@bind(vm.age lt 18 ? 'true' : 'false')"/>
Since ZK 8, data binding supports some syntaxes of Java EE 7 Expression Language 3.
<label value="@load(('Hi, ' += vm.firstname += ' ' += vm.lastname))" />
Remember to enclose string concatenation with 2 extra parentheses.
<label value="@load((“increase” = x -> x + 1; “increase”(5)))" />
It will create a new bean “increase” for the function and show 6 as label’s value.
A converter can be implemented with a lambda expression defined in zul. Take conversion from meter to inch as an example,
<textbox value="@load((x -> (x * 100) / 2.54)(vm.value))"
onOK="@command('click', key=((x -> (x * 2.54) / 100)(self.value)))" />
The syntax used is same as ones in Java SE 8 and behaves like an anonymous function which is discarded after evaluated.
We can name a lambda and evaluate indirectly like the example mentioned above.
Use collection operations to implement the name filter.
<listbox model="@load((vm.names.stream()
.filter(x -> x.contains(vm.filter))
.toList()))">
We create a string list, vm.names
as listbox’s model so we can turn collection objects into steam. These operations can then be chained together to form a pipeline.
We can also create a list by collection construction,
<label value="@load(([1, 2, 3, 4].stream().sum()))" />
The label will show 10 as the result.
A static field or static method of a Java class can be referenced with the syntax Classname.Field, such as
<label value="@load((Math.sqrt(16)))" />
You need to import required pakcage/class by import directive.
Note that java.lang.*
is imported by default.