MVVM Extension: Access UI Components Inside ViewModel

From Documentation
DocumentationSmall Talks2012JanuaryMVVM Extension: Access UI Components Inside ViewModel
MVVM Extension: Access UI Components Inside ViewModel

Author
Hawk Chen, Engineer, Potix Corporation
Date
January 31, 2012
Version
ZK 6

Preface

Developing an application in MVVM pattern has several advantages including the clear separation between View and back-end and how you can easily perform a unit test. However, sometime developers want more control of components like dynamically creating components, to customize load on demand or sometimes it's just a personal preference. This article will show you how you can access UI components inside a ViewModel to customize components with a little bit of extra work. But we do not encourage this approach.

Extended Case Scenario: Popup Detail

This article will use the search example mentioned in the article MVVM in ZK 6 - Design your first MVVM page as a base to show you how to further customize components when it is designed using the MVVM pattern. In the search example, an item's description is shown in detail only when a user clicks an item in the listbox (illustrated in the below image). However, it is more convenient for end users to be able to see detailed description simply by mouse hovering an item i.e. without clicking it. I will show you how to implement this feature through MVVM with autowiring components.

Smalltalks-mvvm-in-zk6-view-example.png

Implementation Steps

  1. Add Popup
  2. Add a popup component to display item description.

    searchMvvmAutowire.zul

    	<popup id="msgPopup">
    		<label id="msg"></label>
    	</popup>
    

  3. Enable Autowire of ViewModel
  4. Since a ViewModel is basically a POJO, it does not have the ability to wire UI components, we therefore enable ViewModel to wire components with @init.

    SearchAutoWireVM.java

    public class SearchAutowireVM{
    	//other variables
    	//UI component
    	@Wire("#msgPopup")
    	Popup popup;
    	@Wire("#msg")
    	Label msg;
    
    	@Init
    	public void init(@ContextParam(ContextType.VIEW) Component view){
    		Selectors.wireComponents(view, this, false);
    		//wire event listener
    //		Selectors.wireEventListeners(component, this);
    	}
    	//...
    }
    
    • Here we use "wire by selector", "#msgPopup" which means to wire a component with id "msgPopup". (line 6) This feature is provided by SelectorComposer mentioned in another small talk.
    • We annotate a method public void init(BindContext) with @Init, then the binder will invoke this method while initializing the ViewModel. (line 9)
    • We also apply @ContextParam(ContextType.VIEW) to parameter "view", and binder will pass the parameter with binder's View component. It is the component which applies org.zkoss.bind.BindComposer. In this example, it's a window (line 10).
    • Inside the method, Selectors.wireComponents() will wire those variables with @Wire. (line 11)
    • Selectors.wireEventListeners(component, this) will wire those methods with @Listen annotations as event listeners. There is no event listener in this example, so we comment out this method calling.(line 13)

  5. Popup Message
  6. i. Manipulate popup component to display a popup message at the end of listitem.

    SearchAutoWireVM.java

    	@Command
    	public void popupMessage(@BindingParam("target")Component targetComponent, @BindingParam("content")String content){
    		msg.setValue(content);
    		popup.open(targetComponent,"end_before");
    	}
    
    • Remember that the variables in this method, "msg" and "popup" are components that are wired by @Wire (line 3,4).
    • Notice that this method's two arguments are annotated with @BindingParam("target") . The @BindingParam("target") annotation declares that the argument should be passed from the binder (in ZUL) when the method is invoked and that passed-in argument is specified in the ZUL file with key "target" (illustrated in the searchMvvmAutowire.zul code snippet below, line 8). The target is the target component which the popup message displays on. The content is the message to be displayed.


    ii. Bind @command to mouse over event on listitem.

    searchMvvmAutowire.zul

    <listbox model="@load(vm.items)" selectedItem="@bind(vm.selected)" hflex="true" height="300px">
    	<listhead>
    		<listheader label="Name"/>
    		<listheader label="Price" align="center" />
    		<listheader label="Quantity" align="center"  />
    	</listhead>
    	<template name="model" var="item">
    		<listitem onMouseOver="@command('popupMessage', target=self, content=item.description)">
    			<listcell label="@bind(item.name)"/>
    			<listcell label="@bind(item.price)@converter('formatedNumber', format='###,##0.00')"/>
    			<listcell label="@bind(item.quantity)" sclass="@bind(item.quantity lt 3 ?'red':'')"/>
    		</listitem>
    	</template>
    </listbox>
    

    We should pass the two arguments with key "target" and "content" respectively upon the command method's signature. The order of argument is unimportant but the key and type should correspond to the command method's arguments.

Final Result

Smalltalks-mvvm-in-zk6-autowire-popup-description.png

When users mouse hover an item, a popup of description for that item displays at the end of the row. Users can read the details of an item instantly without having to click on them one by one.

Conclusion

Following rigid principle of MVVM pattern is encouraged, this approach we introduced here is an alternative if it better suit your needs. Although it couples the ViewModel with View, it is easy to return to pure MVVM pattern by simply removing annotations in Java and add ZK bind expressions in ZUL. Developers are offered the flexibility to fit required context and environment accordingly.

See Also

  1. Envisage ZK 6: The Next Generation Data Binding System
  2. Hello ZK MVVM
  3. MVVM in ZK 6 - Design your first MVVM page



Comments



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