Sunday, December 30, 2012

Convert Mouse Position To Lat/Lng in Google Maps



Introduction

This article describe how to convert the mouse position x/y to the map lat/lng in Google Maps.

The Program

Simply get the bounds of map and calculate the lat/lng based on the mouse x/y and the width/henght of map div.

The result has a little bit bias, but accurate enough.

<!DOCTYPE html>
<!-- tested with google chrome -->
<html>
    <head>
        <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
        <script>
            var map,
                mLat,
                mLng;
                
            function initialize() {
                var mapOptions = {
                    zoom: 12,
                    center: new google.maps.LatLng(33.397, -81.644),
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                };
                map = new google.maps.Map(document.getElementById('map_canvas'),
                    mapOptions
                );
                // add a click event listener to
                // get the lat/lng from click event of map
                google.maps.event.addListener(map, 'click', function(event) {
                    mLat = event.latLng.lat();
                    mLng = event.latLng.lng();
                    document.getElementById('latMap').value = mLat;
                    document.getElementById('lngMap').value = mLng;
                });
            }
            function mapDivClicked (event) {
                var target = document.getElementById('map_canvas'),
                    posx = event.pageX - target.offsetLeft,
                    posy = event.pageY - target.offsetTop,
                    bounds = map.getBounds(),
                    neLatlng = bounds.getNorthEast(),
                    swLatlng = bounds.getSouthWest(),
                    startLat = neLatlng.lat(),
                    endLng = neLatlng.lng(),
                    endLat = swLatlng.lat(),
                    startLng = swLatlng.lng(),
                    lat = startLat + ((posy/350) * (endLat - startLat)),
                    lng = startLng + ((posx/500) * (endLng - startLng));

                document.getElementById('posX').value = posx;
                document.getElementById('posY').value = posy;
                document.getElementById('lat').value = lat; // calculated lat
                document.getElementById('lng').value = lng; // calculated lng

                // the error rate
                document.getElementById('latErr').value = ((mLat - lat) / (endLat - startLat) * 100).toFixed(2);
                document.getElementById('lngErr').value = ((mLng - lng) / (endLng - startLng) * 100).toFixed(2);
            }
            google.maps.event.addDomListener(window, 'load', initialize);
        </script>
    </head>
    <body>
        <div id="map_canvas" onclick="mapDivClicked(event);" style="height: 350px; width: 500px;"></div>
        <div>
            mouse x: <input id="posX" />
            mouse y: <input id="posY" />
        </div>
        <div>
            lat: <input id="lat" />
            lng: <input id="lng" />
        </div>
        <div>
            lat from map: <input id="latMap" />
            lng from map: <input id="lngMap" />
        </div>
        <div>
            lat error rate: <input id="latErr" /> %
            lng error rate: <input id="lngErr" /> %
        </div>
    </body>
</html>


The Result



Reference

https://developers.google.com/maps/documentation/javascript/reference

Download

File at github
https://github.com/benbai123/HTML_CSS_Javascript_practice/blob/master/WebService_WebAPIs/Google/GoogleMap/mouse_position_to_latlng.html

Thursday, December 20, 2012

ZK Listbox: Inplace Editing with Renderer and MVVM


Introduction

This article describe how to implement the inplace editing with renderer in MVVM.

Pre-request

ZK Listbox: Event Processing with Renderer and MVVM
http://ben-bai.blogspot.tw/2012/12/zk-listbox-event-processing-with.html

The Program

index.zul

A simple View that get/set value from ViewModel and trigger some Command as needed.

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <div apply="org.zkoss.bind.BindComposer"
        viewModel="@id('vm') @init('test.viewmodel.TestVM')">
        <hlayout>
            <vlayout>
                <listbox width="400px" model="@load(vm.personModel)"
                    selectedIndex="@bind(vm.selectedIndex)"
                    itemRenderer="test.renderer.InplaceEditingPersonRenderer"
                    onModelDataChange="@command('updateModelData')">
                </listbox>
                <hlayout>
                    <button label="show current data" onClick="@command('displayCurrentData')" />
                    <button label="add new" onClick="@command('addNew')" />
                    <button label="delete selected" onClick="@command('delSel')" />
                </hlayout>
            </vlayout>
            <textbox rows="10" width="350px" value="@load(vm.currentData)" />
        </hlayout>
    </div>
</zk>


TestVM.java

Provide data and handle data changed event as needed.

package test.viewmodel;

import java.util.ArrayList;
import java.util.List;

import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;

import test.data.Person;
import test.event.ModelDataChangeEvent;

/**
 * tested with ZK 6.0.2
 * @author ben
 *
 */
public class TestVM {
    private ListModelList<Person> _personModel;
    private int _selectedIndex = -1;
    /**
     * The hard coded person model
     * @return ListModelList contains several Person
     */
    public ListModel<Person> getPersonModel () {
        if (_personModel == null) {
            List<Person> l = new ArrayList<Person>();
            l.add(new Person("First Name One", "Last Name One", 21));
            l.add(new Person("First Name Two", "Last Name Two", 23));
            l.add(new Person("First Name Three", "Last Name Three", 25));
            _personModel = new ListModelList<Person>(l);
        }
        return _personModel;
    }
    /**
     * save the selected index while select a listitem
     * @param selectedIndex
     */
    public void setSelectedIndex (int selectedIndex) {
        _selectedIndex = selectedIndex;
    }
    /**
     * update selected index while delSel
     * @return
     */
    public int getSelectedIndex () {
        return _selectedIndex;
    }
    /**
     * return all Persons' data, can be considered as "save data"
     * @return
     */
    public String getCurrentData () {
        StringBuilder sb = new StringBuilder("");
        if (_personModel != null) {
            if (_selectedIndex >= 0) {
                Person p = _personModel.getElementAt(_selectedIndex);
                if (isValidPerson(p)) {
                    sb.append("Selected Person: \r\n")
                        .append(p.getFirstName()).append(" ")
                        .append(p.getLastName()).append(", age = ")
                        .append(p.getAge()).append("\r\n\r\n");
                }
            }
            for (Person p : _personModel.getInnerList()) {
                if (isValidPerson(p)) {
                    sb.append(p.getFirstName()).append(" ")
                        .append(p.getLastName()).append(", age = ")
                        .append(p.getAge()).append("\r\n");
                }
            }
        }
        return sb.toString();
    }
    /**
     * add a new Person to _personModel,
     * added to tail if no selected index,
     * added below current selected item if selected idnex >= 0
     */
    @Command
    public void addNew () {
        if (_selectedIndex >= 0) {
            _personModel.add(_selectedIndex+1, new Person("", "", null));
        } else {
            _personModel.add(new Person("", "", null));
        }
    }
    /**
     * remove selected Person from model
     */
    @Command
    @NotifyChange("_selectedIndex")
    public void delSel () {
        if (_selectedIndex >= 0) {
            _personModel.remove(_selectedIndex);
            _selectedIndex = -1;
        }
    }
    /**
     * update the value of a Person based on the index and Data
     * in the ModelDataChangeEvent
     * @param event ModelDataChangeEvent contains the information with respect the index and changed data
     */
    @Command
    public void updateModelData (
            @ContextParam(ContextType.TRIGGER_EVENT) ModelDataChangeEvent event) {
        Person oldPerson = _personModel.get(event.getIndex());;
        Person newPerson = (Person)event.getData();
        oldPerson.setFirstName(newPerson.getFirstName());
        oldPerson.setLastName(newPerson.getLastName());
        oldPerson.setAge(newPerson.getAge());
    }
    /**
     * simply notify that the currentData should be updated
     */
    @Command
    @NotifyChange("currentData")
    public void displayCurrentData () {

    }
    /**
     * confirm all fields of a Person is valid
     * @param p the Person to check
     * @return whether all fields of the Person is valid
     */
    private boolean isValidPerson (Person p) {
        return !(p.getFirstName() == null
                || p.getFirstName().isEmpty()
                || p.getLastName() == null
                || p.getLastName().isEmpty()
                || p.getAge() == null
                || p.getAge() < 0);
    }
}


InplaceEditingPersonRenderer.java

Render listheaders and listitems, initial sorting, the inner textbox/intbox will handle the onChange event and pass it to listbox as needed.

package test.renderer;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Textbox;

import test.data.Person;
import test.data.PersonComparator;
import test.event.ModelDataChangeEvent;

/**
 * tested with ZK 6.0.2
 * @author ben
 *
 */
public class InplaceEditingPersonRenderer implements ListitemRenderer<Person> {
    @SuppressWarnings("unchecked")
    /**
     * render Person to a listitem
     */
    public void render (Listitem listitem, Person data, int index) {
        Listbox listbox = listitem.getListbox();
        if (index == 0 && listbox.getListhead() == null) {
            createListhead().setParent(listbox);
        }
        listitem.setValue(data);

        addTextboxCell(listitem, data.getFirstName())
            .addEventListener(Events.ON_CHANGE, getFirstnameChangedListener(listbox, data, listitem));
        addTextboxCell(listitem, data.getLastName())
            .addEventListener(Events.ON_CHANGE, getLastnameChangedListener(listbox, data, listitem));
        Intbox ibx = addIntboxCell(listitem, data.getAge());
        ibx.addEventListener(Events.ON_CHANGE, getAgeChangedListener(listbox, ibx, data, listitem));
    }
    /**
     * create listhead
     * @param listbox, append the listhead to it
     */
    private Listhead createListhead () {

        Listhead lh = new Listhead();
        createListheader("First Name", "firstName").setParent(lh);
        createListheader("Last Name", "lastName").setParent(lh);
        createListheader("Age", "age").setParent(lh);
        return lh;
    }
    /**
     * create a listheader
     * @param label the label of the listheader
     * @param fieldToCompare the value to set to PersonComparator's 'field' field
     * @return
     */
    private Listheader createListheader (String label, String fieldToCompare) {
        Listheader lhr = new Listheader(label);
        lhr.setSortAscending(new PersonComparator(true, fieldToCompare));
        lhr.setSortDescending(new PersonComparator(false, fieldToCompare));
        return lhr;
    }
    /**
     * add a listcell contains a textbox and return the textbox
     * @param listitem the listitem to append this listcell
     * @param value the value to set to the textbox
     * @return Textbox the created textbox
     */
    private Textbox addTextboxCell (Listitem listitem, String value) {
        Listcell lc = new Listcell();
        Textbox tbx = new Textbox(value);
        tbx.setParent(lc);
        lc.setParent(listitem);
        return tbx;
    }
    /**
     * add a listcell contains an intbox and returh the intbox
     * @param listitem the listitem to append this listcell
     * @param value the value to set to the intbox
     * @return Intbox the created intbox
     */
    private Intbox addIntboxCell (Listitem listitem, Integer value) {
        Listcell lc = new Listcell();
        Intbox ibx = new Intbox();
        if (value != null) {
            ibx.setValue(value);
        }
        ibx.setParent(lc);
        lc.setParent(listitem);
        return ibx;
    }
    /**
     * The EventListener for firstName changed
     * @param listbox the listbox, post event to it
     * @param oldData the original Person, need the lastName and age of it
     * @param listitem the listitem contains the textbox of this event, need the index of it
     * @return EventListener that listen to firstName changed
     */
    @SuppressWarnings("rawtypes")
    private EventListener getFirstnameChangedListener (final Listbox listbox, final Person oldData, final Listitem listitem) {
        return new EventListener () {
            public void onEvent (Event event) {
                InputEvent ievent = (InputEvent)event;
                Events.postEvent(ModelDataChangeEvent
                    .getModelDataChangeEvent(listbox,
                        new Person(ievent.getValue(), oldData.getLastName(), oldData.getAge()),
                        listitem.getIndex()));
            }
        };
    }
    /**
     * The EventListener for lastName changed
     * @param listbox the listbox, post event to it
     * @param oldData the original Person, need the firstName and age of it
     * @param listitem the listitem contains the textbox of this event, need the index of it
     * @return EventListener that listen to lastName changed
     */
    @SuppressWarnings("rawtypes")
    private EventListener getLastnameChangedListener (final Listbox listbox, final Person oldData, final Listitem listitem) {
        return new EventListener () {
            public void onEvent (Event event) {
                InputEvent ievent = (InputEvent)event;
                Events.postEvent(ModelDataChangeEvent
                    .getModelDataChangeEvent(listbox,
                        new Person(oldData.getFirstName(), ievent.getValue(), oldData.getAge()),
                        listitem.getIndex()));
            }
        };
    }
    /**
     * The EventListener for age changed
     * @param listbox the listbox, post event to it
     * @param oldData the original Person, need the firstName and lastName of it
     * @param listitem the listitem contains the intbox of this event, need the index of it
     * @return EventListener that listen to age changed
     */
    @SuppressWarnings("rawtypes")
    private EventListener getAgeChangedListener (final Listbox listbox, final Intbox intbox, final Person oldData, final Listitem listitem) {
        return new EventListener () {
            public void onEvent (Event event) {
                Events.postEvent(ModelDataChangeEvent
                    .getModelDataChangeEvent(listbox,
                        new Person(oldData.getFirstName(), oldData.getLastName(), intbox.getValue()),
                        listitem.getIndex()));
            }
        };
    }
}


ModelDataChangeEvent.java

Contains the changed data and the index to apply this change in it.

package test.event;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;

/**
 * tested with ZK 6.0.2
 * @author ben
 *
 */
public class ModelDataChangeEvent extends Event {
    private static final long serialVersionUID = 3645653880934243558L;

    // the changed data, can be any Object
    private final Object _data;
    // the index to apply this change
    private int _index;

    /**
     * Get a ModelDataChangeEvent
     * @param target the target to post this event
     * @param data the changed data
     * @param index the index to apply this change
     * @return ModelDataChangeEvent
     */
    public static ModelDataChangeEvent getModelDataChangeEvent (Component target, Object data, int index) {
        return new ModelDataChangeEvent("onModelDataChange", target, data, index);
    }
    /**
     * 
     * @param name event name, should starts with "on"
     * @param target
     * @param data
     * @param index
     */
    public ModelDataChangeEvent (String name, Component target, Object data, int index) {
        super(name, target);
        _data = data;
        _index = index;
    }
    public Object getData () {
        return _data;
    }
    public int getIndex () {
        return _index;
    }
}


Person.java

Contains some fields of a Person, simple POJO.

package test.data;

public class Person {
    private String _firstName;
    private String _lastName;
    private Integer _age;

    public Person (String firstName, String lastName, Integer age) {
        _firstName = firstName;
        _lastName = lastName;
        _age = age;
    }
    public void setFirstName (String firstName) {
        _firstName = firstName;
    }
    public void setLastName (String lastName) {
        _lastName = lastName;
    }
    public void setAge (Integer age) {
        _age = age;
    }
    public String getFirstName () {
        return _firstName;
    }
    public String getLastName () {
        return _lastName;
    }
    public Integer getAge () {
        return _age;
    }
}


PersonComparator.java

Use it to compare two person, on specific field, in ascending or descending way.

package test.data;

import java.util.Comparator;

/**
 * the Comparator to compare a Person of fields firstName,
 * lastName or age in ascending or descending order
 * @author ben
 *
 */
public class PersonComparator implements Comparator<Person> {
    private boolean _asc;
    private String _field;

    /**
     * Constructor
     * @param asc true: ascending, false: descending
     * @param field to compare, firstName, lastName or age
     */
    public PersonComparator (boolean asc, String field) {
        _asc = asc;
        _field = field;
    }
    /**
     * compare two person
     */
    public int compare(Person p1, Person p2) {
        int result = 0,
            mult =  _asc ? 1 : -1;
        result = "firstName".equals(_field)?
                    p1.getFirstName().compareTo(p2.getFirstName()) * mult
                : "lastName".equals(_field)?
                    p1.getLastName().compareTo(p2.getLastName()) * mult
                : "age".equals(_field)?
                    compareAge(p1.getAge(), p2.getAge()) * mult
                : 0;
        return result;
    }
    /**
     * function for compare age
     */
    private int compareAge (Integer a1, Integer a2) {
        int a = (a1 == null || a1 < 0)? 0 : a1.intValue();
        int b = (a2 == null || a2 < 0)? 0 : a2.intValue();
        return a > b? 1
                : a < b? -1
                : 0;
    }
}


The Result

View demo on line
http://screencast.com/t/YhtUpCtDW

Download

Project at github
https://github.com/benbai123/ZK_Practice/tree/master/Pattern/MVVM/ListboxMVVMInplaceEditing

Demo flash
https://github.com/benbai123/ZK_Practice/blob/master/demo_src/swf/Pattern/MVVM/Renderer_MVVM_Inplace_Editing.swf

ZK Listening/Processing Event in ZUL Page or Java Code



Introduction

In ZK, you can listen to and process an event of a component in ZUL page, or listen to and process an event by adding an EventListener by java code.

This article will describe the two methods to listen/process event.

Pre-request

ZK Basic MVC Pattern with GenericForwardComposer
http://ben-bai.blogspot.tw/2012/10/zk-basic-mvc-pattern-with.html

The Program

listening_and_processing_event_in_zul_or_java.zul

There are two buttons in this page, the first one listening and processing the onClick event in zul page directly, an EventListener will be added to the second button by java code in ListeningProcessingEventTestComposer.java.

<zk>
    <!-- tested with ZK 6.0.2 -->
    <div apply="test.ListeningProcessingEventTestComposer">
        <textbox id="tb" />
        <!-- the button below listening and processing in zul page
             -->
        <button label="button one">
            <!-- the fragment below is equal to
                addEventListener("onClick", new EventListener () {
                    public void onEvent (Event event) {
                        tb.setValue("button one clicked !");
                    }
                });
                where the 'tb' here will mapping to the component that id is 'tb'
                in the zul page by ZK automatically -->
            <attribute name="onClick"><![CDATA[
                tb.setValue("button one clicked !");
            ]]></attribute>
        </button>
        <button id="btnTwo" label="button two" />
    </div>
</zk>


ListeningProcessingEventTestComposer.java

Simply add an EventListener to btnTwo in doAfterCompose method

package test;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Button;
import org.zkoss.zul.Textbox;

/**
 * tested with ZK 6.0.2
 * @author ben
 *
 */
@SuppressWarnings("serial")
public class ListeningProcessingEventTestComposer extends GenericForwardComposer {
    Button btnTwo;
    Textbox tb;
    public void doAfterCompose (Component comp) throws Exception {
        super.doAfterCompose(comp);
        btnTwo.addEventListener( // add an EventListener
            "onClick", // listen to onClick event
            new EventListener () { // create an EventListener instance to add
                public void onEvent (Event event) { // the method will be called while onClick event triggered
                    tb.setValue("button two clicked !"); // set some string to the Textbox tb !
                }
            }
        );
    }
}


The Result

View demo on line
http://screencast.com/t/nLZ72Lj9X

Reference

Event listening and processing
http://books.zkoss.org/wiki/ZK%20Developer's%20Guide/ZK%20in%20Depth/Event%20listening%20and%20processing

Download

Files at github

listening_and_processing_event_in_zul_or_java.zul
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/WebContent/listening_and_processing_event_in_zul_or_java.zul

ListeningProcessingEventTestComposer.java
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/src/test/ListeningProcessingEventTestComposer.java

Demo flash
https://github.com/benbai123/ZK_Practice/blob/master/Components/demos/listening_processing_event_in_zul_or_java.swf

Saturday, December 15, 2012

ZK Listbox: Event Processing with Renderer and MVVM



Introduction

Usually we will use template and binding data directly in listbox while using MVVM, but there are some issues such like the zul file and ViemModel might be tightly coupled by the event processing in template, or you cannot use template since some framework limitation, or your data cannot rendered by template well.

However, finally you make a decision that to use ListitemRenderer and you still need to process some events, instead of go through the 'binding vm via java code', you can also try add EventListener in ListitemRenderer and pass some custom event with required data to listbox then bind that custom event of Listbox to ViewModel.

This article will describe how to pass event to listbox from inner item.

Note: You can just ignore the 'template' and 'binding vm via java code' mentioned above, they are actually not so related to this article.

Pre-request

ZK Basic MVVM Pattern
http://ben-bai.blogspot.tw/2012/12/zk-basic-mvvm-pattern.html

ZK Build View in ZUL file or Java Code (the java code part)
http://ben-bai.blogspot.tw/2012/12/zk-build-view-in-zul-file-or-java-code.html

ZK Listening/Processing Event in ZUL Page or Java Code
http://ben-bai.blogspot.tw/2012/12/zk-listeningprocessing-event-in-zul.html

Pass Event to Other Component
http://ben-bai.blogspot.tw/2012/12/pass-event-to-other-component.html

ZK Listbox: Using ListitemRenderer to Render Complex Data in Listbox
http://ben-bai.blogspot.tw/2012/12/zk-listbox-using-listitemrenderer-to.html

The Program

index.zul

In the zul file, we get model from vm, assign renderer to listbox, and execute some command in vm wile the custom onOrderEvent event of listbox is triggered.

As you can see, this is a very simple View for MVVM.

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <div apply="org.zkoss.bind.BindComposer"
        viewModel="@id('vm') @init('test.viewmodel.TestVM')">
        <hbox>
            <!-- simply get itemModel from vm, use ItemRenderer
                to render items and to preprocess inner event
                and trigger orderItem event while the onOrderEvent
                custom event -->
            <listbox width="400px" model="@load(vm.itemModel)"
                itemRenderer="test.renderer.ItemRenderer"
                onOrderEvent="@command('orderItem')">
                <listhead>
                    <listheader label="Item Name" />
                    <listheader label="Available Amount" />
                    <listheader label="Order Amount" />
                    <listheader label="Order Button" />
                </listhead>    
            </listbox>
            <!-- simply get orderModel from vm, use OrderRenderer
                to render items and to preprocess inner event
                and trigger cancelOrder event while the onOrderEvent
                custom event -->
            <listbox width="400px" model="@load(vm.orderModel)"
                itemRenderer="test.renderer.OrderRenderer"
                onOrderEvent="@command('cancelOrder')">
                <listhead>
                    <listheader label="Item Name" />
                    <listheader label="Ordered Amount" />
                    <listheader label="Cancel Button" />
                </listhead>
            </listbox>
        </hbox>
    </div>
</zk>


TestVM.java

In the ViewModel, there is a Model ShoppingCart in it, and have some getter to provide model, and some command that will be triggered by OrderEvent.

As you can see, this is a very simple ViewModel for MVVM

package test.viewmodel;

import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zul.ListModel;

import test.data.Item;
import test.data.Order;
import test.data.ShoppingCart;
import test.event.OrderEvent;

/**
 * Tested with ZK 6.0.2
 * 
 * The VM interact with the view
 * 
 * @author benbai123
 *
 */
public class TestVM {
    ShoppingCart _cart = new ShoppingCart();

    public ListModel<Item> getItemModel () {
        return _cart.getItemModel();
    }
    public ListModel<Order> getOrderModel () {
        return _cart.getOrderModel();
    }
    @Command
    @NotifyChange({"itemModel", "orderModel"})
    public void orderItem (
            @ContextParam(ContextType.TRIGGER_EVENT) OrderEvent event) {
        _cart.orderItem(event);
    }
    @Command
    @NotifyChange({"itemModel", "orderModel"})
    public void cancelOrder (
            @ContextParam(ContextType.TRIGGER_EVENT) OrderEvent event) {
        _cart.cancelOrder(event);
    }
}


Item.java

Contains information of an item

package test.data;

/**
 * Item class contains item name and amount
 * @author benbai123
 *
 */
public class Item {
    private String _itemName;
    private int _amount;

    public Item (String itemName, int amount) {
        _itemName = itemName;
        _amount = amount;
    }
    public String getItemName () {
        return _itemName;
    }
    public void setAmount (int amount) {
        _amount = amount;
    }
    public int getAmount () {
        return _amount;
    }
}


Order.java

Contains information of an order.

package test.data;

/**
 * Order class contains item (the Item class) and item index
 * (item index received while order that item) 
 * @author benbai123
 *
 */
public class Order {
    private Item _item;
    private int _itemIndex;

    public Order (Item item, int itemIndex) {
        _item = item;
        _itemIndex = itemIndex;
    }
    public Item getItem () {
        return _item;
    }
    public int getItemIndex () {
        return _itemIndex;
    }
}


ShoppingCart.java

Contains a list of item and a list of order, also handle the 'order item' and 'cancel order' in it.

package test.data;

import java.util.ArrayList;
import java.util.List;

import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;

import test.event.OrderEvent;

/**
 * The shoppingcart contains the item model and order model
 * also handle the orderItem and cancelOrder here
 * 
 * @author benbai123
 *
 */
public class ShoppingCart {
    private ListModelList<Item> _itemModel;
    private ListModelList<Order> _orderModel;

    public ListModel<Item> getItemModel () {
        if (_itemModel == null) {
            List<Item> items = new ArrayList<Item>();
            items.add(new Item("Item One", 5));
            items.add(new Item("Item Two", 3));
            items.add(new Item("Item Three", 10));
            _itemModel = new ListModelList<Item>(items);
        }
        return _itemModel;
    }
    public ListModel<Order> getOrderModel () {
        if (_orderModel == null) {
            _orderModel = new ListModelList<Order>(new ArrayList<Order>());
        }
        return _orderModel;
    }

    public void orderItem (OrderEvent event) {
        addOrder (event.getItemName(), event.getAmount(), event.getItemIndex());
        decreaseItemAmount (event.getItemIndex(), event.getAmount());
    }

    public void cancelOrder (OrderEvent event) {
        removeOrder (event.getItemName());
        increaseItemAmount (event.getItemIndex(), event.getAmount());
    }
    private void decreaseItemAmount (int index, int amount) {
        Item item = _itemModel.getElementAt(index);
        item.setAmount(item.getAmount() - amount);
    }
    private void increaseItemAmount (int index, int amount) {
        Item item = _itemModel.getElementAt(index);
        item.setAmount(item.getAmount() + amount);
    }
    private void addOrder (String itemName, int amount, int itemIndex) {
        Order order = getOrderByName(itemName);
        if (order != null) {
            Item item = order.getItem();
            item.setAmount(item.getAmount() + amount);
        } else {
            order = new Order(new Item(itemName, amount), itemIndex);
            _orderModel.add(order);
        }
    }
    private void removeOrder (String itemName) {
        Order order = getOrderByName(itemName);;
        if (order != null) {
            _orderModel.remove(order);
        }
    }
    private Order getOrderByName (String itemName) {
        int size = _orderModel.getSize();
        Order order;
        for (int i = 0; i < size; i++) {
            order = _orderModel.getElementAt(i);
            if (itemName.equals(order.getItem().getItemName())) {
                return order;
            }
        }
        return null;
    }
}


ItemRenderer.java

Renderer to render a list of Item.

The order button will process the onClick event itself, and pass the OrderEvent with required data to listbox as needed. This let us only need to face the event we are interested (the OrderEvent of the Item List) instead of face all button click then handle all situation in ViewModel.

package test.renderer;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.Clients;

import org.zkoss.zul.Button;
import org.zkoss.zul.Intbox;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Listitem;

import test.data.Item;
import test.event.OrderEvent;

/**
 * Tested with ZK 6.0.2
 * 
 * ItemRenderer render Item class to listitem
 * @author benbai123
 *
 */
public class ItemRenderer implements ListitemRenderer<Item> {
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void render (final Listitem listitem, final Item data, final int index) {
        final Listbox lb = listitem.getListbox();
        // item name and available amount
        new Listcell(data.getItemName()).setParent(listitem);
        new Listcell(data.getAmount() + "").setParent(listitem);

        // intbox for input the amount to order
        Listcell lc = new Listcell();
        final Intbox intbox = new Intbox();
        intbox.setParent(lc);
        lc.setParent(listitem);

        // button to perform the order action
        lc = new Listcell();
        Button orderBtn = new Button("Order Item");
        orderBtn.addEventListener(Events.ON_CLICK, new EventListener() {
            public void onEvent (Event event) {
                // if the value is not grater zero or over the available amount
                // show alert message
                int amount = intbox.getValue() == null? 0 : intbox.getValue();
                if (amount <= 0) {
                    Clients.alert(" positive value only");
                } else if (amount > data.getAmount()) {
                    Clients.alert(" over available amount ");
                } else {
                    // post an OrderEvent to listbox
                    // to inform order action with the
                    // item name and the amount to order
                    Item item = new Item(data.getItemName(), amount);
                    Events.postEvent(OrderEvent.getOrderEvent(lb, item, index));
                }
            }
        });
        orderBtn.setParent(lc);
        lc.setParent(listitem);
    }
}


OrderRenderer.java

Renderer to render a list of Order.

Similar to the order button, the cancel button will process the onClick event itself, and pass the OrderEvent with required data to listbox as needed.

package test.renderer;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;

import org.zkoss.zul.Button;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;

import test.data.Item;
import test.data.Order;
import test.event.OrderEvent;

/**
 * Tested with ZK 6.0.2
 * 
 * OrderRenderer render Order class to listitem
 * @author benbai123
 *
 */
public class OrderRenderer implements ListitemRenderer<Order> {
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void render (final Listitem listitem, final Order data, final int index) {
        final Listbox lb = listitem.getListbox();

        // get item from order
        Item item = data.getItem();
        // item name and ordered amount
        new Listcell(item.getItemName()).setParent(listitem);
        new Listcell(item.getAmount() + "").setParent(listitem);

        // button to perform the cancel action
        Listcell lc = new Listcell();
        Button cancelBtn = new Button("Cancel Order");
        cancelBtn.addEventListener(Events.ON_CLICK, new EventListener() {
            public void onEvent (Event event) {
                // post an OrderEvent to listbox
                // to inform cancel action with the
                // item name and the ordered amount to restore
                Events.postEvent(
                    OrderEvent.getOrderEvent(lb, data.getItem(), data.getItemIndex())
                );
            }
        });
        cancelBtn.setParent(lc);
        lc.setParent(listitem);
    }
}


OrderEvent.java

Contains the data of an order for both to order item or to cancel order.

package test.event;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;

import test.data.Item;

/**
 * Tested with ZK 6.0.2
 * @author benbai123
 *
 */
public class OrderEvent extends Event {
    private static final long serialVersionUID = 3645653880934243558L;

    private final String _itemName;
    // itemIndex denotes the index to restore the amount
    private final int _itemIndex;
    private final int _amount;

    public static OrderEvent getOrderEvent (Component target, Item data, int index) {
        return new OrderEvent("onOrderEvent", target, data.getItemName(), index, data.getAmount());
    }
    public OrderEvent (String name, Component target, String itemName, int itemIndex, int amount) {
        super(name, target);
        _itemName = itemName;
        _itemIndex = itemIndex;
        _amount = amount;
    }
    public String getItemName () {
        return _itemName;
    }
    public int getItemIndex () {
        return _itemIndex;
    }
    public int getAmount () {
        return _amount;
    }
}


The Result

View demo online
http://screencast.com/t/XZNxVOzbs

Extended Reading

http://stackoverflow.com/questions/13818305/pure-java-binding-in-zk-listbox-with-itemrenderer

http://www.zkoss.org/forum/listComment/20938-Listbox-using-template-how-can-I-get-the-previous-row-so-can-use-in-logic-for-current-row

Download

Demo flash at github
https://github.com/benbai123/ZK_Practice/blob/master/demo_src/swf/Pattern/MVVM/handle_event_in_item_renderer_with_mvvm.swf

Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Pattern/MVVM/RendererWithMVVM

Friday, December 14, 2012

ZK Listbox: Using ListitemRenderer to Render Complex Data in Listbox



Introduction

This article describe how to implement a ListitemRenderer to display the data of a specific class in listitem.

From official site:

ListitemRenderer:
When a listbox is assigned with a model, a default renderer is assigned too. The default renderer will assume that each item has only one column, and it converts the data into a string directly. If you want to display multiple columns or retrieve a particular field of the data, you have to implement ListitemRenderer to handle the rendering.

ListModel:
Listbox and Grid allow developers to separate the view and the model by implementing ListModel. Once the model is assigned (with Listbox.setModel(ListModel)), the display of the listbox is controlled by the model, and an optional renderer. The model is used to provide data, while the renderer is used to provide the custom look. By default, the data is shown as a single-column grid/listbox. If it is not what you want, please refer to the View section for writing a custom renderer


Pre-request

(must)
ZK Basic MVC Pattern with GenericForwardComposer
http://ben-bai.blogspot.tw/2012/10/zk-basic-mvc-pattern-with.html

(must)
ZK Build View in ZUL file or Java Code (the java code part)
http://ben-bai.blogspot.tw/2012/12/zk-build-view-in-zul-file-or-java-code.html

The Program

listbox_with_model_and_renderer.zul

In this file, simply declare 3 listboxes with different ID and apply the ListboxModelRendererComposer, the model and renderer will be assigned to listboxes in composer.

<zk>
    <!-- Tested with ZK 6.0.2 -->

    <div style="margin: 10px;"
        apply="test.listbox.composer.ListboxModelRendererComposer">
        <vbox>
            <label value="listbox with string data in ListModel, do not need renderer" />
            <div height="10px" />
            <listbox id="lbOne" />
            <div height="30px" />

            <label value="listbox with Person in ListModel but no renderer, cannot display data well" />
            <div height="10px" />
            <listbox id="lbTwo" />
            <div height="30px" />

            <label value="listbox with Person in ListModel and ListitemRenderer, display person data without any problem" />
            <div height="10px" />
            <listbox id="lbThree" />
        </vbox>
    </div>
</zk>


ListboxModelRendererComposer.java

After components composed, assign the model/renderer to listboxes.

package test.listbox.composer;

import java.util.ArrayList;
import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;

import test.data.bean.Person;
import test.listbox.renderer.PersonListitemRenderer;

/**
 * Tested with ZK 6.0.2
 * @author benbai
 *
 */
public class ListboxModelRendererComposer extends GenericForwardComposer {
    Listbox lbOne;
    Listbox lbTwo;
    Listbox lbThree;

    public void doAfterCompose (Component comp) throws Exception {
        super.doAfterCompose(comp);

        // set models and render to listbox after comopsed
        lbOne.setModel(getSimpleStringModel());
        lbTwo.setModel(getPersonModel());
        lbThree.setModel(getPersonModel());

        lbThree.setItemRenderer(new PersonListitemRenderer());
    }

    public ListModel getSimpleStringModel () {
        List l = new ArrayList();

        // simply add some Strings to a list
        // than wrap the list by a ListModelList
        l.add("data one");
        l.add("data two");
        l.add("data three");

        return new ListModelList(l);
    }
    public ListModel getPersonModel () {
        List l = new ArrayList();

        // simply add some Persons to a list
        // than wrap the list by a ListModelList
        l.add(new Person("First Name One", "Last Name One", 21));
        l.add(new Person("First Name Two", "Last Name Two", 22));
        l.add(new Person("First Name Three", "Last Name Three", 23));

        return new ListModelList(l);
    }
}


Person.java

The class that contains some attributes of a person.

package test.data.bean;

public class Person {
    private String _firstName = "";
    private String _lastName = "";
    private int _age = 1;

    public Person(String firstName, String lastName, int age) {
        _firstName = firstName;
        _lastName = lastName;
        _age = age;
    }

    public String getFirstName() {
        return _firstName;
    }
    public String getLastName() {
        return _lastName;
    }
    public String getFullName() {
        return _firstName + " " + _lastName;
    }
    public int getAge () {
        return _age;
    }
}


PersonListitemRenderer.java

Implements ListitemRenderer, used to render a Person object while render Listitem, also build listheaders.

package test.listbox.renderer;

import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.ListitemRenderer;

import test.data.bean.Person;

/**
 * Tested with ZK 6.0.2
 * @author benbai
 *
 */
public class PersonListitemRenderer implements ListitemRenderer {
    /**
     * The method to implements of a renderer,
     * will be called by listbox automatically while render items
     */
    public void render (Listitem listitem, Object value, int index) {
        Person p = (Person)value;

        // keep value in listitem
        listitem.setValue(value);

        addListcell(listitem, p.getFirstName());
        addListcell(listitem, p.getLastName());
        addListcell(listitem, p.getAge() + "");
        addListcell(listitem, p.getFullName());

        // create list headers while render first item
        if (index == 0) {
            renderListheads(listitem.getListbox());
        }
    }
    private void addListcell (Listitem listitem, String value) {
        Listcell lc = new Listcell ();
        Label lb = new Label(value);
        lb.setParent(lc);
        lc.setParent(listitem);
    }
    private void renderListheads (Listbox listbox) {
        Listhead lh = new Listhead();

        new Listheader("First Name").setParent(lh);
        new Listheader("Last Name").setParent(lh);
        new Listheader("Age").setParent(lh);
        new Listheader("Full Name").setParent(lh);

        lh.setParent(listbox);
    }
}


The Result



Reference

http://books.zkoss.org/wiki/ZK_Component_Reference/Data/Listbox

http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/MVC/Model/List_Model

http://books.zkoss.org/wiki/ZK_Developer's_Reference/MVC/View/Renderer/Listbox_Renderer

Download

Files at github

listbox_with_model_and_renderer.zul
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/WebContent/listbox_with_model_and_renderer.zul

Person.java
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/src/test/data/bean/Person.java

ListboxModelRendererComposer.java
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/src/test/listbox/composer/ListboxModelRendererComposer.java

PersonListitemRenderer.java
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/src/test/listbox/renderer/PersonListitemRenderer.java

Tuesday, December 11, 2012

Pass Event to Other Component



Introduction

Some times we want to change a component while some event of another component is triggered, we can achieve this by posting an event with some data to the component we want to change instead of modify it directly in the event listener of another component.

This seems a little bit weird and useless but this would be helpful in some situation such like implementing inplace editing of grid with renderer under MVVM (will be described in another article later)

Pre-request

(must)
Basic MVC with SelectorComposer
http://ben-bai.blogspot.tw/2012/10/zk-basic-mvc-pattern-with_31.html

The Composer

PasseventTestComposer.java

Simply post an event to label while the value of textbox is changed, update label's value in the event listener itself.

package test.event.passeventtest;

import java.util.HashMap;
import java.util.Map;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Label;

/**
 * Tested with ZK 6.0.2
 * 
 * Test passing event with data from one component to another
 * 
 * This seems a little bit weird and useless here
 * but this would be helpful in some situation
 * such like implement inplace editing of grid with renderer
 * under MVVM
 * (will be described in another article later)
 * 
 * @author benbai
 *
 */
public class PasseventTestComposer extends SelectorComposer {
    @Wire
    Label lb;

    /**
     * Post an event with the new value to label
     * while the value of textbox is changed
     * instead of modify the value of label directly
     * @param event
     */
    @Listen("onChange=#tbx")
    public void onChange$tbx (InputEvent event) {
        Map data = new HashMap();
        data.put("value", event.getValue());
        Events.postEvent("onValueChange", lb, data);
    }
    /**
     * Really modify the value of label here
     * @param event
     */
    @Listen("onValueChange=#lb")
    public void onValueChange$lb (Event event) {
        Map data = (Map)event.getData();
        String value = (String)data.get("value");
        lb.setValue(value);
    }
}


The ZUL Page

pass_event_to_other_component.zul

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <window apply="test.event.passeventtest.PasseventTestComposer">
        <label id="lb" value="label" />
        <textbox id="tbx" />
    </window>
</zk>


The Result

View demo on line
http://screencast.com/t/9wYBLE3lubaF

Reference

http://books.zkoss.org/wiki/ZK_Developer's_Reference/Event_Handling/Event_Firing

Download

Files at github

pass_event_to_other_component.zul
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/WebContent/pass_event_to_other_component.zul

PasseventTestComposer.java
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/src/test/event/passeventtest/PasseventTestComposer.java

pass_event_to_other_component.swf
https://github.com/benbai123/ZK_Practice/blob/master/Components/demos/pass_event_to_other_component.swf

Sunday, December 9, 2012

ZK Build View in ZUL file or Java Code



Introduction

In ZK, you can build the view (UI) in two ways, use xml in zul file, or use java code in java file. This is important in some scenario such like you use a complex object as the data in a grid and use custom renderer to render components in a row.

This post will practice the basic part of building view in zul file/java file by xml/java-code.

Pre-request

(Must)
The Basic MVC by GenericForwardComposer
http://ben-bai.blogspot.tw/2012/10/zk-basic-mvc-pattern-with.html

The Program

build_view_by_xml_in_zul_file.zul

Simply create components, assign properties and handle events in zul file by xml.

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <div>
        <!-- create window component and set properties -->
        <window title="test window" border="normal">
            <!-- add event listener for onClick event of window -->
            <attribute name="onClick"><![CDATA[
                alert("test window is clicked !");
            ]]></attribute>
            <!-- create label component and set properties -->
            <label value="test label" style="color: green;">
                <!-- add event listener for onClick event of label -->
                <attribute name="onClick"><![CDATA[
                    alert("test label is clicked !");
                ]]></attribute>
            </label>
        </window>
    </div>
</zk>


build_view_by_javacode_in_java_file.zul

Only apply the TestBuildViewByJavacode.java as composer in zul file, and than create components, assign properties and handle events in java code in the doAfterCompose method of TestBuildViewByJavacode.java (the composer).

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <div apply="test.buildview.TestBuildViewByJavacode">

    </div>
</zk>


TestBuildViewByJavacode.java

Create components, assign properties and handle events in java code in the doAfterCompose method.

package test.buildview;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Label;
import org.zkoss.zul.Window;

/**
 * Tested with ZK 6.0.2
 * @author benbai
 *
 */
public class TestBuildViewByJavacode extends GenericForwardComposer {
    /** While doAfterCompose, all components in zul page under
     * the div that apply this composer (including the div itself)
     * is ready and can be accessed in java code
     *
     */
    public void doAfterCompose (Component comp) throws Exception {
        super.doAfterCompose(comp);
        // the comp here is the div which apply this
        // composer in build_view_by_javacode_in_java_file.zul
        System.out.println(comp);

        // below is the equivalent code of
        // <window title="test window" border="normal">
        //     <attribute name="onClick"><![CDATA[
        //         alert("test window is clicked !");
        //     ]]></attribute>
        //     <label value="test label" style="color: green;">
        //         <attribute name="onClick"><![CDATA[
        //             alert("test label is clicked !");
        //         ]]></attribute>
        //     </label>
        // </window>
        Window win = new Window(); // create window component
        win.setTitle("test window"); // set properties
        win.setBorder("normal");
        // add event listener for onClick event of window
        win.addEventListener(Events.ON_CLICK, new EventListener () {
            public void onEvent (Event event) {
                alert("test window is clicked !");
            }
        });
        win.setParent(comp); // append it to parent div

        Label lb = new Label(); // create label component
        lb.setValue("test label"); // set properties
        lb.setStyle("color: green;");
        // add event listener for onClick event of label
        lb.addEventListener(Events.ON_CLICK, new EventListener () {
            public void onEvent (Event event) {
                alert("test label is clicked !");
            }
        });
        lb.setParent(win); // append it to parent window
    }
}


The Result

View demo on line
http://screencast.com/t/NxnhxhUqC

Reference

http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/UI_Composing/Component-based_UI

Download

Files at github

build_view_by_xml_in_zul_file.zul
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/WebContent/folders/build_view_test/build_view_by_xml_in_zul_file.zul

build_view_by_javacode_in_java_file.zul
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/WebContent/folders/build_view_test/build_view_by_javacode_in_java_file.zul

test.buildview.TestBuildViewByJavacode.java
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/src/test/buildview/TestBuildViewByJavacode.java

Demo flash at github
https://github.com/benbai123/ZK_Practice/blob/master/Components/demos/build_view_test.swf

Saturday, December 8, 2012

Init/Cleanup Session/Desktop in ZK


Introduction

This post talking about how to prepare some data for each session or each desktop and clear them while session/desktop invalidated.

The Program

index.zul

Link to this page to see init of desktop of this page and init while session created.

Click button to see init of another.zul and cleanup of this page, or wait 10 seconds to see cleanup of session and desktop(s).

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <div>test desktop cleanup and session cleanup</div>
    <div>click 'to another.zul' button than you will see the cleanup message for desktop in console</div>
    <div>or wait 10 secondes than you will see cleanup message for session and all desktops in console</div>
    
    <button label="to another.zul">
        <attribute name="onClick"><![CDATA[
            Executions.getCurrent().sendRedirect("/another.zul");
        ]]></attribute>
    </button>
</zk>


another.zul

Click button to see init of index.zul and cleanup of this page, or wait 10 seconds to see cleanup of session and desktop(s).

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <div>test desktop cleanup and session cleanup</div>
    <div>click 'to index.zul' button than you will see the cleanup message for desktop in console</div>
    <div>or wait 10 secondes than you will see cleanup message for session desktop(s) in console</div>
    
    <button label="to index.zul">
        <attribute name="onClick"><![CDATA[
            Executions.getCurrent().sendRedirect("/index.zul");
        ]]></attribute>
    </button>
</zk>


web.xml

We specify a TestSessionCleaner here to do some init while session created and do cleanup while session destroyed

<?xml version="1.0" encoding="UTF-8"?>

<!-- web.xml
    Purpose:
        
    Description:
        
    History:
        Wed Dec 29 12:13:00     2004, Created by tomyeh

Copyright (C) 2004 Potix Corporation. All Rights Reserved.
-->

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> 

    <description><![CDATA[My ZK Application]]></description>
    <display-name>MyApp</display-name>

    <!-- //// -->
    <!-- ZK -->
    <!-- listener for session cleanup, use it to do init and
        cleanup of session

        used to be org.zkoss.zk.ui.http.HttpSessionListener,
        we use another class (TestSessionCleaner) which extends from
        org.zkoss.zk.ui.http.HttpSessionListener
        and so some customize here
     -->
    <listener>
        <description>
            ZK listener for session cleanup
        </description>
        <listener-class>test.TestSessionCleaner</listener-class>
    </listener>
    <servlet>
        <description>ZK loader for ZUML pages</description>
        <servlet-name>zkLoader</servlet-name>
        <servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>

        <!-- Must. Specifies URI of the update engine (DHtmlUpdateServlet).
        It must be the same as <url-pattern> for the update engine.
        -->
        <init-param>
            <param-name>update-uri</param-name>
            <param-value>/zkau</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup><!-- Must -->
    </servlet>
    <servlet-mapping>
        <servlet-name>zkLoader</servlet-name>
        <url-pattern>*.zul</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>zkLoader</servlet-name>
        <url-pattern>*.zhtml</url-pattern>
    </servlet-mapping>
    <servlet>
        <description>The asynchronous update engine for ZK</description>
        <servlet-name>auEngine</servlet-name>
        <servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>

    </servlet>
    <servlet-mapping>
        <servlet-name>auEngine</servlet-name>
        <url-pattern>/zkau/*</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.zul</welcome-file>
        <welcome-file>index.zhtml</welcome-file>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
    </welcome-file-list>
</web-app>


zk.xml

We specify a TestDesktopInit to do init for desktops and a TestDesktopCleanup to do cleanup for desktops here.

<zk>
    <!-- Tested with ZK 6.0.2 -->
    <!-- specifies the time, in seconds, between client requests before
        a session is invalidated. A negative time indicates the session
        should never timeout. The default zero means to use the system
        default (which is usually specified in web.xml).
     -->
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
    <!-- specifies the time, in seconds, between client requests before
        a desktop is invalidated. A negative time indicates the
        desktop should never timeout.
     -->
    <desktop-config>
        <desktop-timeout>5</desktop-timeout>
    </desktop-config>
    <!-- Classes that implement DesktopCleanup and DesktopInit,

        From official site:
        Notice that ZK will instantiate an object from the class you
        registered for each callback. For example, an object is
        instantiated to invoke DesktopInit.init(Desktop, Object), and
        another object instantiated to invoke DesktopCleanup.cleanup(Desktop),
        even if you register a class that implements both DesktopInit and DesktopCleanup.
     -->
    <listener>
        <listener-class>test.TestDesktopCleanup</listener-class>
    </listener>
    <listener>
        <listener-class>test.TestDesktopInit</listener-class>
    </listener>
</zk>


TestSessionCleaner.java

Extends org.zkoss.zk.ui.http.HttpSessionListener and do some customize in sessionCreated and sessionDestroyed methods.

package test;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;

import org.zkoss.zk.ui.http.HttpSessionListener;

/**
 * Tested with ZK 6.0.2
 * @author benbai
 *
 */
public class TestSessionCleaner extends HttpSessionListener {

    private static Map<String, Object> MapOfVeryBigDataPerSession = new HashMap<String, Object>();
    // called while session created
    public void sessionCreated(HttpSessionEvent evt) {
        super.sessionCreated(evt);
        HttpSession session = evt.getSession();
        System.out.println(" session created " + session.getId());
        MapOfVeryBigDataPerSession.put(session.getId(), session.getId());
        System.out.println(" put data into MapOfVeryBigDataPerSession, size = " + MapOfVeryBigDataPerSession.size());
        System.out.println();
    }
    // called while session destroy
    public void sessionDestroyed(HttpSessionEvent evt) {
        super.sessionDestroyed(evt);
        HttpSession session = evt.getSession();
        System.out.println(" session destroyed " + session.getId());
        MapOfVeryBigDataPerSession.remove(session.getId());
        System.out.println(" remove data from MapOfVeryBigDataPerSession, size = " + MapOfVeryBigDataPerSession.size());
        System.out.println();
    }
}


TestDesktopInit.java

Implements org.zkoss.zk.ui.util.DesktopInit, the init method will be called while desktop initiated, you can also do different actions based on the request path as needed.

package test;

import java.util.HashMap;
import java.util.Map;

import org.zkoss.zk.ui.util.DesktopInit;
import org.zkoss.zk.ui.Desktop;

/**
 * Tested with ZK 6.0.2
 * @author benbai
 *
 */
public class TestDesktopInit implements DesktopInit {
    private static Map<String, Object> MapOfVeryBigDataPerDesktop = new HashMap<String, Object>();
    // the init function that will be called by ZK
    // while desktop initiated
    public void init(Desktop desktop, java.lang.Object request) {
        System.out.println(" desktop init, id = " + desktop.getId() + ", path = " + desktop.getRequestPath());
        MapOfVeryBigDataPerDesktop.put(desktop.getId(), desktop.getId());
        System.out.println(" put data into MapOfVeryBigDataPerDesktop, size = " + MapOfVeryBigDataPerDesktop.size());
        System.out.println();
    }
    public static void removeDataFromMapOfVeryBigDataPerDesktop (String key) {
        MapOfVeryBigDataPerDesktop.remove(key);
    }
    public static int getSizeOfMapOfVeryBigDataPerDesktop () {
        return MapOfVeryBigDataPerDesktop.size();
    }
}


TestDesktopCleanup.java

Implements org.zkoss.zk.ui.util.DesktopCleanup, the cleanup method will be called while desktop destroy, you can also do different actions based on the request path as needed.

package test;

import org.zkoss.zk.ui.util.DesktopCleanup;
import org.zkoss.zk.ui.Desktop;

/**
 * Tested with ZK 6.0.2
 * @author benbai
 *
 */
public class TestDesktopCleanup implements DesktopCleanup {
    // the cleanup function that will be called by ZK
    // while desktop destroy
    public void cleanup (Desktop desktop) {
        System.out.println(" desktop cleanup " + desktop.getId() + ", path = " + desktop.getRequestPath());
        TestDesktopInit.removeDataFromMapOfVeryBigDataPerDesktop(desktop.getId());
        System.out.println(" remove data from MapOfVeryBigDataPerDesktop, size = "
                    + TestDesktopInit.getSizeOfMapOfVeryBigDataPerDesktop());
        System.out.println();
    }
}



The Result

Please run this project and see the message in your console as needed.

Reference

http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_session-config_Element

http://books.zkoss.org/wiki/ZK%20Configuration%20Reference/zk.xml/The%20desktop-config%20Element

http://books.zkoss.org/wiki/ZK_Configuration_Reference/web.xml/ZK_Session_Cleaner

http://www.zkoss.org/forum/listComment/17031-ZK-Session-Desktop-won-t-get-invalidated-after-actual-session-timeout

Download

Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Flow/CleanupDesktopAndSession

Wednesday, December 5, 2012

ZK Basic MVVM Pattern


Introduction

From official site:
MVVM is an abbreviation of a design pattern named Model-View-ViewModel which originated from Microsoft. It is a specialization of Presentation Model introduced by Martin Fowler, a variant of the famous MVC pattern. This pattern has 3 roles: View, Model, and ViewModel. The View and Model plays the same roles as they do in MVC.

This post practice the MVVM pattern in ZK.

Note: As mentioned in the title, only very basic part in this article.

The ZUL Page

basic_MVVM.zul

Different with MVC, in MVVM, basically the View-Model will not 'control' the component directly.

<zk>
    <!-- Tested with ZK 6.0.2 -->

    <!-- The basic patterns:
            apply="org.zkoss.bind.BindComposer" the composer that handle MVVM
            @id: the 'Name' for the View-Model under the window
            @init: the java-class that will be instantiate as a View-Model

        The two lines below means use BasicViewModel as a View-Model,
        and give it a name "basicVM"
     -->
    <window apply="org.zkoss.bind.BindComposer"
        viewModel="@id('basicVM') @init('test.basic.mvvm.BasicViewModel')">

        Input your name then click button:

        <!-- @bind(basicVM.name):
                load the 'name' from basicVM
                Which means there should a getName method in basicVM (BasicViewModel)

                save value to basicVM by setName method in basicVM

            This line below means get value from basicVM's getName method and
            save value to basicVM by setName method
         -->
        <textbox value="@bind(basicVM.name)" />

        <!-- @command('sayHello'):
                Trigger the method of View-Model,
                the View-Model will be detected automatically,
                it will call basicVM's sayHello method here

            This line below means when button clicked,
            call basicVM's sayHello method
         -->
        <button label="Say hello" onClick="@command('sayHello')" />

        <div height="50px" width="100px"
            style="margin: 10px; background-color: #A6C3EA;">
            <!-- @load(basicVM.message):
                    load the 'message' from basicVM
                    Which means there should a getMessage method in basicVM (BasicViewModel)
    
                This line below means get value from basicVM's getMessage method
             -->
            <label value="@load(basicVM.message)" />
        </div>
    </window>
</zk>


The Composer

BasicViewModel.java

package test.basic.mvvm;

import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;

/**
 * Tested with ZK 6.0.2
 * @author ben
 *
 */
public class BasicViewModel {
    String _msg = "The message label";
    String _name = "defaultName";

    public String getMessage () {
        return _msg;
    }

    public String getName () {
        return _name;
    }
    public void setName (String name) {
        _name = name;
    }
    /**
     * Annotations:
     * @Command denotes the function below is a command
     * @NotifyChange denotes the properties should be updated after this command
     * 
     * The annotations below means sayHello method is a command, and
     * the message property should be updated after sayHello command
     * is executed
     */
    @Command
    @NotifyChange("message")
    public void sayHello () {
        _msg = "Hello " + _name;
    }
}


The Result

initial page



after input name then click button



Reference

http://books.zkoss.org/wiki/ZK_Developer's_Reference/MVVM

http://books.zkoss.org/wiki/Small%20Talks/2011/November/Hello%20ZK%20MVVM

Download

Files at github

basic_MVVM.zul
https://github.com/benbai123/ZK_Practice/blob/master/Pattern/MVVM/BasicMVVM/WebContent/basic_MVVM.zul

BasicViewModel.java
https://github.com/benbai123/ZK_Practice/blob/master/Pattern/MVVM/BasicMVVM/src/test/basic/mvvm/BasicViewModel.java

Sunday, December 2, 2012

C/C++ Array Size and Length



Introduction

A simple note of array size and length test

Code

array_size_and_length.cpp

#include <iostream>

using namespace std;

/** Test basic array operation,
 * the simple basic array, not std::array
 *
 */
class TestClass {
    public:
        int _a;
        double _b;
        TestClass ()
        {
            _a = 1;
            _b = 1.1;
        }
        TestClass (int a, double b) : _a(a), _b(b)
        {
        }
};
int getLength(int nums[]);
int getLength(TestClass vals[]);
int main() {
    // an int array contains 3 elements
    int nums[] = {1, 2, 5};
    // TestClass array contains 2 elements
    TestClass vals[] = {TestClass(), TestClass(2, 2.2)};
    // output: total bytes of nums[]:                   12
    cout << "total bytes of nums[]: \t\t\t" << sizeof(nums) << endl;
    // output: element size of nums:                    4
    cout << "element size of nums: \t\t\t" << sizeof(nums[0]) << endl;
    // output: number of element (length) of nums[]:    3
    // this will work since sizeof(nums) is correct here
    cout << "number of element (length) in nums[]: \t" << sizeof(nums)/sizeof(nums[0]) << endl;
    //
    // output: wrong length of nums[]:                  1 (wrong value)
    //
    // Note: this will not work since only the address of nums[0] (a pointer)
    //       will be passed to getLength(int nums[]) function
    cout << "wrong length of nums[]: \t\t" << getLength(nums) << " (wrong value)" << endl;
    cout << endl;

    // output: total bytes of vals[]:                   32
    cout << "total bytes of vals[]: \t\t\t" << sizeof(vals) << endl;
    // output: element size of vals:                    16
    cout << "element size of vals: \t\t\t" << sizeof(vals[0]) << endl;
    // output: number of element (length) of vals[]:    2
    // this will work since sizeof(vals) is correct here
    cout << "number of element (length) in vals[]: \t" << sizeof(vals)/sizeof(vals[0]) << endl;
    //
    // output: wrong length of vals[]:                  0 (wrong value)
    //
    // Note: this will not work since only the address of vals[0] (a pointer)
    //       will be passed to getLength(TestClass vals[]) function
    cout << "wrong length of vals[]: \t\t" << getLength(vals) << " (wrong value)" << endl;
    cout << endl;

    system("PAUSE");
    return 0;
}
int getLength(int nums[]) {
    // sizeof(nums) -> 4, (pointer's size)
    // sizeof(nums[0]) -> 4, (size of int)
    // return value is 1

    return sizeof(nums)/sizeof(nums[0]);
}
int getLength(TestClass vals[]) {
    // sizeof(vals) -> 4, (pointer's size)
    // sizeof(vals[0]) -> 16, (size of TestClass)
    // return value is 0

    return sizeof(vals)/sizeof(vals[0]);
}


Result



Reference

http://www.cplusplus.com/doc/tutorial/arrays/

Download

File at github
https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/Array/array_size_and_length.cpp