Hello ZK MVVM"
m (correct highlight (via JWB)) |
|||
(37 intermediate revisions by 7 users not shown) | |||
Line 6: | Line 6: | ||
__TOC__ | __TOC__ | ||
− | This is the first article of a series of articles about ZK and MVVM design pattern. | + | 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 | + | 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 [http://martinfowler.com/eaaDev/PresentationModel.html Presentation Model] introduced by Martin Fowler, a special | + | 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 for View in MVC pattern which is responsible for exposing | + | 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?== | ==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 | + | 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 | + | *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 bigger screen, | + | *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, | + | *Since ViewModel does not "see" presentation layer, users can unit-test the ViewModel functions easily without UI elements. |
− | ==What does this | + | ==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 | + | 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 | + | 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]] | ||
− | # | + | #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 | + | #Then UI elements were changed accordingly to provide visual feedback to the user. |
''View'': helloMVP.zul | ''View'': helloMVP.zul | ||
Line 57: | Line 60: | ||
''Presenter'': 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 ''Presenter'' was injected with the View components Label < | + | 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 | + | As stated in the paragraph earlier, data binder plays the role of syncing data between UI and ViewModel. |
− | # | + | #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 | + | #The binder finds the corresponding action logic in the ViewModel and calls it. |
− | #The action logic | + | #The action logic accesses data from Model layer and updates corresponding properties of the ViewModel. |
− | # | + | #ViewModel notify the binder that some properties have been changed. |
− | #Per what properties have been changed, the binder | + | #Per what properties have been changed, the binder loads data from the ViewModel. |
− | #Binder then updates the corresponding UI controls to provide visual | + | #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 method 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 property to save). | *Which UI attribute is used to input into which data? (so binder knows what property to save). | ||
− | In ZK | + | In ZK Bind, ZK Annotation is utilized to do these jobs: |
''View'': helloMVVM.zul | ''View'': helloMVVM.zul | ||
<source lang="xml"> | <source lang="xml"> | ||
− | <window apply="BindComposer" viewModel="@ | + | <window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')"> |
− | <label value="@ | + | <label value="@load(vm.message)"/> |
− | <button label="Show" onClick="@ | + | <button label="Show" onClick="@command('showHello')"/> |
</window> | </window> | ||
</source> | </source> | ||
''ViewModel'': HelloViewModel.java | ''ViewModel'': HelloViewModel.java | ||
− | <source lang="java"> | + | <source lang="java" highlight="3,7"> |
public HelloViewModel { | public HelloViewModel { | ||
private String message; | private String message; | ||
Line 104: | Line 107: | ||
} | } | ||
− | @NotifyChange("message") | + | @Command @NotifyChange("message") |
public void showHello() { | public void showHello() { | ||
message = "Hello World!"; | message = "Hello World!"; | ||
Line 112: | 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 is "showHello" as specified in the ZK annotation < | + | #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 < | + | #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 finds that the attribute < | + | #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 < | + | 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 -- Pop Up the Message= | =The example revised -- Pop Up the Message= | ||
− | Customers tend to change their | + | 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 | + | No problem, let's prepare a modal window and put the message in the modal window. Easy. |
==Revised MVP Implementation== | ==Revised MVP Implementation== | ||
Line 138: | Line 141: | ||
''Presenter'': 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 popwin$lbl; | private Label popwin$lbl; | ||
Line 150: | Line 153: | ||
</source> | </source> | ||
− | 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 < | + | 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== | ||
''View'': helloMVVM2.zul | ''View'': helloMVVM2.zul | ||
− | <source lang="xml"> | + | <source lang="xml" highlight="4"> |
− | <window apply="BindComposer" viewModel="@ | + | <window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('org.zkoss.mvvm.examples.hello.HelloViewModel')"> |
− | <button label="Show" onClick="@ | + | <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="@ | + | 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="@ | + | <label value="@load(vm.message)"/> |
</hbox> | </hbox> | ||
</window> | </window> | ||
</window></source> | </window></source> | ||
− | The advantages of using MVVM design pattern | + | 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, | + | 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="@ | + | <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="@ | + | <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 185: | Line 192: | ||
*Template binding | *Template binding | ||
<source lang="xml"> | <source lang="xml"> | ||
− | <listbox model="@ | + | <listbox model="@load(vm.persons)" selectedItem="@bind(vm.selected)"> |
<template name="model" var="p"> | <template name="model" var="p"> | ||
− | <listitem label="@ | + | <listitem label="@load(p.firstname)"/> |
</template> | </template> | ||
</listbox> | </listbox> | ||
Line 193: | Line 200: | ||
*Form binding | *Form binding | ||
<source lang="xml"> | <source lang="xml"> | ||
− | <grid | + | <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 201: | Line 208: | ||
*Conditional binding: binding on different commands | *Conditional binding: binding on different commands | ||
<source lang="xml"> | <source lang="xml"> | ||
− | <grid | + | <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 209: | Line 216: | ||
*EL 2.2 powered | *EL 2.2 powered | ||
<source lang="xml"> | <source lang="xml"> | ||
− | <image src="@ | + | <image src="@load(person.boy ? 'boy.png' : 'girl.png')"/> |
− | <button onClick="@ | + | <button onClick="@command(vm.add ? 'add' : 'update')" /> |
− | <button disabled="@ | + | <button disabled="@load(empty vm.symbol)"/> |
</source> | </source> | ||
*Java annotated property dependency tracking (in ViewModel) | *Java annotated property dependency tracking (in ViewModel) | ||
Line 233: | Line 240: | ||
=Summary= | =Summary= | ||
− | In this article, I have mentioned the design pattern MVVM and its advantages. I also | + | 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= | ||
+ | *[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
Henri Chen, Principal Engineer, Potix Corporation
October 17, 2011
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).
- ↑ 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
- 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.
- Access data and so on.
- 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
As stated in the paragraph earlier, data binder plays the role of syncing data between UI and ViewModel.
- A user presses a button on the screen.
- A corresponding event is fired to the binder.
- The binder finds the corresponding action logic in the ViewModel and calls it.
- The action logic accesses data from Model layer and updates corresponding properties of the ViewModel.
- ViewModel notify the binder that some properties have been changed.
- Per what properties have been changed, the binder loads data from the ViewModel.
- 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:
- 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" is as specified in the ZK annotation
@command('showHello')
. - The binder calls the
showHello()
method in theHelloViewModel
and changes themessage
property. Note that the@Command
and the@NotifyChange("message")
Java method annotations onshowHello()
method inHelloViewModel
.@Command
tells the binder that the method is for a command while@NotifyChange("message")
tells the binder that the propertymessage
in theHelloViewModel
will be changed if this command is called. - The binder then finds that the attribute
value
of componentlabel
is associated with the changedmessage
property of theHelloViewModel
(as specified in ZK annotation@load(vm.message)
). So it loads data from the propertyvm.message
by callinggetMessage()
getter method of theHelloViewModel
and updates the label'svalue
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. |