Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Wednesday, July 11, 2012

ZK in Action [3] : MVVM - Working Together with ZK Client API


In the previous posts we've implemented the following functionalities with ZK's MVVM:

A key distinction between ZK MVVM and ZK MVC implementation wise is that we do not access and manipulate the UI components directly in the controller(ViewModel) class. In this post, we'll see how we can delegate some of the UI manipulation to client side code, as well as how to pass parameters from View to ViewModel.

Objective

Build an update function to our simple inventory CRUD feature. Users can edit-in-place entries in the table and given the choice to update or discard the changes made. Modified entries are highlighted in red.



ZK Features in Action

  • ZK Client-side APIs
  • ZK Style Class
  • MVVM : Pass Parameters from View to ViewModel

Implementation in Steps

Enable In-place editing in Listbox so we can edit entries:

<listcell>
       <textbox inplace="true" value="@load(each.name)" ...</textbox>
   </listcell>
   ....
   <listcell>
       <doublebox inplace="true" value="@load(each.price)" ...</textbox>
   </listcell>
   ...
  • inplace="true" renders input elements such as Textbox without their borders displaying them as plain labels; the borders appear only if the input element is selected
  • line 2, 6,"each" refers to each Item object in the data collection

Once an entry is edited we want to give users the option to Update or Discard the change. 

The Update and Discard buttons need to be visible only if user has made modifications on the Listbox entries. First we define JavaScript functions to show and hide the Update and Discard buttons:
<toolbar>
    ...
    <span id="edit_btns" visible="false" ...>
        <toolbarbutton label="Update" .../>
        <toolbarbutton label="Discard" .../>
    </span>
</toolbar>

    <script type="text/javascript">
        function hideEditBtns(){
     jq('$edit_btns').hide();
        }
  
        function showEditBtns(){ 
     jq('$edit_btns').show();
        }

    </script>
    ...
  • line 2, we wrap the Update and Discard and set visibility to false
  • line 9, 13, we define functions which hide and show the Update and Discard buttons respectively
  • line 11, 15, we make use of jQuery selector jq( '$edit_btns') to retrieve the ZK widget whose ID is "edit_btns"; notice the selector pattern for a ZK widget ID is '$', not '#'

When entries in the Listbox are modified, we'll make the Update/Discard buttons visible and make the modified values red. Once either Update or Discard button is clicked, we'd like to hide the buttons again

Since this is pure UI interactions, we'll make use of ZK's client side APIs:
<style>
   .inputs { font-weight: 600; }
   .modified { color: red; }
</style>
...
    <toolbar xmlns:w="client" >
    ...
    <span id="edit_btns" visible="false" ...>
         <toolbarbutton label="Update" w:onClick="hideEditBtns()" .../>
         <toolbarbutton label="Discard" w:onClick="hideEditBtns()" .../>
    </span>
    </toolbar>

    <script type="text/javascript">
        //show hide functions

        zk.afterMount(function(){
            jq('.inputs').change(function(){
            showEditBtns();
            $(this).addClass('modified');
     })
        });
    </script>
    ...
    <listcell>
       <doublebox inplace="true" sclass="inputs" value="@load(each.price)" ...</textbox>
   </listcell>
   ...
  • line 2, we specify a style class for our input elements (Textbox, Intbox, Doublebox, Datebox) and assign it to input elements' sclass attribute, eg. line 26; sclass defines style class for ZK widgets
  • line 18~20, we get all the input elements by matching their sclass name and assign an onChange event handler. Once the value in an input element is changed, the Update/Discard buttons will become visible and the modified value will be highlighted in red.
  • line 17, zk.afterMount is run when ZK widgets are created
  • line 6, we specify the client namespace so we can register client side onClick event listeners with the syntax "w:onClick". Note we can still register our usual onClick event listener that's handled at the server simultaneously.
  • line 9, 10, we assign client side onClick event listener; the hideEditBtns function would be called to make the buttons invisible again

Define a method to store the modified Item objects into a collection so the changes could be updated in batch if user choose to do so:

public class InventoryVM {

    private HashSet<Item> itemsToUpdate = new HashSet<item>();
    ...

    @Command
    public void addToUpdate(@BindingParam("entry") Item item){
        itemsToUpdate.add(item);
    }
  • line 6, we annotate this method as a command method so it can be invoked from View
  • line 7, @BindingParam("entry") Item item binds an arbitrarily named parameter called "entry"; we anticipate the parameter would be of type Item

Create a method to update the changes made in View to the Data Model

public class InventoryVM {

    private List<Item> items;
    private HashSet<Item> itemsToUpdate = new HashSet<item>();
    ...

    @NotifyChange("items")
    @Command
    public void updateItems() throws Exception{
        for (Item i : itemsToUpdate){
            i.setDatemod(new Date());
            DataService.getInstance().updateItem(i);
        }
        itemsToUpdate.clear();
        items = getItems();
    }



When modifications are made on Listbox entries, invoke the addToUpdate method and pass to it the edited Item object which in turn is saved to the itemsToUpdate collection

<listitem>
 <listcell>
  <doublebox value="@load(each.price) 
                @save(each.name, before='updateItems')"  
                onChange="@command('addToUpdate',entry=each)" />
 </listcell>
 ...
</listitem>
  • @save(each.name, before='updateItems') ensures that modified values are not saved unless updateItems is called (ie. when user click the "Update" button)

Finally, when user clicks Update, we call the updateItems method to update changes to Data Model. If Discard is clicked, we call getItems to refresh the Listbox without applying any changes

...
 <toolbarbutton label="Update" onClick="@command('updateItems')" .../>
 <toolbarbutton label="Discard" onClick="@command('getItems')" .../>
 ...


In a Nutshell

  • Under the MVVM pattern we strive to keep the ViewModel code independent of any View components
  • Since we don't have direct reference to the UI components in the ViewModel code, we can delegate the UI manipulation (in our sample code, show/hide, style change) code to the client using ZK's client side APIs
  • We can make use of jQuery selectors and APIs at ZK client side
  • We can easily pass parameters from View to ViewModel with @BindingParam
Next up, we'll go over a bit more on ZK Styling before we tackle the MVVM validators and converters.

Reference

ViewModel (ZK in Action[0]~[3]):
public class InventoryVM {

 private List<Item> items;
 private Item newItem;
 private Item selected;
 private HashSet<Item> itemsToUpdate = new HashSet<Item>();
 
 public InventoryVM(){}
 
 //CREATE
 @NotifyChange("newItem")
 @Command
 public void createNewItem(){
  newItem = new Item("", "",0, 0,new Date());
 }
 
 @NotifyChange({"newItem","items"})
 @Command
 public void saveItem() throws Exception{
  DataService.getInstance().saveItem(newItem);
  newItem = null;
  items = getItems();
 }
  
 @NotifyChange("newItem")
 @Command
 public void cancelSave() throws Exception{
  newItem = null;
 }
 
 //READ
 @NotifyChange("items")
 @Command
 public List<Item> getItems() throws Exception{
  items = DataService.getInstance().getAllItems();
  for (Item j : items){
   System.out.println(j.getModel());
  }
  Clients.evalJavaScript("zk.afterMount(function(){jq('.inputs').removeClass('modified').change(function(){$(this).addClass('modified');showEditBtns();})});"); //how does afterMount work in this case?
  return items;
 }
 
 //UPDATE
 @NotifyChange("items")
 @Command
 public void updateItems() throws Exception{
  for (Item i : itemsToUpdate){
   i.setDatemod(new Date());
   DataService.getInstance().updateItem(i);
  }
  itemsToUpdate.clear();
  items = getItems();
 }
 
 @Command
 public void addToUpdate(@BindingParam("entry") Item item){
  itemsToUpdate.add(item);
 }
 
 //DELETE
 @Command
 public void deleteItem() throws Exception{
  if (selected != null){
  
   String str = "The item with name \""+selected.getName()+"\" and model \""+selected.getModel()+"\" will be deleted.";
   Messagebox.show(str,"Confirm Deletion", Messagebox.OK|Messagebox.CANCEL, Messagebox.QUESTION, 
    new EventListener<Event>(){
     @Override
     public void onEvent(Event event) throws Exception {
      if (event.getName().equals("onOK")){
       DataService.getInstance().deleteItem(selected);
       items = getItems();
       BindUtils.postNotifyChange(null, null, InventoryVM.this, "items");
      }
     }
   });
   
  } else {
   Messagebox.show("No Item was Selected");
  } 
 }
 

 public Item getNewItem() {
  return newItem;
 }

 public void setNewItem(Item newItem) {
  this.newItem = newItem;
 }

 public Item getselected() {
  return selected;
 }

 public void setselected(Item selected) {
  this.selected = selected;
 }
}
View (ZK in Action[0]~[3]):
<zk>
 <style>
  .z-toolbarbutton-cnt { font-size: 17px;} .edit-btns {border: 2px
  solid #7EAAC6; padding: 6px 4px 10px 4px; border-radius: 6px;}
  .inputs { font-weight: 600; } .modified { color: red; }
 </style>
 <script type="text/javascript">
  function hideEditBtns(){ jq('$edit_btns').hide(); }

  function showEditBtns(){ jq('$edit_btns').show(); }

  zk.afterMount(function(){ jq('.inputs').change(function(){
  $(this).addClass('modified'); showEditBtns(); }) });
 </script>
 <window apply="org.zkoss.bind.BindComposer"
  viewModel="@id('vm') @init('lab.sphota.zk.ctrl.InventoryVM')"
  xmlns:w="client">
  <toolbar width="100%">
   <toolbarbutton label="Add"
    onClick="@command('createNewItem')" />
   <toolbarbutton label="Delete"
    onClick="@command('deleteItem')"
    disabled="@load(empty vm.selected)" />
   <span id="edit_btns" sclass="edit-btns" visible="false">
    <toolbarbutton label="Update" 
     onClick="@command('updateItems')" w:onClick="hideEditBtns()"/>
    <toolbarbutton label="Discard"
     onClick="@command('getItems')" w:onClick="hideEditBtns()" />
   </span>
  </toolbar>
  <groupbox mold="3d"
   form="@id('itm') @load(vm.newItem) @save(vm.newItem, before='saveItem')"
   visible="@load(not empty vm.newItem)">
   <caption label="New Item"></caption>
   <grid width="50%">
    <rows>
     <row>
      <label value="Item Name" width="100px"></label>
      <textbox value="@bind(itm.name)" />
     </row>
     <row>
      <label value="Model" width="100px"></label>
      <textbox value="@bind(itm.model)" />
     </row>
     <row>
      <label value="Unit Price" width="100px"></label>
      <decimalbox value="@bind(itm.price)"
       format="#,###.00" constraint="no empty, no negative" />
     </row>
     <row>
      <label value="Quantity" width="100px"></label>
      <spinner value="@bind(itm.qty)"
       constraint="no empty,min 0 max 999: 
       Quantity Must be Greater Than Zero" />
     </row>
     <row>
      <cell colspan="2" align="center">
       <button width="80px" label="Save" mold="trendy"
        onClick="@command('saveItem')"  />
       <button width="80px" label="Cancel" mold="trendy"
        onClick="@command('cancelSave')" />
      </cell>
     </row>
    </rows>
   </grid>
  </groupbox>
  <listbox selectedItem="@bind(vm.selected)" model="@load(vm.items) ">
   <listhead>
    <listheader label="Name" sort="auto" hflex="2" />
    <listheader label="Model" sort="auto" hflex="1" />
    <listheader label="Quantity" sort="auto" hflex="1" />
    <listheader label="Unit Price" sort="auto" hflex="1" />
    <listheader label="Last Modified" sort="auto" hflex="2" />
   </listhead>
   <template name="model">
    <listitem>
     <listcell>
      <textbox inplace="true" width="110px" sclass="inputs"
       value="@load(each.name) @save(each.name, before='updateItems')"
       onChange="@command('addToUpdate',entry=each)">
      </textbox>
     </listcell>
     <listcell>
      <textbox inplace="true" width="110px" sclass="inputs" 
       value="@load(each.model) @save(each.model, before='updateItems')"
       onChange="@command('addToUpdate',entry=each)" />
     </listcell>
     <listcell>
      <intbox inplace="true" sclass="inputs" 
       value="@load(each.qty) @save(each.qty, before='updateItems')"
       onChange="@command('addToUpdate',entry=each)" />
     </listcell>
     <listcell>
      <doublebox inplace="true" sclass="inputs" format="###,###.00" 
       value="@load(each.price) @save(each.price, before='updateItems')"
       onChange="@command('addToUpdate',entry=each)" />
     </listcell>
     <listcell label="@load(each.datemod)" />
    </listitem>
   </template>
  </listbox>
 </window>
</zk>

Sunday, March 18, 2012

jQuery Selector Inspired Controller in ZK 6's MVC Pattern

The MVC pattern is adopted pervasively among Web frameworks. Various flavours exist for the pattern but the common goal is to achieve separation of concerns.

Under the ZK framework, MVC implementation ultimately requires the Controller to gain reference to, and listen events coming from, the UI components in View. An interesting bit of ZK 6's MVC pattern is the CSS/jQuery Selector inspired mechanism in its Controller that makes this plumbing task simpler and more flexible.

ZK Primer


In simplicity, ZK is a component based and event driven Ajax Java framework. Understanding this basic description alone is enough to take us through its MVC pattern.

Component based

A component either declared in XML or in Java has its state maintained as a POJO in the JVM. A component is then rendered with a set of JavaScript instructions at client side.


Event Driven

Each UI component can listen to event(s). There's a variety of ways to register event listeners to the components.
Just to name a few here,
in XML:
<button onclick="...">
</button>
in Java:
Button btn = new Button();
btn.addEventListener("onClick", new EventListener(){...});       

MVC Nomenclature in ZK

The component declarations make up the View. Although it's possible to construct the UI in Java, akin to GWT or Swing, most would prefer writing mark-up in XML. A ZUL in ZK is an XML compliant page that contains the UI mark-up. One can consider a ZUL page as an Ajax enabled JSP page.

The Controller in ZK is a Java class that implements the Composer interface or extends any one of Composer's implementations. SelectorComposer is the target of our investigation in this post.

Selector Inspired Controller in Action


Consider a simple window that prompts a user to enter her name, email address, and select the journal she'd like to subscribe:

We'll examine how the server-side selector mechanism works in our controller class as we implement the following features:
  1. Displaying a list of the available journals for subscription in a combo box
  2. Clear all fields in this simple form when the "Clear" button is clicked

Let's first see the components in mark-up which our controller will work with:
<grid apply="lab.zkoss.mvc.SubscriptionComposer">
        ...
    <rows>
        <row>
            <label value="User Name"/>
            <textbox mold="rounded"/> 
        </row>
        <row>
            <label value="email"/>
            <textbox mold="rounded"/> 
        </row>
        <row>
            <cell ...>
                <label value="Please subscribe me to "></label>
                <combobox model="${journalListModel}">
                    <template name="model">
                       <comboitem label="${each.title}"/>
                    </template>
                </combobox>
            </cell>
        </row>
        <row>
            <cell ...>
                <button label="Clear" ..."/>
                <button label="Submit" ..."/>
            </cell>
        </row>
        </rows>
</grid>

In our controller class, to implement the said features under the ZK framework, we'd need to gain reference to the UI components so the list of available journals can be rendered in the combo box and the onClick event for the Clear button can be handled.

public class SubscriptionComposer extends SelectorComposer{

 @Wire("combobox")
 Combobox journalbox;

 @Wire("textbox, combobox")
 List<InputElement> inputs;
 
 
 public void doAfterCompose(Component comp) throws Exception{ 
  super.doAfterCompose(comp);
  JournalDAO jdao = new JournalDAO();
  List<Journal> journalList = jdao.findAll();
  ListModelList journalListModel = new ListModelList(journalList);
  journalbox.setModel(journalListModel);
  
 }
 
 @Listen("onClick = button[label='Clear']")
 public void clearAll(){
  for(InputElement i:inputs) i.setText("");
 }
}
Let's elaborate on how selectors are used.

The Controller's Scope

When a controller is "applied" to a component, all of the component's children components also become accessible to the controller. 

In our implementation, the grid component is applied with our controller:
<grid apply="lab.zkoss.mvc.SubscriptionComposer">
...
</grid>
Hence the grid and all of its children components define the scope which our controller SubscriptionComposer can take effect.

Component Wiring

The @Wire annotation on line 3 and 6 take in a CSS selector pattern as its parameter. With the pattern "combobox, the annotation associates the sole combo box in our UI mark-up with the Combobox instance, journalbox declared in the controller class.
One of the many alternatives to achieve the same exact wiring is to give the Combox an ID, for instance:

<combobox id="thisworks2">
...
</combobox>
and the parameter for the annotation would be:
@Wire("#thisworks2")
Combobox journalbox;

Component Initialization

Once we've obtained references to the UI components in View, we could initialize them accordingly.
The doAfterCompose method allows developers to insert instructions to be executed right after the component under effect and its children are created. It's a method invoked by the framework and must be implemented for all classes implementing the Composer interface; such as the SelectorComposer class which we're extending our SubscriptionComposer from.

For our hypothetical feature, we need to initialize our combo box by populating it with a list of journals available for subscription.
@Wire("combobox")
 Combobox journalbox;

 ...
 
 public void doAfterCompose(Component comp) throws Exception{ 
  super.doAfterCompose(comp);
  JournalDAO jdao = new JournalDAO();
  List<Journal> journalList = jdao.findAll();
  ListModelList<Journal> journalListModel = new ListModelList(journalList);
  journalbox.setModel(journalListModel);
  
 }

On line 11, the combo box which we obtained reference to via the selector mechanism, is given the model data we prepared on line 10. ListModelList is a wraper class that enables changes made in its wrapped collection to be updated on its host UI component accordingly.

<combobox model="${journalListModel}">
        <template name="model">
            <comboitem label="${each.title}"/>
        </template>
    </combobox>

Once the combo box is supplied with a list model, the template tag will iteratively create a combo item for each entry in the list model.

Event Listening

The @Listen annotation adds method clearAll, as an event listener for the onClick event, to the button matching the pattern button[label="Clear"].

@Wire("textbox, combobox")
 List<InputElement> inputs;
 
 @Listen("onClick = button[label='Clear']")
 public void clearAll(){
  for(InputElement i:inputs) i.setText("");
 }


As before, there're many alternatives to the selector pattern shown here. One possibility is cell:first-child button, since the "Clear" button is the only component that matches this pattern.

<cell ...>
        <button label="Clear" ..."/>
        <button label="Submit" ..."/>
    </cell>

The brevity of the clearAll method is made possible because a single annotation @Wire("textbox, combobox") in fact wired all fields in the UI to a list of ZK components.

In a Nutshell


In a typical ZK controller class implementation, before we can initialize a UI component, listen to its events, or change its state, we must first obtain a reference to that component.
This CSS/jQuery selector inspired controller gives us great flexibility in referencing the UI components of interest. A reference can be made by matching a component's ID, class name, component attributes, or by traversing through the component tree.
With this flexibility, changes made in the UI cause us minimum grief since the controller code can be updated as easily as coming up with new selector patterns.

References

ZK SmallTalks
ZK Developer's Reference