Ch08
Component Choice
No, you've already made the choice. Now you have to understand it. -- Oracle from "The Matrix Reloaded"
Component Choice Guide
ZK provide too much component, sometime we lost in them... But we still can find some thread of thought, follow these threads we can find the needed component in a short time.
If you need arrange a set components in specify way, we recommend you search in layout category first and don't forget Grid
component that is not in layout category. On the other hand, if you don't need arrange them, you can search in container category. The components in container category usually can set title
attribute and has Caption
component.
If you want to show a set of tuple data, please check Grid
, Listbox
, Tree
suit your requirement or not. Grid
is powerful, can provide more free format. Listbox
has selectedItem
attribute can get the end user selected data more easier. You can treat Tree
as an advance Listbox
, because it like Listbox
but can handle hierarchy data. Of course, you can combine Vlayout
and other component to produce handmade layout if the present data is less and simple.
Please don't forget Popup
, if you need tooltip, context menu, popup. For more information please refer to ZK codument.
Thinking in component
Choose correct component is important, but use them correctly is important too. We use sidebar.zul
in ToDoZK for example to think about this issue.
First idea
Use Tree
to build Sidebar
is intuition and reasonable. No matter the relation of workspace/milestone or Document Page/Release Log is heirarchy. We hope solve all things with a Tree
is natural:
<zk xmlns:n="native">
<div apply="org.zkoss.bind.BindComposer" viewModel='@id("vm") @init("org.zkoss.todoZK.viewmodel.SidebarVM")'
onBookmarkChange='@command("bookmarkChange", evnt=event)' sclass="menu">
<tree model="@load(vm.boardModel)" selectedItem="@save(vm.selectedItem)" hflex="1">
<template name="model" var="board">
<treeitem label="@load(board.data.title)" />
</template>
</tree>
</div>
</zk>
Look at this beautiful ZUL, very pleasant. But when we build the model of Tree
, that will be a nightmare. First you must create BoardItem
class, because all object in tree model must be the same class:
public class BoardItem {
public static final int WORKSPACE_TYPE = -1;
public static final int MILESTONE_TYPE = -2;
public static final int ROOT_PAGE_TYPE = 1;
public static final int ABOUT_PAGE_TYPE = 2;
public static final int LOG_PAGE_TYPE = 3;
private String title;
private Long id;
private int type;
//skip getter and setter
}
We can't evade the routine of transform workspace/milestone data to BoardItem
. But the code of build "Document Page", "Release Log" BoardItem
is ugly:
//in view model's init()
DefaultTreeNode<BoardItem> root = new DefaultTreeNode<BoardItem>(null, new ArrayList<DefaultTreeNode<BoardItem>>());
boardModel = new DefaultTreeModel<BoardItem>(root);
//static page
DefaultTreeNode<BoardItem> pageRoot = new DefaultTreeNode<BoardItem>(
new BoardItem(Long.MIN_VALUE, "Document", BoardItem.ROOT_PAGE_TYPE),
new ArrayList<DefaultTreeNode<BoardItem>>()
);
pageRoot.add(
new DefaultTreeNode<BoardItem>(
new BoardItem(Long.MIN_VALUE, "About", BoardItem.ABOUT_PAGE_TYPE)
)
);
pageRoot.add(
new DefaultTreeNode<BoardItem>(
new BoardItem(Long.MIN_VALUE, "Release Log", BoardItem.LOG_PAGE_TYPE)
)
);
root.add(pageRoot);
boardModel.addOpenObject(pageRoot);
//convert workspace and milestone......
We must give the meaningless id
value, must maintain type
value is unique, the setSelectedItem()
of view model must process these special instance...... The result is dirty code hard to maintain.
Refactoring
Split into two Tree
components!
<zk xmlns:n="native">
<vlayout apply="org.zkoss.bind.BindComposer" viewModel='@id("vm") @init("org.zkoss.todoZK.viewmodel.SidebarVM")'
onBookmarkChange='@command("bookmarkChange", evnt=event)' sclass="menu">
<tree selectedItem="@bind(vm.docSelectItem)">
<treechildren>
<treeitem label="Document" onClick='@command("showDocument")'>
<treechildren>
<treeitem label="About" onClick='@command("showAbout")' />
<treeitem label="Release Log" onClick='@command("showRelease")' />
</treechildren>
</treeitem>
</treechildren>
</tree>
<tree model="@load(vm.boardModel)" selectedItem="@bind(vm.selectedItem)" hflex="1">
<template name="model" var="board">
<treeitem label="@load(board.data.title)" />
</template>
</tree>
</vlayout>
</zk>
(You can't merge two Tree
into one, because the component with <template>
will ignore all other child component.)
Write them directly in ZUL, thus the operation of selected item is clear. If system need to add, delete or modify static page item, the routine is easier too. But we must do one more thing to remove selectedItem
of one Tree
if the other Tree
is selected. Without this fix, it could be two selectedItem on the screen. The solution is easy:
/**
* Do nothing, just trigger NotifyChange to clear other tree's selectedItem.
*/
@NotifyChange("selectedItem")
public void setDocSelectItem(TreeNode selectedItem) {}
/**
* @return Always null for reset selectedItem.
*/
public TreeNode getDocSelectItem() {
return null;
}
/**
* @return Always null for reset selectedItem.
*/
public TreeNode getSelectedItem() {
return null;
}
Alternatives of Tree
As we talk before, you can implement ToDoZK requirement with other component. In sidebar.zul
, we can also use Tabpanel
(with accordion mold), Grid
(with Group
component)... etc. There is an example we use Groupbox
and Listbox
:
<div children="@load(vm.workspaces)"> <template name="children" var="ws"> <groupbox mold="3d"> <caption label="@load(ws.title)" /> <listbox model="@load(ws.milestones)"> <template name="model" var="ms"> <listitem label="@load(ms.title)" /> </template> </listbox> </groupbox> </template> </div>
==== To System Architecture ====
There is no standard answer of the question: "Which component is fittest for my requirement". Realize all the ZK components and think more. (Or pay to ZK for custom component!)