Hello ZK MVVM"

From Documentation
m (correct highlight (via JWB))
 
(70 intermediate revisions by 7 users not shown)
Line 6: Line 6:
 
__TOC__
 
__TOC__
  
This is the first article in a series about ZK and MVVM design pattern. I will explain how to use ZK and ZK Bind, our new generation of data binding system, with simple examples. From beginner level to advance level, each article will cover a major feature of the ZK Bind or discuss the best practice of using the ZK Bind with MVVM design pattern in some commonly seen use cases in real world applications. All example source codes are downloadable.
+
This is the first article of a series of articles about ZK and the MVVM design pattern. This article will explain how ZK and ZK Bind, our new generation of data binding system can be used, with simple examples. From beginner's level to advanced level, each article will cover a major feature of ZK Bind or discuss the best practice of using ZK Bind with MVVM design pattern in some commonly seen use cases in real world applications. All example source codes are downloadable.
 +
 
 
=Introduction=
 
=Introduction=
  
Here is some background information if you are not familiar with the term of MVVM. For those who are already familiar with what MVVM is, you can skip this section.
+
MVVM is a variant of the Model/View/Controller(MVC) design pattern that helps achieve '''''separation of data and logic from presentation''''' easily. It isolates the View layer and the Model layer avoiding tight coupling between the View and Controller layer. UI designers and programmers can do their jobs in parallel and independently. Thus the productivity is enhanced and project schedule is on track.
 +
 
 +
Here are some background information for users who are not familiar with the term MVVM. For those who are already familiar with what MVVM is, please skip this section.
  
 
==What is MVVM?==
 
==What is MVVM?==
  
MVVM represents Model, View, and ViewModel which is identical to Presentation Model introduced by Martin Fowler, a special variance of the famous MVC pattern.
+
MVVM represents '''Model''', '''View''', and '''ViewModel''' which is identical to the [http://martinfowler.com/eaaDev/PresentationModel.html Presentation Model] introduced by Martin Fowler, a special variant of the famous MVC pattern.
  
The ViewModel in MVVM design pattern acts like a special Controller in MVC pattern which is responsible for exposing the data objects from the Model to the View and providing required action logic for user requests from the View. The ViewModel is kind like an abstraction of a View, which contains a View's state and behavior. From another angle, you can also say that the View layer is kind like an UI projection of the ViewModel.
+
The '''ViewModel''' in MVVM design pattern acts like a special Controller for View in MVC pattern which is responsible for exposing data objects from the data Model to the View and for providing required action and logic for user requests from the View. The ViewModel is kind of like an abstraction of a View, which contains a View's states and behaviors. From another angle, it can also be said that the View layer is kind of  like an UI projection of the ViewModel.
  
Until now, you might start puzzling: "Isn't that just what Controller is in MVC? Are you just inventing a new term?". Of course not. One more important feature about ViewModel in MVVM pattern is that a ViewModel shall knows nothing about View's visual elements at all. This is the key that makes MVVM design pattern different from other MVC variances.
+
Up until now, you might start to wonder: "Isn't that just what Controller is in MVC? Are you just inventing a new term?". Of course not. Another and a more important feature of ViewModel in MVVM pattern is that a ViewModel ''''''knows nothing about View's visual elements''''''. This is the key that makes MVVM design pattern different from other MVC variances.
  
 
==Why MVVM?==
 
==Why MVVM?==
Line 23: Line 26:
 
<i><b>Separation of data and logic from presentation</b></i>
 
<i><b>Separation of data and logic from presentation</b></i>
  
The key feature that the ViewModel knows nothing about View's visual elements guarantees the one way dependency from View to the ViewModel and avoid mutual programming ripple effects between UI and the ViewModel. Consequently, it brings the following advantages:
+
The key feature that the ViewModel knows nothing about View's visual elements guarantees the one way dependency from View to the ViewModel thus avoiding mutual programming ripple effects between UI and the ViewModel. Consequently, it brings the following advantages:
  
*As long as the contract is set(what data to show and what actions to proceed), the UI design and coding of ViewModel can be implemented parellelly. Either side will not block the other's way.
+
*As long as the contract is set(what data to show and what actions to proceed), the UI design and coding of ViewModel can be implemented in parallel and independently. Either side will not block the other's way.
*You can easily change your UI design from time to time without changing the ViewModel as long as the contract does not change.
+
*UI design can be easily changed from time to time without changing the ViewModel as long as the contract does not change.
*You are more likly to design different views for different devices with a common ViewModel. For a desktop browser with bigger screen, you can show more information in one page; while for a smart phone with limited display space, you might design a wizard-based step-by-step operation UI without the need to change (much of) the ViewModel.
+
*It will be easier to design different views for different devices with a common ViewModel. For a desktop browser with a bigger screen, more information can be shown on one page; while for a smart phone with limited display space, designing a wizard-based step-by-step operation UI can be done without the need to change (much of) the ViewModel.
*Since ViewModel does not "see" presentation layer, you can unit-test the ViewModel functions easily without UI elements.
+
*Since ViewModel does not "see" presentation layer, users can unit-test the ViewModel functions easily without UI elements.
  
==What does this has to do with data binding?==
+
==What does this have to do with ZK Bind?==
  
Implementation-wise, no matter how, "someone" in the system has to help synchronizing data between View and ViewModel layers. Also this "someone" has to accept the user request from the View layer and bridge to the action logic provided by the ViewModel layer. This "someone", the kernel part in the MVVM design pattern, is typically a data binding system; either written by the application developers themselves or provided by a framework.
+
Implementation-wise, no matter how, ''someone'' in the system has to help synchronizing data between View and ViewModel layers. Also, this ''someone'' has to accept the user request from the View layer and bridge the request to the action and logic provided by the ViewModel layer. This ''someone'', the kernel part of the MVVM design pattern, is either data synchronising codes written by the application developers themselves or a data binding system provided by the framework. In ZK 6, ZK Bind is this key data binding infrastructure of the MVVM design pattern.
  
It would be easier to explains the concept with examples with two different implementations. One in Model-View-Presenter(MVP)<ref>Model-View-Presenter(MVP) is another variance of MVC design pattern. If you are not aware of what that is, don't worry, just watch the example codes.</ref> design pattern and another in Model-View-ViewModel(MVVM).
+
It would be easier to explain the concept with an example which has two different implementations. One in Model-View-Presenter(MVP)<ref>Model-View-Presenter(MVP) is another variance of MVC design pattern. If you are not aware of what that is, don't worry, just view the example codes.</ref> design pattern and another in Model-View-ViewModel(MVVM).
  
 
<references/>
 
<references/>
Line 43: Line 46:
 
==MVP Implementation==
 
==MVP Implementation==
 
[[Image:SmallTalk_MVP_HELLO_FLOW.png]]
 
[[Image:SmallTalk_MVP_HELLO_FLOW.png]]
#An user push a button(an action) on the screen. A corresponding event is fired.
+
#A user push a button(an action) on the screen. A corresponding event is fired.
 
#An event listener on the back-end handles this event.
 
#An event listener on the back-end handles this event.
 
#Access data and so on.
 
#Access data and so on.
#Then UI elements was changed accordingly to provide visual feed backs to the user.
+
#Then UI elements were changed accordingly to provide visual feedback to the user.
  
helloMVP.zul
+
''View'': helloMVP.zul
 
<source lang="xml">
 
<source lang="xml">
<window apply="test.hello.HelloComposer">
+
<window apply="HelloComposer">
 
<label id="lbl"/>
 
<label id="lbl"/>
 
<button id="btn" label="Show"/>
 
<button id="btn" label="Show"/>
Line 56: Line 59:
 
</source>
 
</source>
  
HelloComposer.java
+
''Presenter'': HelloComposer.java
<source lang="java">
+
<source lang="java" highlight="2,4">
 
     public class HelloComposer extends GenericForwardComposer {  
 
     public class HelloComposer extends GenericForwardComposer {  
 
         private Label lbl;
 
         private Label lbl;
Line 67: Line 70:
 
</source>
 
</source>
  
The "Presentor" was injected with the View components Label lbl, Button btn, and the event listener onClick$btn(). As you can see, the characteritic of MVP design pattern is that the Presentor(the Controller) would need to hold references to the View layer UI components so it can update the states of the View and generates visual feed backs. As in this example, the value of the Label component lbl is updated with the "Hello World" and provide visual feed backs to the user.
+
The ''Presenter'' was injected with the View components Label <code>lbl</code>  and the event listener <code>onClick$btn()</code>. As shown above, the characteristics of a MVP design pattern is that the Presenter(the Controller) would need to hold references to the View layer UI components so that it can update the states of the View and generate visual feedback. As in this example, the <code>value</code> of the Label component <code>lbl</code> is updated with "Hello World" and provides visual feedback to the user.
  
 
==MVVM Implementation==
 
==MVVM Implementation==
 
[[Image:SmallTalk_MVVM_HELLO_FLOW.png]]
 
[[Image:SmallTalk_MVVM_HELLO_FLOW.png]]
  
As you can see on the above graph, data binder plays the role of syncing data between UI and ViewModel.
+
As stated in the paragraph earlier, data binder plays the role of syncing data between UI and ViewModel.
#An user presses a button on the screen.
+
#A user presses a button on the screen.
 
#A corresponding event is fired to the binder.
 
#A corresponding event is fired to the binder.
#The binder finds the corresponding action logic in the ViewModel and call it.
+
#The binder finds the corresponding action logic in the ViewModel and calls it.
#The action logic access data and so on.
+
#The action logic accesses data from Model layer and updates corresponding properties of the ViewModel.
#View-Model notify the binder that some properties have been changed.
+
#ViewModel notify the binder that some properties have been changed.
#Per what properties have been changed, the binder load data from the ViewModel.
+
#Per what properties have been changed, the binder loads data from the ViewModel.
#Binder then updates the corresponding UI controls to provide visual feed backs to the user.
+
#Binder then updates the corresponding UI controls to provide visual feedback to the user.
  
Apparently, UI designer has to tell binder at least following things:
+
Apparently, UI designer has to tell binder at least the following things:
*Which UI event is used to proceed which action logic? (so binder knows what to call).
+
*Which UI event is used to proceed which action logic? (so binder knows what method to call).
 
*Which UI attribute is used to show which data? (so binder knows what to load and what to update).
 
*Which UI attribute is used to show which data? (so binder knows what to load and what to update).
*Which UI attribute is used to input into which data? (so binder knows what to save).
+
*Which UI attribute is used to input into which data? (so binder knows what property to save).
  
In ZK MVVM, we utilize the ZK Annotation(link) to do these jobs:
+
In ZK Bind, ZK Annotation is utilized to do these jobs:
  
helloMVVM.zul
+
''View'': helloMVVM.zul
 
<source lang="xml">
 
<source lang="xml">
<window apply="test.hello.HelloViewModel">
+
<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')">
<custom-attributes composerName="vm"/>
+
<label value="@load(vm.message)"/>
<label value="@bind(vm.message)"/>
+
<button label="Show" onClick="@command('showHello')"/>
<button label="Show" onClick="@bind('showHello')"/>
 
 
</window>
 
</window>
 
</source>
 
</source>
  
HelloViewModel.java
+
''ViewModel'': HelloViewModel.java
<source lang="java">
+
<source lang="java" highlight="3,7">
public HelloViewModel extends GenericBindComposer {
+
public HelloViewModel {
 
private String message;
 
private String message;
 
public String getMessage() {
 
public String getMessage() {
Line 105: Line 107:
 
}
 
}
 
 
@NotifyChange("message")
+
@Command @NotifyChange("message")
 
public void showHello() {
 
public void showHello() {
 
message = "Hello World!";
 
message = "Hello World!";
Line 113: Line 115:
  
 
Per this example, let's run through the program flow:
 
Per this example, let's run through the program flow:
#When end user press the "Show" button, the onClick event is fired to binder.
+
#When end user press the "Show" button, the onClick event is fired to the binder.
#The binder finds the command name in the ViewModel is "showHello" as specified in the ZK annotation <tt>@bind('showHello')</tt>.
+
#The binder finds that the command name in the ViewModel which is "showHello" is as specified in the ZK annotation <code>@command('showHello')</code>.
#The binder calls the <tt>showHello()</tt> method in the HelloViewModel and change the <tt>message</tt> property. Note the <tt>@NotifyChange("message")</tt> Java method annotation on <tt>showHello()</tt> method in HelloViewModel. It tells the binder that the property <tt>message</tt> in the ViewModel will be changed if calling this command.
+
#The binder calls the <code>showHello()</code> method in the <code>HelloViewModel</code> and changes the <code>message</code> property. Note that the <code>@Command</code> and the <code>@NotifyChange("message")</code> Java method annotations on <code>showHello()</code> method in <code>HelloViewModel</code>. <code>@Command</code> tells the binder that the method is for a command while <code>@NotifyChange("message")</code> tells the binder that the property <code>message</code> in the <code>HelloViewModel</code> will be changed if this command is called.
#The binder then find that the attribute <tt>value</tt> of component <tt>label</tt> is associated with the changed <tt>vm.message</tt> property, so it loads data from the property <tt>vm.message</tt> and updates the label's <tt>value</tt> attribute. The new message "Hello World!" is then shown on the screen and provide the visual feed back to the end user.
+
#The binder then finds that the attribute <code>value</code> of component <code>label</code> is associated with the changed <code>message</code> property of the <code>HelloViewModel</code>(as specified in ZK annotation <code>@load(vm.message)</code>). So it loads data from the property <code>vm.message</code> by calling <code>getMessage()</code> getter method of the <code>HelloViewModel</code> and updates the label's <code>value</code> attribute. The new message "Hello World!" is then shown on the screen and provides the visual feedback to the end user.
  
In this MVVM implementation with data binding system, the <tt>HelloViewModel</tt> refers none of the UI components. It only exposes the message contents via <tt>getMessage()</tt> and provides the necessary action logic <tt>showHello()</tt>. It does not know how these information or action logic will be used. It is the UI designer's job to decide which UI components to use in which layout just as seen in <tt>HelloMVVM.zul</tt>.
+
In this MVVM implementation with data binding system, the <code>HelloViewModel</code> is a simple POJO and refers none of the UI components. It only exposes the message contents via <code>getMessage()</code> and provides the necessary action logic <code>showHello()</code>. It does not know how these information or action logic will be used. It is the UI designer's job to decide which UI components are to be used in which layout as seen in <code>HelloMVVM.zul</code>.
  
=The example revised=
+
=The example revised -- Pop Up the Message=
Customers tend to change their mind when they see the real screen. Say, after you demonstrated the program to the customer and he/she said, ''"Well, I thought the Hello World! message shall be in a pop up window..."''.
+
Customers tend to change their minds when they see the real screen. Say, after you demonstrated the program to a customer and he/she said, ''"Well, I thought the Hello World! message should be in a pop up window..."''.
  
No problem, let's prepare a modal window and puts the message in the modal window. Easy.  
+
No problem, let's prepare a modal window and put the message in the modal window. Easy.  
  
 
==Revised MVP Implementation==
 
==Revised MVP Implementation==
helloMVP2.zul
+
''View'': helloMVP2.zul
 
<source lang="xml">
 
<source lang="xml">
<window apply="test.hello.HelloComposer2">
+
<window apply="HelloComposer2">
 
<button id="btn" label="Show"/>
 
<button id="btn" label="Show"/>
<window id="win" title="Hello" width="300px" height="200px" visible="false">
+
<window id="popwin" title="Hello" width="300px" height="200px" visible="false">
 
<hbox align="center" pack="center" hflex="true" vflex="true">
 
<hbox align="center" pack="center" hflex="true" vflex="true">
 
<label id="lbl"/>
 
<label id="lbl"/>
Line 138: Line 140:
 
</source>
 
</source>
  
HelloComposer2.java
+
''Presenter'': HelloComposer2.java
<source lang="java">
+
<source lang="java" highlight="2,3,5">
 
public class HelloComposer2 extends GenericForwardComposer {
 
public class HelloComposer2 extends GenericForwardComposer {
private Label win$lbl;
+
private Label popwin$lbl;
private Button btn;
+
private Window popwin;
private Window win;
 
 
 
 
public void onClick$btn(Event event) {
 
public void onClick$btn(Event event) {
win$lbl.setValue("Hello World!");
+
popwin$lbl.setValue("Hello World!");
win.doModal();
+
popwin.doModal();
 
}
 
}
 
}
 
}
 
</source>
 
</source>
  
Both the View zul file and the Controller has to be revised. This time, it injects the pop up window <tt>win</tt> and so on because the View has changed.
+
The ''View'' zul file has to be revised and the ''Presenter'' java file has to be totally rewritten. This time, it injects the pop up window <code>popwin</code>, the label <code>popwin$lbl</code> inside the pop up window and so on because the View has changed.
  
 
==Revised MVVM Implementation==  
 
==Revised MVVM Implementation==  
helloMVVM2.zul
+
''View'': helloMVVM2.zul
<source lang="xml">
+
<source lang="xml" highlight="4">
<window apply="test.hello.HelloViewModel">
+
<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')">
<custom-attributes composerName="vm"/>
+
<button label="Show" onClick="@command('showHello')"/>
<button label="Show" onClick="@bind('showHello')"/>
 
 
<window title="Hello" width="300px" height="200px" mode="modal"
 
<window title="Hello" width="300px" height="200px" mode="modal"
visible="@bind(not empty vm.message)">
+
visible="@load(not empty vm.message)">
 
<hbox align="center" pack="center" hflex="true" vflex="true">
 
<hbox align="center" pack="center" hflex="true" vflex="true">
<label value="@bind(vm.message)"/>
+
<label value="@load(vm.message)"/>
 
</hbox>
 
</hbox>
 
</window>
 
</window>
 
</window></source>
 
</window></source>
  
The advantages of using MVVM design pattern shows up when customers change their requirements on the View. UI Designer can proceed the change independently and the <tt>HelloViewModel</tt> does not have to be changed because what the customer required to change was the way to show an information not the information itself. Notice that here we control the show/hide of the modal window per the "message" is empty or not.
+
The advantages of using MVVM design pattern comes in when customers changes their requirements on the View. UI Designers can proceed the changes independently and the <code>HelloViewModel</code> java file does not have to be changed because what the customer required was to change the way how the message is presented not the message itself. Notice that here the show/hide of the modal window is controlled by whether the <code>message</code> is empty or not(<code>visible="@load(not empty vm.message)"</code>).
  
 
=ZK Bind Features=
 
=ZK Bind Features=
  
To make ZK Bind as easy to use as possible, we have implement many features. We will cover such features one by one in the coming articles with proper examples commonly seen in real world applications. Here is a brief feature list:
+
To make ZK Bind as easy to use as possible, many features have been implemented. These features will be covered and introduced one by one in the coming articles with examples commonly seen in real world applications. Here is a brief features list: (For details, please see [http://books.zkoss.org/wiki/Small_Talks/2011/October/Envisage_ZK_6:_The_Next_Generation_Data_Binding_System this].)
  
*One way binding in Save direction only
+
*One way binding in ''Save'' direction only
 
<source lang="xml">
 
<source lang="xml">
<textbox value="@bind(save=a.b.c)"/>
+
<textbox value="@save(a.b.c)"/>
 
</source>
 
</source>
*One way binding in Load direction only:  
+
*One way binding in ''Load'' direction only:  
 
<source lang="xml">
 
<source lang="xml">
<textbox value="@bind(load=a.b.c)"/>
+
<textbox value="@load(a.b.c)"/>
 
</source>
 
</source>
 
*Two way binding
 
*Two way binding
 +
<source lang="xml">
 +
<textbox value="@load(a.b.c) @save(a.b.c)"/>
 +
</source>
 +
equals to
 
<source lang="xml">
 
<source lang="xml">
 
<textbox value="@bind(a.b.c)"/>
 
<textbox value="@bind(a.b.c)"/>
Line 188: Line 192:
 
*Template binding
 
*Template binding
 
<source lang="xml">
 
<source lang="xml">
<listbox model="@bind(vm.persons)" selectedItem="@bind(vm.selected)">
+
<listbox model="@load(vm.persons)" selectedItem="@bind(vm.selected)">
 
     <template name="model" var="p">
 
     <template name="model" var="p">
         <listitem label="@bind(p.firstname)"/>
+
         <listitem label="@load(p.firstname)"/>
 
     </template>
 
     </template>
 
</listbox>
 
</listbox>
Line 196: Line 200:
 
*Form binding
 
*Form binding
 
<source lang="xml">
 
<source lang="xml">
<grid self="@form(id='fx', save=vm.currentTask before 'updateTask')">
+
<grid form="@id('fx') @save(vm.currentTask, before='updateTask')">
 
     <row><textbox value="@bind(fx.name)"/></row>
 
     <row><textbox value="@bind(fx.name)"/></row>
 
     <row><textbox value="@bind(fx.description)"/></row>
 
     <row><textbox value="@bind(fx.description)"/></row>
     <row><datebox value="@bind(fx.date, converter='formatedDate', format='yyyy/MM/dd')"/></row>
+
     <row><datebox value="@bind(fx.date) @converter('formatedDate', format='yyyy/MM/dd')"/></row>
 
</grid>
 
</grid>
 
</source>
 
</source>
 
*Conditional binding: binding on different commands
 
*Conditional binding: binding on different commands
 
<source lang="xml">
 
<source lang="xml">
<grid self="@form(id='fx', save={vm.currentTask before 'updateTask', vm.newTask before 'addTask'})">
+
<grid form="@id('fx') @save(vm.currentTask, before='updateTask') @save(vm.newTask, before='addTask')">
 
     <row><textbox value="@bind(fx.taskName)"/></row>
 
     <row><textbox value="@bind(fx.taskName)"/></row>
 
     <row><textbox value="@bind(fx.description)"/></row>
 
     <row><textbox value="@bind(fx.description)"/></row>
     <row><datebox value="@bind(fx.date, converter='formatedDate', format='yyyy/MM/dd')"/></row>
+
     <row><datebox value="@bind(fx.date) @converter('formatedDate', format='yyyy/MM/dd')"/></row>
 
</grid>
 
</grid>
 
</source>
 
</source>
 
*EL 2.2 powered
 
*EL 2.2 powered
 
<source lang="xml">
 
<source lang="xml">
<image src="@bind(person.boy ? 'boy.png' : 'girl.png')"/>
+
<image src="@load(person.boy ? 'boy.png' : 'girl.png')"/>
<button onClick="@bind(vm.add ? 'add' : 'update')" />
+
<button onClick="@command(vm.add ? 'add' : 'update')" />
<button disabled="@bind(empty vm.symbol)"/>
+
<button disabled="@load(empty vm.symbol)"/>
 
</source>
 
</source>
*Java annotated property dependency (in ViewModel)
+
*Java annotated property dependency tracking (in ViewModel)
 
<source lang="java">
 
<source lang="java">
 
@NotifyChange
 
@NotifyChange
Line 236: Line 240:
 
=Summary=
 
=Summary=
  
In this article, I have mentioned the design pattern MVVM and its advantages. I also scratch the basic operations of our new generation of data binding system for ZK, the ZK Bind. The On Demand Hello World example somehow presents the basic idea of MVVM and how ZK Bind works but might be too simiple to be a good one in real world. I will address more functions of the ZK Bind with more examples in following series of articles including the concepts of conditional binding, form binding, template binding, and separation of loading and saving, etc..  Stay tuned.
+
In this article, I have mentioned the design pattern MVVM and its advantages. I have also touched upon the basic operations of ZK's new generation data binding system - the ZK Bind. The On Demand Hello World example presents the basic idea of MVVM and how ZK Bind works. However, it may appear to be too simple for an impressive real world application. We will address more functions of the ZK Bind with more examples in up coming series of articles including the concepts of conditional binding, form binding, template binding, and separation of loading and saving, etc..  Stay tuned.
 
 
=Download=
 
 
 
  
 +
=Demo and Download=
 +
*[http://zkfiddle.org/sample/3sk0uv8/10-Hello-ZK-MVVM HelloMVVM]
 +
*[http://zkfiddle.org/sample/2k17s4l/10-Hello-ZK-MVVM2 HelloMVVM2]
 
{{Template:CommentedSmalltalk_Footer|
 
{{Template:CommentedSmalltalk_Footer|
 
|name=Potix Corporation
 
|name=Potix Corporation
 
}}
 
}}

Latest revision as of 04:18, 20 January 2022

Hello ZK MVVM

Author
Henri Chen, Principal Engineer, Potix Corporation
Date
October 17, 2011
Version
ZK 6

This is the first article of a series of articles about ZK and the MVVM design pattern. This article will explain how ZK and ZK Bind, our new generation of data binding system can be used, with simple examples. From beginner's level to advanced level, each article will cover a major feature of ZK Bind or discuss the best practice of using ZK Bind with MVVM design pattern in some commonly seen use cases in real world applications. All example source codes are downloadable.

Introduction

MVVM is a variant of the Model/View/Controller(MVC) design pattern that helps achieve separation of data and logic from presentation easily. It isolates the View layer and the Model layer avoiding tight coupling between the View and Controller layer. UI designers and programmers can do their jobs in parallel and independently. Thus the productivity is enhanced and project schedule is on track.

Here are some background information for users who are not familiar with the term MVVM. For those who are already familiar with what MVVM is, please skip this section.

What is MVVM?

MVVM represents Model, View, and ViewModel which is identical to the Presentation Model introduced by Martin Fowler, a special variant of the famous MVC pattern.

The ViewModel in MVVM design pattern acts like a special Controller for View in MVC pattern which is responsible for exposing data objects from the data Model to the View and for providing required action and logic for user requests from the View. The ViewModel is kind of like an abstraction of a View, which contains a View's states and behaviors. From another angle, it can also be said that the View layer is kind of like an UI projection of the ViewModel.

Up until now, you might start to wonder: "Isn't that just what Controller is in MVC? Are you just inventing a new term?". Of course not. Another and a more important feature of ViewModel in MVVM pattern is that a ViewModel 'knows nothing about View's visual elements'. This is the key that makes MVVM design pattern different from other MVC variances.

Why MVVM?

Separation of data and logic from presentation

The key feature that the ViewModel knows nothing about View's visual elements guarantees the one way dependency from View to the ViewModel thus avoiding mutual programming ripple effects between UI and the ViewModel. Consequently, it brings the following advantages:

  • As long as the contract is set(what data to show and what actions to proceed), the UI design and coding of ViewModel can be implemented in parallel and independently. Either side will not block the other's way.
  • UI design can be easily changed from time to time without changing the ViewModel as long as the contract does not change.
  • It will be easier to design different views for different devices with a common ViewModel. For a desktop browser with a bigger screen, more information can be shown on one page; while for a smart phone with limited display space, designing a wizard-based step-by-step operation UI can be done without the need to change (much of) the ViewModel.
  • Since ViewModel does not "see" presentation layer, users can unit-test the ViewModel functions easily without UI elements.

What does this have to do with ZK Bind?

Implementation-wise, no matter how, someone in the system has to help synchronizing data between View and ViewModel layers. Also, this someone has to accept the user request from the View layer and bridge the request to the action and logic provided by the ViewModel layer. This someone, the kernel part of the MVVM design pattern, is either data synchronising codes written by the application developers themselves or a data binding system provided by the framework. In ZK 6, ZK Bind is this key data binding infrastructure of the MVVM design pattern.

It would be easier to explain the concept with an example which has two different implementations. One in Model-View-Presenter(MVP)[1] design pattern and another in Model-View-ViewModel(MVVM).

  1. Model-View-Presenter(MVP) is another variance of MVC design pattern. If you are not aware of what that is, don't worry, just view the example codes.

The example -- On Demand Hello World!

The use case: Press a button on the screen and show on the screen a message provided by the Model, say, "Hello World!".

MVP Implementation

SmallTalk MVP HELLO FLOW.png

  1. A user push a button(an action) on the screen. A corresponding event is fired.
  2. An event listener on the back-end handles this event.
  3. Access data and so on.
  4. Then UI elements were changed accordingly to provide visual feedback to the user.

View: helloMVP.zul

<window apply="HelloComposer">
	<label id="lbl"/>
	<button id="btn" label="Show"/>
</window>

Presenter: HelloComposer.java

    public class HelloComposer extends GenericForwardComposer { 
        private Label lbl;

        public void onClick$btn(Event event) { 
            lbl.setValue("Hello World!");
        } 
    }

The Presenter was injected with the View components Label lbl and the event listener onClick$btn(). As shown above, the characteristics of a MVP design pattern is that the Presenter(the Controller) would need to hold references to the View layer UI components so that it can update the states of the View and generate visual feedback. As in this example, the value of the Label component lbl is updated with "Hello World" and provides visual feedback to the user.

MVVM Implementation

SmallTalk MVVM HELLO FLOW.png

As stated in the paragraph earlier, data binder plays the role of syncing data between UI and ViewModel.

  1. A user presses a button on the screen.
  2. A corresponding event is fired to the binder.
  3. The binder finds the corresponding action logic in the ViewModel and calls it.
  4. The action logic accesses data from Model layer and updates corresponding properties of the ViewModel.
  5. ViewModel notify the binder that some properties have been changed.
  6. Per what properties have been changed, the binder loads data from the ViewModel.
  7. Binder then updates the corresponding UI controls to provide visual feedback to the user.

Apparently, UI designer has to tell binder at least the following things:

  • Which UI event is used to proceed which action logic? (so binder knows what method to call).
  • Which UI attribute is used to show which data? (so binder knows what to load and what to update).
  • Which UI attribute is used to input into which data? (so binder knows what property to save).

In ZK Bind, ZK Annotation is utilized to do these jobs:

View: helloMVVM.zul

<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')">
	<label value="@load(vm.message)"/>
	<button label="Show" onClick="@command('showHello')"/>
</window>

ViewModel: HelloViewModel.java

public HelloViewModel {
	private String message;
	public String getMessage() {
		return message;
	}
	
	@Command @NotifyChange("message")
	public void showHello() {
		message = "Hello World!";
	}
}

Per this example, let's run through the program flow:

  1. When end user press the "Show" button, the onClick event is fired to the binder.
  2. The binder finds that the command name in the ViewModel which is "showHello" is as specified in the ZK annotation @command('showHello').
  3. The binder calls the showHello() method in the HelloViewModel and changes the message property. Note that the @Command and the @NotifyChange("message") Java method annotations on showHello() method in HelloViewModel. @Command tells the binder that the method is for a command while @NotifyChange("message") tells the binder that the property message in the HelloViewModel will be changed if this command is called.
  4. The binder then finds that the attribute value of component label is associated with the changed message property of the HelloViewModel(as specified in ZK annotation @load(vm.message)). So it loads data from the property vm.message by calling getMessage() getter method of the HelloViewModel and updates the label's value attribute. The new message "Hello World!" is then shown on the screen and provides the visual feedback to the end user.

In this MVVM implementation with data binding system, the HelloViewModel is a simple POJO and refers none of the UI components. It only exposes the message contents via getMessage() and provides the necessary action logic showHello(). It does not know how these information or action logic will be used. It is the UI designer's job to decide which UI components are to be used in which layout as seen in HelloMVVM.zul.

The example revised -- Pop Up the Message

Customers tend to change their minds when they see the real screen. Say, after you demonstrated the program to a customer and he/she said, "Well, I thought the Hello World! message should be in a pop up window...".

No problem, let's prepare a modal window and put the message in the modal window. Easy.

Revised MVP Implementation

View: helloMVP2.zul

<window apply="HelloComposer2">
	<button id="btn" label="Show"/>
	<window id="popwin" title="Hello" width="300px" height="200px" visible="false">
		<hbox align="center" pack="center" hflex="true" vflex="true">
			<label id="lbl"/>
		</hbox>
	</window>
</window>

Presenter: HelloComposer2.java

public class HelloComposer2 extends GenericForwardComposer {
	private Label popwin$lbl;
	private Window popwin;
	
	public void onClick$btn(Event event) {
		popwin$lbl.setValue("Hello World!");
		popwin.doModal();
	}
}

The View zul file has to be revised and the Presenter java file has to be totally rewritten. This time, it injects the pop up window popwin, the label popwin$lbl inside the pop up window and so on because the View has changed.

Revised MVVM Implementation

View: helloMVVM2.zul

<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')">
	<button label="Show" onClick="@command('showHello')"/>
	<window title="Hello" width="300px" height="200px" mode="modal" 		
		visible="@load(not empty vm.message)">
		<hbox align="center" pack="center" hflex="true" vflex="true">
			<label value="@load(vm.message)"/>
		</hbox>
	</window>
</window>

The advantages of using MVVM design pattern comes in when customers changes their requirements on the View. UI Designers can proceed the changes independently and the HelloViewModel java file does not have to be changed because what the customer required was to change the way how the message is presented not the message itself. Notice that here the show/hide of the modal window is controlled by whether the message is empty or not(visible="@load(not empty vm.message)").

ZK Bind Features

To make ZK Bind as easy to use as possible, many features have been implemented. These features will be covered and introduced one by one in the coming articles with examples commonly seen in real world applications. Here is a brief features list: (For details, please see this.)

  • One way binding in Save direction only
<textbox value="@save(a.b.c)"/>
  • One way binding in Load direction only:
<textbox value="@load(a.b.c)"/>
  • Two way binding
<textbox value="@load(a.b.c) @save(a.b.c)"/>

equals to

<textbox value="@bind(a.b.c)"/>
  • Template binding
<listbox model="@load(vm.persons)" selectedItem="@bind(vm.selected)">
    <template name="model" var="p">
        <listitem label="@load(p.firstname)"/>
    </template>
</listbox>
  • Form binding
<grid form="@id('fx') @save(vm.currentTask, before='updateTask')">
    <row><textbox value="@bind(fx.name)"/></row>
    <row><textbox value="@bind(fx.description)"/></row>
    <row><datebox value="@bind(fx.date) @converter('formatedDate', format='yyyy/MM/dd')"/></row>
</grid>
  • Conditional binding: binding on different commands
<grid form="@id('fx') @save(vm.currentTask, before='updateTask') @save(vm.newTask, before='addTask')">
    <row><textbox value="@bind(fx.taskName)"/></row>
    <row><textbox value="@bind(fx.description)"/></row>
    <row><datebox value="@bind(fx.date) @converter('formatedDate', format='yyyy/MM/dd')"/></row>
</grid>
  • EL 2.2 powered
<image src="@load(person.boy ? 'boy.png' : 'girl.png')"/>
<button onClick="@command(vm.add ? 'add' : 'update')" />
<button disabled="@load(empty vm.symbol)"/>
  • Java annotated property dependency tracking (in ViewModel)
@NotifyChange
public void setFirstname(String firstname) {
    this.firstname = firstname;
}

@NotifyChange
public void setLastname(String lastname) {
    this.lastname = lastname;
}

@DependsOn({"firstname", "lastname"})
public String getFullname() {
    return firstname + " " + lastname;
}

Summary

In this article, I have mentioned the design pattern MVVM and its advantages. I have also touched upon the basic operations of ZK's new generation data binding system - the ZK Bind. The On Demand Hello World example presents the basic idea of MVVM and how ZK Bind works. However, it may appear to be too simple for an impressive real world application. We will address more functions of the ZK Bind with more examples in up coming series of articles including the concepts of conditional binding, form binding, template binding, and separation of loading and saving, etc.. Stay tuned.

Demo and Download

Comments



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