ZK Dos and Donts
Matthieu Duchemin, Engineer, Potix Corporation
ZK Do's and Don'ts Part 1
ZK Framework offers a lot of features to simplify the creation of Java-based web applications. Some of these features are not always well known. From time to time, we come across suboptimal implementation. While the results aren't necessarily broken or damaging, improving them can have very positive effects with few changes or efforts. Usually, it only takes a few simple steps to turn them simpler and easier. You will find here a few cases where ZK efficiency can be maximized.
Example project available here
Calling components directly when using a model
ZK offers default ListModel implementations such as ListModelList. They provide an easy way to separate the view from the model. By providing a template or a renderer to control the appearance, and a model to handle the data.
While delegating control to the model, it sometimes feels "easier" to act directly on a component. One would use grid.getRows().appendChild(..) to add a new row, or use listbox.setSelectedIndex() to set the selection state of a Listbox.
If the components content is directly controlled by the application, this may be correct. However, if the content is controlled by a model, the model itself should perform these actions.
- Use ListModels instead of basic collections when assigning a model to a component.
- Use ListModel methods to apply changes to the model, including it's selection state.
- Use component methods to alter the view when using a model.
- Mix model and manual entries in the same component.
Understanding ZK scopes
Scopes in ZK define the range affected by and action. For example, when creating an event queue, we need to define the scope of listeners affected by its events.
The basic scopes are DESKTOP, SESSION and APPLICATION. 
APPLICATION is very straightforward: it will affect every listeners on the whole application.
DESKTOP roughly represents a unique browser tab. In specific cases, like Iframe, a tab can contain more than one desktop.
When using events, choosing the right scope can lead to increased performances, reduced network activity and reduced memory consumption. It’s also worth remembering that using an event queue with any scope except Desktop will automatically activate server push.
- Always use the smallest possible scope.
- Be careful when using session scope, as it will affect every desktop in the session.
- Be extra careful when using application scope, as it will affect any desktops in the application.
- Use a scope broader than desktop unless you are sure that you need it.
- Create an event queue using the session or application scopes if you do not want server push to be activated.
Component belong to another desktop
Saving a reference to a component can be a powerful way to keep track of changes, but there is one pitfall to avoid. ZK components are bound to a desktop, and cannot be passed around to another desktop or session. Choosing where to save or send the component reference is critical, as accessing it outside of its original desktop would cause exceptions in the best case, and corrupted views in the worst. To avoid this, always use desktop scope when passing around components, and only send values when serving a whole session or application.
Trying to access a component belonging to another desktop will generate the following exception:
HTTP ERROR 500 Problem accessing /zk-dos-and-donts/part1/3-comp-and-desktop/. Reason: The parent and child must be in the same desktop: <Window o5JQ0> Caused by: org.zkoss.zk.ui.UiException: The parent and child must be in the same desktop: <Window o5JQ0>
java.lang.IllegalStateException: Access denied: component, <Listcell z_27_b53>, belongs to another desktop: [Desktop g272]
- Send references of business objects or values between desktops if needed.
- Use event queues for live data. Use session or application parameters for static data.
- Keep reference to a view object such as a component as a session or application parameter.
- Send a reference to a view object in an event queue with a scope is broader than desktop.
Choosing @init, @load, @save, or @bind
Using the MVVM data-binding annotations to wire a view model and a view, you may use these four annotations.
- @init will load the value in the view once, but will not update it.
- @load will load the value in the view, then updated it when the view model notify a change.
- @save will not load anything in the view, but will save the values from the view into the view model when updated.
- @bind is basically @save plus @load. Values will be updated from and to the view model when necessary.
- Choose the least expensive option for each binding.
- Bind a value using @save or @bind on components whose value cannot be set by the user, such as label, etc.
- Bind a value using @load or @bind on components whose value is never set on server side, such as a password entry field.
- Use @load when @init is sufficient.
Notifying the whole ListModel on change
Speaking of models and data-binding, ListModels will automatically synchronize with the view on modification, such as adding or removing an element. This makes adding new entries in a complex control such as a grid very easy.
However, it’s not always best to refresh the whole model after making a small update on one of its items. ListModels can actually update individual items, which can save a lot of resources and network traffic.
- Bind a ListModel to a component using @init, unless you need to replace the whole model by a new object.
- Use the ListModelList.notifyChange(…) [ZK 184.108.40.206], or .update(..) [ZK 8.0.0] method to update a single entry, or set the entry again to trigger a refresh instead of notifying the whole ListModel.
- Use @NotifyChange(“*”), or BindUtils.postNotifyChange(…, …, …, "*"); unless there is no better option.
- Use @NotifyChange(“myListModel”) or BindUtils.postNotifyChange(…, …, …, "myListModel"); to update a single row.
|Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.|