ZK10 Preview: Using the new and light Client MVVM

From Documentation
Revision as of 09:41, 25 May 2022 by Jameschu (talk | contribs)
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 19, 2022
Version
10.0.0.FL.20220425-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 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.
}

-> Notice that we 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 needed

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.