ZK10 Preview: Using the new and light Client MVVM"

From Documentation
(13 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
{{Template:Smalltalk_Author|
 
{{Template:Smalltalk_Author|
 
|author=James Chu, Engineer, Potix Corporation
 
|author=James Chu, Engineer, Potix Corporation
|date=May 19, 2022
+
|date=May 26, 2022
|version=10.0.0.FL.20220425-Eval  
+
|version=10.0.0.FL.20220525-Eval  
 
}}
 
}}
  
Line 11: Line 11:
  
 
[[File:ZK_MVVM_Mechanism.jpg|thumb|800px|center|ZK MVVM Mechanism]]<br/>
 
[[File:ZK_MVVM_Mechanism.jpg|thumb|800px|center|ZK MVVM Mechanism]]<br/>
 
 
The new Client MVVM also starts from a ZUL and a ViewModel, and the binding information will be passed to the client-side with ZK widgets. When the widgets are created and rendered on the browser, the bindings will also be created at the same time.
 
The new Client MVVM also starts from a ZUL and a ViewModel, and the binding information will be passed to the client-side with ZK widgets. When the widgets are created and rendered on the browser, the bindings will also be created at the same time.
  
 
[[File:ZK10_Client_MVVM_Mechanism.jpg|thumb|800px|center|ZK 10 Client MVVM Mechanism]]<br/>
 
[[File:ZK10_Client_MVVM_Mechanism.jpg|thumb|800px|center|ZK 10 Client MVVM Mechanism]]<br/>
 
 
As you can see, the binding now happens on the client-side. However, from an application developer's point of view, this change is transparent, and it is something ZK takes care of for you. You'll still be building the MVVM application in the same way, referencing our [https://books.zkoss.org/zk-mvvm-book/9.5/introduction_of_mvvm.html ZK MVVM guide], except for a few differences which we will cover in the following sections.
 
As you can see, the binding now happens on the client-side. However, from an application developer's point of view, this change is transparent, and it is something ZK takes care of for you. You'll still be building the MVVM application in the same way, referencing our [https://books.zkoss.org/zk-mvvm-book/9.5/introduction_of_mvvm.html ZK MVVM guide], except for a few differences which we will cover in the following sections.
  
 
Now, let's take a look at how we can enable the Client MVVM.
 
Now, let's take a look at how we can enable the Client MVVM.
  
== Setup ==
+
== Setting up Client MVVM ==
 
There are two ways to use the client MVVM, either use it against a specific ViewModel, or, set it globally. Either way, you have to first include the jar file and add the listener.
 
There are two ways to use the client MVVM, either use it against a specific ViewModel, or, set it globally. Either way, you have to first include the jar file and add the listener.
  
=== Step 0 ===  
+
=== Step 1 ===  
Use ZK 10 EE-Eval freshly, and include the '''zephyr''' jar file.
+
Use ZK 10 EE-Eval freshly, and include the [https://mavensync.zkoss.org/eval/org/zkoss/zephyr/zephyr-ex/ '''zephyr-ex'''] jar file.
  
=== Step 1 ===
+
=== Step 2 ===
 
Add the listener for Client MVVM. (in zk.xml)
 
Add the listener for Client MVVM. (in zk.xml)
 
<source lang='xml'>
 
<source lang='xml'>
Line 34: Line 32:
 
</source>
 
</source>
  
=== Step 2 ===
+
=== Step 3 ===
 
==== Option 1: Apply to a ViewModel ====  
 
==== Option 1: Apply to a ViewModel ====  
  
Line 48: Line 46:
 
==== Option 2: Set it globally ====  
 
==== Option 2: Set it globally ====  
  
We can change the [https://www.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.bind.defaultComposer.class default auto-applied BindComposer]:
+
We can change the [https://www.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.bind.defaultComposer.class default auto-applied org.zkoss.bind.BindComposer]:
  
 
(in zk.xml)
 
(in zk.xml)
Line 77: Line 75:
 
<label value="${expr}" />
 
<label value="${expr}" />
 
<label value="@load(('Hi, ' += vm.person.firstName += ' ' += vm.person.lastName))​" />
 
<label value="@load(('Hi, ' += vm.person.firstName += ' ' += vm.person.lastName))​" />
<label value="@load((vm.names.stream().filter(x -> x.contains(vm.filter)).toList())) />
+
<label value="@load((vm.names.stream().filter(x -> x.contains(vm.filter)).toList()))" />
 
</source>
 
</source>
  
Line 93: Line 91:
 
* [http://books.zkoss.org/zk-mvvm-book/9.5/advanced/wire_components.html @Wire]​ and [http://books.zkoss.org/zk-mvvm-book/9.5/syntax/selectorparam.html @SelectorParam] are not supported.
 
* [http://books.zkoss.org/zk-mvvm-book/9.5/advanced/wire_components.html @Wire]​ and [http://books.zkoss.org/zk-mvvm-book/9.5/syntax/selectorparam.html @SelectorParam] are not supported.
  
* You cannot use ​[http://books.zkoss.org/zk-mvvm-book/9.5/syntax/bindingparam.html @BindingParam] to get components.
+
* You cannot use ​[http://books.zkoss.org/zk-mvvm-book/9.5/syntax/bindingparam.html @BindingParam] and [http://books.zkoss.org/zk-mvvm-book/9.5/syntax/contextparam.html @ContextParam] to get components.
 +
<source lang='java'>
 +
@Command
 +
public void commandA(@BindingParam Component otherComponent,
 +
                    @ContextParam(ContextType.COMPONENT) Component targetComponent​​) {
 +
    // do something here.
 +
}
 +
</source>
 +
 
 +
Note that even though you cannot get the full component, you '''CAN''' still use @BindingParam to retrieve values and other "ContextType"s.
 +
<source lang='xml'>
 +
<button label="click it" onClick="@command('commandB', foo='something', bar='somethingelse')" />
 +
</source>
 
<source lang='java'>
 
<source lang='java'>
 
@Command
 
@Command
public void commandA(@BindingParam Component comp) {
+
public void commandB(@BindingParam("foo") String foo,
 +
                    @ContextParam(ContextType.PAGE) Page page,
 +
                    @ContextParam(ContextType.DESKTOP) Desktop desktop) {
 
     // do something here.
 
     // do something here.
 
}
 
}
Line 104: Line 116:
 
<source lang='java'>
 
<source lang='java'>
 
@Command
 
@Command
public void commandB(@ContextParam(ContextType.TRIGGER_EVENT) MouseEvent event, @ContextParam(ContextType.COMPONENT) Component component​​) {
+
public void commandB(@ContextParam(ContextType.TRIGGER_EVENT) MouseEvent event) {
     // event.getTarget() and component is null
+
     // event.getTarget() is null.
 +
    // event.getPageX()/event.getPageY()/.. are still available.
 
}
 
}
  
Line 114: Line 127:
 
</source>
 
</source>
  
=== 3. Deferred Binding no longer needed ===   
+
=== 3. Deferred Binding no longer supported ===   
 
Deferred Binding is no longer supported. This feature is to avoid unnecessary AU requests (data updates). Since client MVVM does those bindings on the client-side, it doesn't require such an update.
 
Deferred Binding is no longer supported. This feature is to avoid unnecessary AU requests (data updates). Since client MVVM does those bindings on the client-side, it doesn't require such an update.
  
Line 123: Line 136:
 
</source>
 
</source>
  
=== 4. SmarNotifyChange always on ===
+
=== 4. SmartNotifyChange always on ===
 
SmartNotifyChange is always enabled in Client MVVM​, which means that the properties will only update (reload) when the value of the expression is changed.
 
SmartNotifyChange is always enabled in Client MVVM​, which means that the properties will only update (reload) when the value of the expression is changed.
  

Revision as of 08:35, 28 June 2022

DocumentationSmall Talks2022MayZK10 Preview: Using the new and light Client MVVM
ZK10 Preview: Using the new and light Client MVVM

Author
James Chu, Engineer, Potix Corporation
Date
May 26, 2022
Version
10.0.0.FL.20220525-Eval

Introduction

One of the major enhancements of ZK 10 is the Client MVVM. Without creating lots of binding objects on the server-side, client MVVM helps to reduce the memory footprint significantly. You can view the performance testing result in this article ZK10_Preview:_30x_Lighter_and_300x_Faster_with_Client_MVVM.

When we use ZK (server) MVVM, we start from a ZUL file and apply "BindComposer" to a component. It will create a binder, data bindings, and initialize ViewModel instances on the server-side. The binder is the kernel of MVVM and acts as a bridge to transfer data and events between the View and ViewModel. See more detail here.

ZK MVVM Mechanism


The new Client MVVM also starts from a ZUL and a ViewModel, and the binding information will be passed to the client-side with ZK widgets. When the widgets are created and rendered on the browser, the bindings will also be created at the same time.

ZK 10 Client MVVM Mechanism


As you can see, the binding now happens on the client-side. However, from an application developer's point of view, this change is transparent, and it is something ZK takes care of for you. You'll still be building the MVVM application in the same way, referencing our ZK MVVM guide, except for a few differences which we will cover in the following sections.

Now, let's take a look at how we can enable the Client MVVM.

Setting up Client MVVM

There are two ways to use the client MVVM, either use it against a specific ViewModel, or, set it globally. Either way, you have to first include the jar file and add the listener.

Step 1

Use ZK 10 EE-Eval freshly, and include the zephyr-ex jar file.

Step 2

Add the listener for Client MVVM. (in zk.xml)

<listener><listener-class>org.zkoss.zephyrex.bind.BinderPropertiesRenderer</listener-class></listener>

Step 3

Option 1: Apply to a ViewModel

Apply "org.zkoss.zephyrex.bind.ClientBindComposer" in ZUL.

For example: (in ZUL File)

<div apply="org.zkoss.zephyrex.bind.ClientBindComposer" viewModel="..." >
    <!-- other components -->
</div>

Option 2: Set it globally

We can change the default auto-applied org.zkoss.bind.BindComposer:

(in zk.xml)

<library-property><name>org.zkoss.bind.defaultComposer.class</name><value>org.zkoss.zephyrex.bind.ClientBindComposer</value></library-property>

(in ZUL File)

<div viewModel="..." >
    <!-- other components -->
</div>

If you use the global method, the ClientBindComposer will be applied automatically. If you wish to use a different composer (ex. the traditional ZK MVVM) on a specific VM, you can specify the "apply" attribute on that VM with the corresponding composer, and the manually specified apply will have the priority.

Once it is correctly configured, we can start to enjoy the benefits of client MVVM!

Restrictions and Differences

With client MVVM, the MVVM mechanism is now done at the client-side instead of the server-side, there are several limitations and differences compared to the server-side MVVM.

1. EL expressions not supported

EL expression - ${expr} and EL3 are not supported. For Example the following expressions are not supported:

<label value="${expr}" />
<label value="@load(('Hi, ' += vm.person.firstName += ' ' += vm.person.lastName))​" />
<label value="@load((vm.names.stream().filter(x -> x.contains(vm.filter)).toList()))" />

2. Follow the MVVM pattern

The main feature of MVVM is to decouple the UI and non-UI code, which means components should be controlled in MVVM. Developers should avoid controlling components directly. Even though we have this guideline, there are cases where developers have been accessing the components directly, and they use to work in ZK MVVM.

But now, when you apply client MVVM, ZK will update all the binding evaluation results into widget properties. This means the server-side does not have the full and most up-to-date information. Therefore, you cannot, and should no longer access and control those child components as a server-side component. You have to stick to the MVVM pattern.

  • Traversing components, like using "self" and ".parent" are not supported.
<button onClick="@command('delete', index=self.parent.parent.index)"/>
@Command
public void commandA(@BindingParam Component otherComponent,
                     @ContextParam(ContextType.COMPONENT) Component targetComponent​​) {
    // do something here.
}

Note that even though you cannot get the full component, you CAN still use @BindingParam to retrieve values and other "ContextType"s.

<button label="click it" onClick="@command('commandB', foo='something', bar='somethingelse')" />
@Command
public void commandB(@BindingParam("foo") String foo,
                     @ContextParam(ContextType.PAGE) Page page,
                     @ContextParam(ContextType.DESKTOP) Desktop desktop) {
    // do something here.
}
  • Component related API might return null (no component on the server-side).
@Command
public void commandB(@ContextParam(ContextType.TRIGGER_EVENT) MouseEvent event) {
    // event.getTarget() is null.
    // event.getPageX()/event.getPageY()/.. are still available.
}

// the component is null
org.zkoss.bind.Converter#coerceToUi(B beanProp, C component, BindContext ctx);
org.zkoss.bind.Converter#coerceToBean(U compAttr, C component, BindContext ctx);

3. Deferred Binding no longer supported

Deferred Binding is no longer supported. This feature is to avoid unnecessary AU requests (data updates). Since client MVVM does those bindings on the client-side, it doesn't require such an update.

<textbox value="@bind(vm.text1)">
    <custom-attributes org.zkoss.bind.event.deferPost="false"/>
</textbox>

4. SmartNotifyChange always on

SmartNotifyChange is always enabled in Client MVVM​, which means that the properties will only update (reload) when the value of the expression is changed.

5. Conditional Binding works differently

Conditional Binding works differently when the command updates the value without doing "NotifyChange". For example:

<label value="@load(vm.text, after='doChange')" />
<button label="Do Change" onClick="@command('doChange')" />
public String text = "123";
@Command
public void doChange() {
    this.text += "changed";
}

In ZK MVVM, the value of Label will be "123changed" after the button - "Do Change" is clicked.

In client MVVM, the value will remain "123". If you intend to see "123changed" you will need to do NotifyChange.

Debugging Tips

Unsupported/incompatible usages mentioned in the previous section will be reported as system logs during the application startup, remember to check the logs when running into issues using the client MVVM.

Upgrade Tips

  • Upgrading your ZK MVVM project to Client MVVM is straightforward if you do not run across any of the unsupported usages mentioned above.
  • Client MVVM can be applied to a specific VM if you are not ready to use it globally.
  • You may wish to update your code and remove the incompatibility if the performance/memory benefit client MVVM brings is important in your use case.
  • Client MVVM is a pluggable feature, if you are not ready to use Client MVVM, no worries, you can still upgrade to ZK 10 and use the existing ZK MVVM.

Summary

The new ZK 10's Client MVVM makes ZK application lighter and faster. In some in-house test cases where a large number of components are used, we are seeing 30 to 300 times improvements. We hope it also greatly improves the performance and usability of your project.

Try it out today and let us know what you think!

For more ZK 10 articles, visit here.


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.