Hello ZK MVVM"

From Documentation
m
(new zk bind syntax)
Line 93: Line 93:
 
''View'': helloMVVM.zul
 
''View'': helloMVVM.zul
 
<source lang="xml">
 
<source lang="xml">
<window apply="org.zkoss.bind.BindComposer" viewModel="@bind(vm='org.zkoss.mvvm.examples.hello.HelloViewModel')">
+
<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')">
<label value="@bind(vm.message)"/>
+
<label value="@load(vm.message)"/>
<button label="Show" onClick="@bind('showHello')"/>
+
<button label="Show" onClick="@command('showHello')"/>
 
</window>
 
</window>
 
</source>
 
</source>
Line 107: Line 107:
 
}
 
}
 
 
@NotifyChange("message")
+
@Command @NotifyChange("message")
 
public void showHello() {
 
public void showHello() {
 
message = "Hello World!";
 
message = "Hello World!";
Line 115: 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 that the command name in the ViewModel which 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 <tt>@load('showHello')</tt>.
#The binder calls the <tt>showHello()</tt> method in the <tt>HelloViewModel</tt> and changes the <tt>message</tt> property. Note that the <tt>@NotifyChange("message")</tt> Java method annotation on <tt>showHello()</tt> method in <tt>HelloViewModel</tt>. It tells the binder that the property <tt>message</tt> in the <tt>HelloViewModel</tt> will be changed if this command is called.
+
#The binder calls the <tt>showHello()</tt> method in the <tt>HelloViewModel</tt> and changes the <tt>message</tt> property. Note that the <tt>@Command</tt> and the <tt>@NotifyChange("message")</tt> Java method annotations on <tt>showHello()</tt> method in <tt>HelloViewModel</tt>. <tt>@Command</tt> tells the binder that the method is for a command while <tt>@NotifyChange("message")</tt> tells the binder that the property <tt>message</tt> in the <tt>HelloViewModel</tt> will be changed if this command is called.
#The binder then finds that the attribute <tt>value</tt> of component <tt>label</tt> is associated with the changed <tt>message</tt> property of the <tt>HelloViewModel</tt>(as specified in ZK annotation <tt>@bind(vm.message)</tt>). 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 provides the visual feedback to the end user.
+
#The binder then finds that the attribute <tt>value</tt> of component <tt>label</tt> is associated with the changed <tt>message</tt> property of the <tt>HelloViewModel</tt>(as specified in ZK annotation <tt>@load(vm.message)</tt>). 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 provides the visual feedback to the end user.
  
 
In this MVVM implementation with data binding system, the <tt>HelloViewModel</tt> is a simple POJO and 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 are to be used in which layout as seen in <tt>HelloMVVM.zul</tt>.
 
In this MVVM implementation with data binding system, the <tt>HelloViewModel</tt> is a simple POJO and 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 are to be used in which layout as seen in <tt>HelloMVVM.zul</tt>.
Line 158: Line 158:
 
''View'': helloMVVM2.zul
 
''View'': helloMVVM2.zul
 
<source lang="xml">
 
<source lang="xml">
<window apply="org.zkoss.bind.BindComposer" viewModel="@bind(vm='org.zkoss.mvvm.examples.hello.HelloViewModel')">
+
<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')">
<button label="Show" onClick="@bind('showHello')"/>
+
<button label="Show" onClick="@command('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 comes in when customers changes their requirements on the View. UI Designers can proceed the changes independently and the <tt>HelloViewModel</tt> 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 whether the <tt>message</tt> is empty or not(<tt>visible="@bind(not empty vm.message)"</tt>).
+
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 <tt>HelloViewModel</tt> 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 whether the <tt>message</tt> is empty or not(<tt>visible="@load(not empty vm.message)"</tt>).
  
 
=ZK Bind Features=
 
=ZK Bind Features=
Line 176: Line 176:
 
*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>
Line 204: Line 208:
 
*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>
Line 212: Line 216:
 
*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 tracking (in ViewModel)
 
*Java annotated property dependency tracking (in ViewModel)
Line 239: Line 243:
  
 
=Demo and Download=
 
=Demo and Download=
*[http://zkfiddle.org/sample/3sk0uv8/5-Hello-ZK-MVVM HelloMVVM]
+
*[http://zkfiddle.org/sample/3sk0uv8/10-Hello-ZK-MVVM HelloMVVM]
*[http://zkfiddle.org/sample/2k17s4l/4-Hello-ZK-MVVM2 HelloMVVM2]
+
*[http://zkfiddle.org/sample/2k17s4l/10-Hello-ZK-MVVM2 HelloMVVM2]
{{Template:CommentedSmalltalk_Footer_new|
+
{{Template:CommentedSmalltalk_Footer|
 
|name=Potix Corporation
 
|name=Potix Corporation
 
}}
 
}}

Revision as of 07:47, 6 December 2011

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 @load('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 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 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.