ZK MVVM Pattern: Server-Side Data Binding
ZK is a great framework for developing in Java and performing data binding on said web applications. Read on to learn more!
Join the DZone community and get the full member experience.
Join For Free
You may be familiar with MVC patterns like Java Swing, manipulating UI components directly to handle events and setting their properties. However, you may admire how Angular MVVM saves you from operating in the DOM. Although that's great, you still need to manage to transfer data between a client and a server. ZK, on the other hand, supports data binding with Java objects directly and handles data transmission between a client and a server transparent to you. Let me demonstrate you with a simple application.
MVVM (Model-View-ViewModel) Pattern
MVVM (Model-View-ViewModel) is a design pattern created by John Gossman for WPF. This pattern divides an application into 3 roles: View, Model, and ViewModel.
- Model is responsible for exposing data while performing business logic.
- View is responsible for rendering UI and displaying data.
- ViewModel acts like a special Controller which is responsible for exposing data from the Model to the View and providing required actions and logic for user requests from the View.
The ViewModel is a View abstraction, which contains a View's state and behavior. The main characteristic is that ViewModel knows nothing about the View's visual elements and doesn't directly control View. Hence, there should be a mechanism (data binding/binder) that synchronizes data, handle events, and responses between View and ViewModel. This mechanism eliminates the dependency between View and ViewModel.
ZK Framework
ZK is a component-based UI framework that enables you to build modern, responsive web and mobile applications without having to learn JavaScript/AJAX/WebSocket. You can build highly-interactive and responsive AJAX web applications in pure Java. ZK provides hundreds of components which are designed for various UI requirements including layout, input, and data displaying. You can easily build your UI in an XML-formatted language, ZUML, which is far easier to understand than Java code.
ZK MVVM Pattern
When using ZK, I strongly suggest you developing in the MVVM pattern. The architecture looks like:
- ViewModel is just a POJO (plain ordinary Java object) that doesn't need to extend any parent class or implement any interface. Usually, it's developed by you.
- ZK UI components play View.
- MyServiceClass is not a real class name. It represents any class which is usually implemented by you perform business logic like searching or authentication.
- ZK data binding reads your data binding expressions and synchronizes data between ZK components and your ViewModel. Through command bindings, when a user interacts with the UI, e.g. clicking a button, it will invoke a method of your ViewModel.
An Example ToDo List
One day, I suddenly found I had the memory of a goldfish and messed up everything, therefore, I decided to build a To-Do list application with ZK to save my life. Let's start with a simple one like:
Start From a Window
I usually put a <window>
as an application frame and specify the title with a Font Awesome icon in <caption>
. This gives your application an elegant look.
<window border="normal" width="60%" vflex="1" style="margin:auto">
<caption label="My ToDo List" iconSclass="z-icon-list-alt" />
</window>
See? Building a UI is quick and easy in ZUL, and the XML tells you the overall idea about a page without actually running it.
Flexible Sizing
Just like CSS flexbox, you can specify a proportional number at vflex
('v' for vertical), and a component automatically adjusts its size for you. Because there is no sibling of <window>
, its height will consume the full page height.
Font Awesome Integration
You can just use Font Awesome CSS classes in the iconSclass
attribute with the prefix z-icon
because ZK includes all the related resources for you. Therefore, it's no bother to set up.
Put in Some Input Components
Then put a <textbox>
for typing to-dos and a <button>
to submit a to-do.
<window border="normal" width="60%" vflex="1" style="margin:auto">
<caption label="My ToDo List" iconSclass="z-icon-list-alt" />
<hlayout hflex="1">
<textbox hflex="1" placeholder="What needs to be done?" />
<button iconSclass="z-icon-plus-square" />
</hlayout>
</window>
Display a List of ToDo
ZK <listbox>
is the commonly-used component to show a list of items. Hence, I can assign a list of ToDos in my ViewModel to <listbox>
and define a zul template to render each ToDo on my list. The relationship among these 3 parts is:
Create a ListModel
Before showing a ToDo list, I have to create one and expose the list with a getter method:
public class TodoListViewModel {
...
private ListModelList<Todo> todoListModel;
//service class
private TodoListService todoListService = new TodoListService();
public TodoListViewModel(){
//get data from the service layer
List<Todo> todoList = todoListService.getTodoList();
//ListModelList optimizes rendering performance for ZK components
todoListModel = new ListModelList<Todo>(todoList);
}
public ListModelList<Todo> getTodoListModel() {
return todoListModel;
}
...
}
- ListModelList is ZK's collection object optimized for ZK data components.
Todo
represents one to-do item.TodoListService
is a service layer class that performs all to-do business logic including add, delete, update, and query. I assume it savesTodo
into a (fake) database.
Apply a ViewModel
The first step is to bindTodoListViewModel
to <window>
's viewModel
attribute, so that<window>
and its children can access the ViewModel like:
<window viewModel="@id('vm') @init('org.zkoss.mvvm.TodoListViewModel')" ... />
The @id
gives ViewModel a variable name so that we can access the ViewModel's properties by the dot notation, e.g. vm.name
. While the fully-qualified class name is used to instantiate the ViewModel object itself.
Bind ListModel to Listbox
Now, bind todoListModel
to <listbox>
's model
attribute:
<listbox model="@init(vm.todoListModel)" vflex="1">
@init: Load a Property Just Once
@init(vm.todoListModel)
instructs ZK to load the property todoListModel
via getter, TodoListViewModel.getTodoListModel()
, just once when loading the page.
Define a Template
Next, define a template with the name model
inside <listbox>
to tell ZK how to render the ListModel
.
<listbox model="@init(vm.todoListModel)" vflex="1">
...
<template name="model">
<listitem >
<listcell>
<checkbox checked="@init(each.complete)" />
</listcell>
<listcell>
<label value="@init(each.subject)" />
</listcell>
<listcell>
<button iconSclass="z-icon-times"/>
</listcell>
</listitem>
</template>
</listbox>
Implicit "each" Variable
Inside <template>
, ZK provides an implicit variable each
to access objects in the model. In my case, each
represents a Todo
object. Hence, I can access the Todo
's property with dot notation like each.subject
and assign its properties to components to render a Todo
.
Since my ListModel
contains 3 ToDos, ZK will render 3 <listitem>
, and each <listitem>
contains a <checkbox>
, a <textbox>
, and a <button>
.
Save a ToDo
Before saving a ToDo, it's necessary to declare a variable in ViewModel to store what gets typed:
public class TodoListViewModel {
private String subject;
...
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
@save: Save Data Back to a ViewModel's Property
On top of <listbox>
, I put a <textbox>
to type my to-do subject, to tell an input component to save user input back to ViewModel, just use @save
like:
<textbox value="@save(vm.subject)" ... />
Then ZK will save what I type by calling TodoListViewModel.setSubject()
when I leave my focus out of <textbox>
(that's the moment when ZK fires the onChange
event). You can set a breakpoint in the setter method to verify this saving behavior.
Add a ToDo
Saving a ToDo subject I typed is not enough. I have to put a new ToDo, with the subject, into todoListModel
when I click the plus icon button. Then, implement the addTodo
command method in ViewModel. In that method, it adds a new ToDo to my ToDo list with todoListModel.add(todo)
and saves new ToDos into a database with todoListService.saveTodo(todo)
:
public class TodoListViewModel {
...
@Command //@Command declares a command method
public void addTodo(){
...
//save data
Todo todo = new Todo(subject);
todo = todoListService.saveTodo(todo);
//update the model, by using ListModelList, you don't need to notify todoListModel change
//it is efficient that only update one item of the listbox
todoListModel.add(todo);
}
...
}
Like I said in the previous section, when I move the focus out of the <textbox>
, ZK stores what I typed into vm.subject
. Thus, TodoListViewModel
already has what I typed when ZK invokes addTodo()
when I click the button. I just need to create a new ToDo like new Todo(subject)
.
@command: Invoke a Method on at an Event Fired
After creating the command method, I can bind the event attribute, onClick
, to the ViewModel's command method with @command
to invoke the method.
<button onClick="@command('addTodo')" iconSclass="z-icon-plus-square" />
By default, the command name is the method name, addTodo
, but you can specify another name in @Command
if needed.
Fast Adding: Reuse Command Binding
Now I feel like clicking a button slows me down when creating a ToDo. I want a "Fast Adding" capability that's triggered by just pressing the enter key.
Luckily, <textbox>
fires an onOK
event when you press the enter key. Therefore, I just put the same command binding to that event again, no programming at all:
<textbox value="@save(vm.subject)" onOK="@command('addTodo')">
This case shows you the benefits of decoupling the UI and ViewModel. You can reuse data binding on different components and don't need to worry about affecting the ViewModel.
Clear Subject After Adding
After adding a to-do, I want to add another one. But it's troublesome to clear the previous subject for me in the <textbox>
every time. Hence, I decided to clear it after adding.
Since I bind value
to vm.subject
, I can clear the input by setting subject
to null
. But this clearing doesn't reflect on <textbox>
because ZK can't know what you changed inside a method. You have to tell ZK with @NotifyChange
, like:
public class TodoListViewModel {
...
@Command //@Command declares a command method
@NotifyChange("subject") //@NotifyChange tells ZK which property changed by this method
public void addTodo(){
...
//clear value for the next todo.
subject = null;
}
}
I need to specify what property I changed in the method with @NotifyChange("subject")
, so that ZK knows which property to reload after executing this command method.
@load: Load a Property and Keep Updated
So far I only use @save
, and <textbox>
only saves data to the ViewModel. But now I also want to load data from the ViewModel, therefore, it's time to use @load
:
<textbox value="@save(vm.subject)@load(vm.subject)" ...>
With @load
, each time ZK sees a @NotifyChange
for the matched property name, subject
, ZK will load the ViewModel's property through a getter, getSubject()
.
@bind: Synchronize Data in Two Ways
Writing @save
and @load
for the same property is a bit verbose. ZK knows your pain, thus supports a shortcut syntax: @bind
. Then you can shorten the expression above to:
<textbox value="@bind(vm.subject)" ...>
Modify a ToDo
After adding, "My ToDo List" shall allow the user to modify a ToDo in case they add a non-sense ToDo on accident.
First, I replace <label>
with <textbox>
. Then, to make it still look like a label, I specify inplace="true"
. As a result, that <textbox>
will display itself as a label first, then, when I click it, it becomes a textbox for input.
As I mentioned earlier, I can specify @save
to save a <textbox>
to a ToDo's subject
. However, I not only save the state to the ToDo
object, but also save it to a database. Thus, I also invoke a command at the onChange
event with the modified ToDo
as a parameter.
<listcell>
<textbox value="@init(each.subject)@save(each.subject)" inplace="true" width="100%"
onChange="@command('updateTodo', todo=each)"/>
</listcell>
Pass Parameters to a Command Method
ZK supports the ability to append a list of parameters in a key-value format in a command binding:
@command('updateTodo',todo=each)
todo
is the key andeach
is the value.
In order to receive a parameter, it's required to declare a parameter Todo
in the method signature and apply @BindingParam
with the key todo
. So that ZK knows to pass ToDo
into this command method.
public class TodoListViewModel {
...
@Command
public void updateTodo(@BindingParam("todo") Todo todo){
//update todo in the database
todoListService.updateTodo(todo);
}
}
Complete a ToDo
Things become similar here. Just put @save
to save the checked
of <checkbox>
to a ViewModel and invoke a command to update to the database.
<listcell>
<checkbox checked="@init(each.complete)@save(each.complete)"
onCheck="@command('completeTodo',todo=each)"/>
</listcell>
public class TodoListViewModel {
...
@Command
public void completeTodo(@BindingParam("todo") Todo todo){
//update to the database
todoListService.updateTodo(todo);
}
}
Apply a Strikethrough Style
In addition to checking the checkbox, I also want to apply a strikethrough effect for a completed ToDo item.
Create a CSS declaration in style.css
:
.complete-todo{
text-decoration: line-through;
}
Include the CSS file:
<?link rel="stylesheet" type="text/css" href="/style.css"?>
ZK data binding syntax actually accepts an EL expression:
@load(EL-expressions)
Thus, I can implement a simple rendering logic in my XML, like:
<textbox inplace="true" ...
sclass="@load(each.complete?'complete-todo':'')">
It applies a CSS class complete-todo
when each.complete
is true
.
Notify Change Programmatically
In the previous completeTodo()
, I didn't notify the change for ToDo
. Hence, ZK won't apply the CSS complete-todo
when I complete a ToDo. Since todo
is not TodoListViewModel
's property, I notify ZK with the API BindUtils.postNotifyChange()
. Just keep the first and the second parameter as null to use the default value. The third parameter is the data object and the fourth parameter is the data object's property name.
public class TodoListViewModel {
...
@Command
public void completeTodo(@BindingParam("todo") Todo todo){
//update to the database
todoListService.updateTodo(todo);
BindUtils.postNotifyChange(null, null, todo, "complete");
}
}
Delete a ToDo
Deleting a ToDo is easy to implement, just add a command binding:
<listcell>
<button onClick="@command('deleteTodo',todo=each)" iconSclass="z-icon-times"/>
</listcell>
Remember to remove a todo
both in ListModel
and the database.
public class TodoListViewModel {
...
@Command
public void deleteTodo(@BindingParam("todo") Todo todo){
//delete the todo in the database
todoListService.deleteTodo(todo);
//update ListModel, by using ListModelList, you don't need to notify todoListModel change
todoListModel.remove(todo);
}
}
That's it. A web application to save my life is finished. Hallelujah!
Benefits of MVVM
Through this small application, we can see the decoupling between View and ViewModel brings the following benefits:
- Parallel development. As long as the contract (what data to show and what actions to perform) is made, the UI design and coding of ViewModel can proceed in parallel and independently. Either side will not block the other's way.
- Loose coupling with View. UI design can be easily changed from time to time without modifying the ViewModel as long as the contract does not change.
- Better reusability. As long as the contract (view states and commands) doesn't change, you can reuse one ViewModel on multiple pages.
- Lower cost for responsive design. It will be cost less to design different views for different devices with a common ViewModel for responsive design. For a desktop browser with a bigger screen, more information can be shown on one page; while for a smartphone with limited display space, displaying less information with a different layout can be done without the need to change a ViewModel because you can just bind fewer ViewModel properties.
- Better testability. Since ViewModel does not reference the View, developers can unit-test the ViewModel class easily without UI elements.
Complete Source Code
If you think this application may also save your life, you can access its full source code on GitHub. Note: When you run the example project, you will see a different look. Because I capture all screenshots under a theme, gardensalad, which is only available in the theme pack.
Opinions expressed by DZone contributors are their own.
Comments