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

53 comments:

  1. Can you please post a example where we can use same itemrendered to create a cell but its a editable when user click on save button data edited should me saved? Something i will want to use binding in java code.

    ReplyDelete
    Replies
    1. No problem, it (renderer inline editing) was in my schedule

      Delete
    2. done, http://ben-bai.blogspot.tw/2012/12/zk-listbox-inplace-editing-with.html

      Delete
  2. Thanks Bai Ben. One more thing I have to ask is it possible to add reordering of column with cell while i am using binding in ZUL Page by @bind As example here(Wrote by me edited by Nabil) http://zkfiddle.org/sample/3gibjib/1-Another-new-ZK-fiddle but the position of the Listcell not changing according the listHeader Do you have something about it

    ReplyDelete
    Replies
    1. I posted it to the related thread at stackoverflow: http://stackoverflow.com/questions/14040772/zk-reordering-with-listbox-without-drag-and-drop-event

      Delete
  3. Some how can we hide the the logic of if="${each == 1}" and foreach from developer by creating a new component of listbox?

    ReplyDelete
    Replies
    1. I think one way is use item renderer, or you can try customize listhead/listitem with a setter that set a list of listheader/listcell to it and let them render their children by theme self.

      But these way might break the binding in MVVM.

      Delete
  4. Hmm Item rendered i dont want to use as it will break binding and lots of java code will be added. i dont want to break binding so we have to dig more to create a custom component like listbox where we can add this logic

    ReplyDelete
    Replies
    1. In short, I do not think this is a good idea.

      Why not just use the if approach (since, however, it is the general/native way and works fine, also reasonable enough)? I think it is not make sense to customize listbox for only avoid it.

      That will just make the code in zul page looks pretty, but make your application more unstable with several unknown/unexpected issue.

      Delete
  5. I think you are right let me talk to my head about this, this approach already decrease plenty of line of code as previously i created component for reordering and sorting with getitemrendered ,those component also worked with approach only i have to check with database part only because i am saving user selection in database and if user saved some of this/her selection then we are reordering according to that value. I will share you my demo example in day or two .I tried to use fiddle but if i have 2-3 zul and java files then i dont understand how to manage this thing in fiddle i will share my code with you from sourceforgenet.

    ReplyDelete
  6. Hi ben bai added a demo war http://zkframeworkhint.blogspot.in/2013/01/zk-listbox-with-column-reordering-in-zul.html

    ReplyDelete
  7. Hi ben do you have any idea about this http://stackoverflow.com/questions/15083023/zk-modal-window-overlapping-issue

    ReplyDelete
    Replies
    1. Nope, seems it works fine at zkfiddle http://zkfiddle.org/sample/333sjqb/1-window-modal-test

      Delete
  8. What about this http://stackoverflow.com/questions/14961139/is-this-good-approach-to-use-innerclass-in-zk-and-jsf-for-component-variable-bin

    ReplyDelete
  9. How can we show html content inside a label ?

    ReplyDelete
    Replies
    1. add it by java code:

      label.setValue("...");

      you can also try to use readonly multiline textbox

      Delete
    2. So it will convert Html code also like if i have test sometthing like Some text after using it .It is showing text without bold and second thing i will want to show text without html tag How can we do these thing in ZK. Like JSF provide escape attribute.

      Delete
    3. Do you mean you do not want to output the html code in a label, you want to put some dom element in a label? Why do you want to do this? What is the whole scenario?

      Delete
    4. The scenario is this I have to show data input by user in ckeditor I have to show in textbox or label something like if If I added any bold tag inside any text then when I have to show into a label or textbox it will show that selected text bold rather than showing html tag to user

      Delete
    5. I'm confused, the ckeditor can display html result itself, why do you need to use other component to display html result?

      Delete
    6. Ben actually in my case if user adding a new record then i have to show use zk ckeditor but when user will come again into same page then i have to show all added record by user so when user entering any data i have show editor otherwise label with entered data

      Delete
    7. You can override the client side setValue function of label,
      http://zkfiddle.org/sample/kcohpg/1-Label-display-dom-with-ckeditor

      Delete
  10. Hi Ben,
    How can we get all the components from a page like i am calling a method and method contain Component class object now from this object how can i got the all components..
    @Command("saveCustomization")
    public void saveCustomization(@ContextParam(ContextType.VIEW) Component view) {
    //From this view object i will want to get all the component list in indivisual ZUl page
    }

    ReplyDelete
    Replies
    1. you can try page#getRoots then get children recursively

      ref:

      http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/Page.html#getRoots()

      Delete
    2. Right but here is one issue i hav to again go for getChildren multiple time because it giving one component only like If Window contain Borderlayout and Borderlayout contain Vlayout,Center,Hlayout i have to again and again to do getChildren() is there any other method i can call to get all the component of the page

      Delete
    3. Currently there's no such API, you need to retrieve children from root nodes (in a loop or with recursive function) manually, maybe you can make a util function for this task.

      Delete
  11. Hi Ben i will want to replace + or - image with some other image in this grid demo http://www.zkoss.org/zksandbox/#g16 how can i do this?

    ReplyDelete
    Replies
    1. You can try to override the background style of
      .z-detail .z-detail-img
      and
      .z-detail.z-detail-expd .z-detail-img

      Delete
    2. Thanks its working,but its change one image i have to change both image expand as well as collapse .Can you please tell how can i know which css class used by any tag in ZK details about any Class Like Detail class used JS,Jquery,CSS i want to know everything used by a Class

      Delete
    3. I think you can use one image for .z-detail .z-detail-img and use another for .z-detail.z-detail-expd .z-detail-img,

      e.g.,

      <style>
      .z-detail .z-detail-img {
      background-image: url('/TestProj/images/imgOne.png');
      }
      .z-detail.z-detail-expd .z-detail-img {
      background-image: url('/TestProj/images/imgTwo.png');
      background-position: 0px 0px;
      }
      </style>

      Delete
    4. I tried same but not worked but your code working can you refer how can i know about this css used in ZK?

      Delete
    5. What I do is right-click on the expand/collapse icon in Chrome then click 'Inspect element' to view the dom elements and its css style in Chrome console.

      Delete
  12. Any Idea about this issue http://stackoverflow.com/questions/17398202/zk-ctrl-key-or-hot-key-creating-issue-with-diffrent-browsers

    ReplyDelete
  13. i'm copy code you to train in eclipse ,but it have one error in file PersonListitemRenderer.java , why?

    ReplyDelete
    Replies
    1. What error you are getting and which line?

      Delete
    2. This comment has been removed by the author.

      Delete
    3. Line 18 : in file PersonListitemRenderer.java

      error : The type PersonListitemRenderer must implement the inherited abstract method ListitemRenderer.render(Listitem, Object)

      i copy code and create packet same you run on eclipse

      Delete
    4. parameter in method render just don't have index, method render have 2 parameter is listitem and object. because i delete int index error is none but when run project have UiException .

      help me please T^T

      thank

      Delete
    5. After 2 hour i emend code. I know version zk is 5.0.7 but code in page use 6

      END Error !! Thank ha ha ha.

      Delete
    6. Let us know what ZK Version you are using?

      Delete
    7. render method is changed in ZK 6,

      render(Listitem, Object) is for ZK5,

      render(Listitem, Object, int) is for ZK6

      you can implements both method to make your renderer compatible with both ZK5 and ZK6

      Delete
  14. This comment has been removed by the author.

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. Any answer of this question http://forum.zkoss.org/question/90502/how-to-save-window-component-state-in-db/

    ReplyDelete
    Replies
    1. Can you help on this http://stackoverflow.com/questions/21309439/clear-date-from-datebox-through-jquery

      Delete
  17. Hi, how about adding a listgroup through java code to any of the listbox?
    I've been trying to do this, but I couldn't find a solution that works.

    Thank you!

    ReplyDelete
    Replies
    1. You need to use GroupsModel to store your data first, please refer to the sample at zkdemo http://www.zkoss.org/zkdemo/listbox/list_group

      Delete