DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Externalize Microservice Configuration With Spring Cloud Config
  • Create Customizable Database App Systems With One Command
  • What Is Ant, Really?
  • Migrate Mule 3 to Mule 4 Using MMA (Mule Migration Assistant)

Trending

  • Emerging Data Architectures: The Future of Data Management
  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • GDPR Compliance With .NET: Securing Data the Right Way
  • How to Build Scalable Mobile Apps With React Native: A Step-by-Step Guide
  1. DZone
  2. Data Engineering
  3. Data
  4. ZK MVVM Pattern: Server-Side Data Binding

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!

By 
Hawk Chen user avatar
Hawk Chen
DZone Core CORE ·
Jan. 23, 18 · Tutorial
Likes (23)
Comment
Save
Tweet
Share
22.4K Views

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.

Image title

ZK MVVM Pattern

When using ZK, I strongly suggest you developing in the MVVM pattern. The architecture looks like: 

Image title

  • 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:

Image title

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.

Image title

<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.

Image title

<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:

Image title

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.

Image title


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 onChangeevent 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 and each 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.

Image title

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.

Data binding Property (programming) mobile app Command (computing) Database Object (computer science) Font Awesome

Opinions expressed by DZone contributors are their own.

Related

  • Externalize Microservice Configuration With Spring Cloud Config
  • Create Customizable Database App Systems With One Command
  • What Is Ant, Really?
  • Migrate Mule 3 to Mule 4 Using MMA (Mule Migration Assistant)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!