ZK 6.5 Essentials
ZK 6.5 Essentials
For ZK 6.5
PDF generated using the open source mwlib toolkit. See http://code.pediapress.com/ for more information. PDF generated at: Thu, 13 Sep 2012 11:29:44 CST
Contents
Articles
ZK Essentials Introduction An Introduction to ZK's Server client Fusion Architecture Component Based UI Event Driven Programming Working with the Sample Applications The Resources Setting Up the Applications Using Eclipse Store Store with a Database Laying out Your ZK Components How to Layout Components Using ZK Borderlayout Handling the Login Process using ZK MVC and Sessions The Basic Login Implementing ZK MVC Managing credentials using ZK Sessions Displaying Information Using Grid, MVC and composite components Displaying the Data Composite Components in the MVC Displaying information using the Listbox and MVVM What is the MVVM Displaying Information from the View Model Performing Actions on Events Communicating between MVC and MVVM patterns and between View Models Working with ZK and databases The Theory of Using ZK with Databases Using Hibernate with ZK Hibernate, Entities and Database Generation Hibernate Session Management and Implementing the DAOs Hibernate Summary Summary and Further Readings 1 1 1 2 10 14 14 15 17 19 21 21 23 27 27 28 30 32 32 35 38 38 40 43 45 47 48 48 50 53 56 56
References
59 60
ZK Essentials
ZK Essentials
Documentation:Books/ZK_Essentials If you have any feedback regarding this book, please leave it here. <comment>http://books.zkoss.org/wiki/ZK_Essentials</comment>
Introduction
The aim of ZK Essentials is to provide a step by step resource for a developer to address the creation of a substantial application using ZK. Each chapter navigates through a particular ZK topic and explains in detail why certain choices and implementation techniques were chosen over others in context of the application. The book chooses a shopping cart application as a platform to explore ZK. By the end of this book you should be able to gather enough information about ZK techniques and practices to implement a custom Web application. The book starts off by introducing ZK and its architecture then moves into the creation of the application.
When a ZK application runs on the server, it can have access to the backend resources, assemble UI with components, listen to user's activity, and then manipulate components to update UI. All are done at the server. The synchronization of the states of the components between the browser and the server is done automatically by ZK and transparently to the application. When running at the server, the application can access full Java technology stack. User activities, including Ajax and Server Push, are abstracted to event objects. UI are composed of POJO-like components. ZK is the most productive approach to develop a modern Web application.
An Introduction to ZK's Server client Fusion Architecture With ZK's Server+client Fusion architecture, your application will never stop running on the server. The application could enhance the interactivity by adding optional client-side functionality, such as client-side event handling, visual effect customizing or event even UI composing without server-side coding. ZK enables seamless fusion from pure server-centric to pure client-centric. You can have the best of two worlds: productivity and flexibility.
Component Based UI
In ZK, we can declare UI components using either markup language or Java.
For example, here we declared a Window [1] component, setting the border to normal and resizing its width to a definite 250 pixels. Enclosed in the Window [1] are two Button [2] components.
ZK components can also be easily declared in Java source codes and are also compatible with Java. Please see the following.
Component Based UI package org.zkoss.zkdemo; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.GenericRichlet; import org.zkoss.zul.*; public class TestRichlet extends GenericRichlet { //Richlet// public void service(Page page) { final Window win = new Window("ZK Essentials", "normal", false); win.setWidth("250px"); Vlayout vl = new Vlayout(); vl.setParent(win); final Textbox txtbx = new Textbox(); txtbx.setParent(vl); final Label lbl = new Label(); lbl.setParent(vl); txtbx.addEventListener("onChange", new EventListener(){ @Override public void onEvent(Event event) throws Exception { lbl.setValue(txtbx.getValue()); } });
win.setPage(page); } } Please refer to Developer's Reference: Richlet for more details on programming with Richlets.
Component Based UI which would render a window containing two buttons as shown in the image below:
The markup in ZUL is equivalent to the following POJO declarations in Java: Window win = new Window(); win.setTitle("ZK Essentials"); win.setBorder("normal"); win.setWidth("250px"); Button helloBtn = new Button(); helloBtn.setLabel("Hello"); helloBtn.setParent(win); Button byeBtn = new Button(); byeBtn.setLabel("Good-bye"); byeBtn.setParent(win); Components on the server are translated into instructions(in JSON) for widget (JavaScript objects) creation and then sent to the client.
Component Based UI
The markup source is: <window title="ZK Essentials" mode="overlapped" border="normal" width="250px"> <label id="lbl"/>World ! <button label="Hello " onClick="lbl.value = self.label"/>
Component Based UI <button label="Good-bye " onClick="lbl.value = self.label"/> </window> The value of the label with ID "lbl" depends on which button the user clicks: "Hello", or "Good-bye". When a button is clicked, the value of the button's label is assigned to the value of the label. Note that the string "World !" is automatically converted to a label. ZK assigns each component with a UUID (Universal Unique Identifier) to keep track of all the components internally. This UUID is over-ridden when the developer provides it with a more legible ID. Finding a Component Programmatically in Java Suppose we have a POJO declaration for a simple UI that looks something like this: Window outerWin = new Window(); Button outerBtn = new Button(); btn.setParent(outerWin); Window innerWin = new Window(); innerWin.setParent(outerWin); Button innerBtn = new Button(); innerBtn.setParent(innerWin); For better readability, the equivalent declaration above using ZUML in ZUL file is given here: <window id="outerWin"> <button id="outerBtn"> <window id="innerWin"> <button id="innerBtn"/> </window> </window> Now suppose we have a controller class where we want to programmatically access and modify the children components (outerBtn, innerWin, innerBtn); how should we accomplish this if we only have a reference to the Window component? One of the approaches is to call the component's Component.getFellow() access the inner Window, we could do the following:
[7]
An ID Space is a way to group components into a more manageable collection in ZK so we don't risk getting references to components in a large component tree mixed up. This concept is illustrated below:
Component Based UI
The Window [1] component and Page [8] are Space Owners by default. We can make any component a space owner by allowing the component to implement the IdSpace [9] interface. To identify a component's space owner, we can call its getSpaceOwner method. There are other methods to accomplish this, we summarize them in a table for clarity:
Component outerBtn method = (Button)outerWin.getFellow("outerBtn"); Note The components outerWin, outerBtn, and innerWin form an ID Space; with outerWin being the Space Owner. Components in the same ID Space can call each other by ID using the getFellow method. innerWin belongs to the same ID Space as outerWin, hence, it could be called using the getFellow method.
innerWin
= (Window)outerWin.getFellow("innerWin");
innerBtn
= (Button)outerWin.getFellow("innerWin").getFellow("innerBtn"); innerWin and innerBtn belong to an ID Space of their own, with innerWin being the Space Owner. innerWin is also a member of the ID Space which outerWin is the Space Owner. Hence, we can call getFellow on outerWin to get innerWin, then call getFellow on innerWin to get innerBtn. = (Button)Path.getComponent("/outerBtn"); [10] The Path provides the utility method getComponent which takes the relative path of the component as its argument. /outerBtn is equivalent to outerWin/outerBtn innerWin and outerBtn both have outerWin as an ID Space Owner. innerBtn has innerWin as its ID Space Owner, innerWin in turn has outerWin as its Space Owner. Hence, we write /innerWin/innerBtn, which is equivalent to outerWin/innerWin/innerBtn
outerBtn
innerWin
= (Window)Path.getComponent("/innerWin");
innerBtn
= (Button)Path.getComponent("/innerWin/innerBtn");
Component Based UI
8
The getFirstChild method returns the first child component of the caller. The advantage of using this method is that you don't even need to know the component ID to fetch the component. The getFirstChild method gets the outerBtn since it's outerWin's first child component. We then call the getNextSibling method to find the innerWin. We compound another getFirstChild method to get the first, and only, child component of innerWin.
outerBtn
= (Button)outerWin.getFirstChild();
innerWin
= (Window)outerWin.getFirstChild().getNextSibling();
innerBtn
= (Button)outerWin.getFirstChild().getNextSibling().getFirstChild();
Component Based UI <button/> </window> Attribute value must be quoted Correct: <window width="600px"/> Incorrect: <window width=600px/> Using XML tags, we declare a component and set a component's attributes; as an alternative to coding in Java files, we could set a component's attributes to initialize values, evaluate conditions/expressions, and handle events. The figure belows shows an example of how we could easily dictate whether a component is to be displayed on a page when the "if" condition is declared as its attribute. The label "Hello World !" is not displayed since its "if" condition is declared to be false.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html# [2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Button. html# [3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Textbox. html# [4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ ui/ Page. html# [5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html# [6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ ui/ Desktop. html# [7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getFellow() [8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Page. html# [9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ IdSpace. html# [10] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Path. html# [11] Please refer to resources on Internet, such as (http:/ / www. w3schools. com/ xml/ xml_whatis. asp) and (http:/ / www. xml. com/ pub/ a/ 98/ 10/ guide0. html) should you need to get comfortable with its syntax and conventions
10
Breaking down how the event driven model works Register Event Listeners At line 3 and 4, we register the onClick events on the buttons so we'll know when and which button is clicked. <button label="Hello " onClick="lbl.value = self.label"/> <button label="Good-bye " onClick="lbl.value = self.label"/> Event Sent with Request When the user clicks one of the buttons, its respective onClick event is sent to the server for processing by default (optionally, we could handle events from the client directly). As shown highlighted in blue, the "onClick" event and the button's UUID (Universal Unique Identifier) is sent by the Ajax request.
Event Handling Instructions From the request, the ZK upload engine knows which component fired the event and what event is fired. It also knows how to handle this request because we've specified the event handling code in ZUL using EL (Expression Language): "lbl.value = self.label", which translates into the following Java code:
Event Driven Programming lbl.setValue(self.getLabel); Where lbl is the ID we assigned to the Label component and self refers to the Button component itself (akin to this in Java) Update Instruction Sent Back in Response The server then sends the instructions back to the client to reflect this change in the label's value:
11
Hello
Good-bye Where the instructions tell the client engine to set the attribute "value" to "Hello" for the component with the UUID "z_d__2", which is the label component.
We could program this behavior entirely in a ZUL file: <window id="win" title="ZK Essentials" border="normal" width="250px"> <button label="Hello"> <attribute name="onClick"> <![CDATA[ Button btn = new Button(); btn.setLabel("World !"); btn.setParent(win); ]]> </attribute>
Event Driven Programming </button> </window> Inside the button declaration, we declare the "onClick" event as the button's attribute and code its event handling instructions directly in ZUL. This is possible because the Java code is interpreted by BeanShell [1] dynamically. In the Java code, we first created a new Button, set its label to "World !", and then set the Window component as its parent. Despite its convenience, registering event directly from ZUL is not recommended if your application is performance sensitive. As all the Java code needs to be interpreted dynamically. Event Handling In a Controller Following the ZK MVC pattern, we create a controller class where we can wire variables and listen to events in our UI. So here we make the necessary changes: At line 1, we declared: apply=controller class name so that the events fired within the window is all forwarded to the controller for event handling.
12
<window id="win" title="ZK Essentials" border="normal" width="250px" apply="demo.zkoss.Sa <button label="Hello"/> </window> We then implement our controller class: At line 9, we extend the SelectorComposer [2] so we can wire variables and listen to events using annotations whose parameters are CSS-like selectors. At lines 11 and 12, we declare a Window component and wire it with the window in our ZUL file using annotation. At line 14, we use a selector to wire the Button whose label is "Hello" and listen to its onClick event. At line 15, the method createWorld contains the same Java code we did previously to dynamically create a button. package demo.zkoss; import import import import import org.zkoss.zk.ui.select.SelectorComposer; org.zkoss.zk.ui.select.annotation.Listen; org.zkoss.zk.ui.select.annotation.Wire; org.zkoss.zul.Button; org.zkoss.zul.Window;
public class SampleCtrl extends SelectorComposer { @Wire("window") Window win; @Listen("onClick = button[label='Hello']") public void createWorld(){ Button btn = new Button(); btn.setLabel("World !"); btn.setParent(win); } }
Event Driven Programming Event Handling of Dynamically Generated Components Suppose we take our previous example a step further, we would like to clear off the "Hello" button in the window when we click the dynamically created "World !" button. We'll need to register an event listener to the dynamically created "World !" button. At line 12, we add a new event listener to the newly created button. The addEventListener method takes an event name (String) and an EventListener [3] as its arguments. Within the anonymous EventListener class, we implement the onEvent method to have the Window component fetch its first child component, which is the button labelled "Hello", and call its detach method to clear it off the Window component. public class SampleCtrl extends SelectorComposer { @Wire("window") Window win; @Listen("onClick = button[label='Hello']") public void createWorld(){ Button btn = new Button(); btn.setLabel("World !"); btn.setParent(win); btn.addEventListener("onClick", new EventListener(){ public void onEvent(Event event) throws Exception { Button helloBtn; win.getFirstChild().detach(); }; }); } }
13
References
[1] http:/ / www. beanshell. org/ [2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ ui/ select/ SelectorComposer. html# [3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventListener. html#
14
The Resources
This book comes with two examples: Store - which uses an in memory model defined by Java statements Store with database - which uses a persistent model driven by Hibernate. This is used at the last chapter of the book. If you require Maven & Git, please download and install them first: 1. Maven [1] 2. Git [2]
Store
The store is available on github, you can retrieve it using the following commands: git clone git://github.com/zkbooks/ZK-Essentials.git zkessentials git checkout withoutdb If you prefer to set up the project in eclipse please refer to here.
References
[1] http:/ / maven. apache. org/ download. html [2] http:/ / git-scm. com/ download
15
Set-up ZK Studio
1. Open your Eclipse IDE 2. On the main menu bar, select Help > Install New Software... 3. Copy and paste the ZK Studio plugin's update URL : http://studioupdate.zkoss.org/studio/update/eclipse_3_5 For Eclipse 3.5, or http://studioupdate.zkoss.org/studio/update/eclipse_3_6 for Eclipse 3.6 into the input box as shown below:
Setting Up the Applications Using Eclipse 5. Once the download is complete, go to Window > Preferences > ZK > ZK Packages, click Add File/Directory to add the ZK package downloaded 6. Check-mark the package and click "Ok"
16
Store
17
Store
Prerequisites
You require Maven for ZK Studio/Eclipse, if you do now have it you can follow this tutorial on installing it [1]
4. Click browse and locate the extracted zkessentials directory and press OK
Store
18
6. The ZK Essentials project will now be in your workspace ready to run after maven downloads all the appropriate files
Store
19
References
[1] http:/ / m2eclipse. sonatype. org/ installing-m2eclipse. html [2] https:/ / github. com/ zkbooks/ ZK-Essentials/ zipball/ withoutdb
4. Click browse and locate the extracted zkessentials directory and press OK
20
6. The ZK Essentials project will now be in your workspace ready to run after maven downloads all the appropriate files
21
References
[1] https:/ / github. com/ zkbooks/ ZK-Essentials/ zipball/ withdb
Pop-up
Allow contents contained in an area to be evoked on top of its parent page with its position adjustable Allow contents to be hidden so page space is disclosed
[5]
, Pop-ups
[6]
[7]
, Border Layout
, Border Layout
[8]
How to Layout Components Demo [10] Table Layout Component Reference Demo [11] Tab Box Component Reference Demo [2] Group Box Component Reference Demo [12] Vlayout Component Reference Demo [13] Hlayout Component Reference Demo [13]
22
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Tabbox. html# [2] http:/ / www. zkoss. org/ zkdemo/ tabbox [3] http:/ / www. zkoss. org/ zkdemo/ grid/ hierarchy [4] http:/ / www. zkoss. org/ zkdemo/ grid/ master_detail [5] http:/ / www. zkoss. org/ zkdemo/ window/ modal_dialog [6] http:/ / www. zkoss. org/ zkdemo/ menu/ pop-ups [7] http:/ / www. zkoss. org/ zkdemo/ layout/ business_portal [8] http:/ / www. zkoss. org/ zkdemo/ layout/ border_layout [9] http:/ / www. zkoss. org/ zkdemo/ layout/ splitter [10] http:/ / www. zkoss. org/ zkdemo/ layout/ column_layout [11] http:/ / www. zkoss. org/ zkdemo/ layout/ table_layout [12] http:/ / www. zkoss. org/ zkdemo/ layout/ group_box [13] http:/ / www. zkoss. org/ zkdemo/ layout/ light_boxes
Using ZK Borderlayout
23
Using ZK Borderlayout
Suppose we have the following requirements in our shopping cart application: Product View - A table displaying the available products to purchase Shopping Cart View - A list of items placed in the shopping cart Order View - A record of all the orders placed
The attributes involved in the configuration of border layout is listed in the table below:
Using ZK Borderlayout
24
Attributes size
Description set size in pixels or a percentage relative to its parent component show/hide border; "normal" shows border, while "none" hides border allow the whole division to show/hide set the maximum and minimum allowable size for a component; allow the contents area size to be adjustable
border
<east border="normal">...</east>
collapsible
<west collapsible="true">...</west>
The borders can be made adjustable (splittable="true"), and the east and south components can be collapsed to give room to the center component displaying the Product View.
25
title="Shopping Cart View" size="30%" splittable="true" collapsible="true"> <div vflex="1" style="background:#D9E5EF;" apply="demo.web.ui.ctrl.ShoppingCa //Shopping Cart View Implementation </div> </east> <south
title="Order View" size="250px" border="0" splittable="true" collapsible="t <div vflex="1"> //Order View Implementation </div> </south> </borderlayout> Here we note a few key implementations: Since the size of the borderlayout is not specified, its dimensions will assume those of its parent component Window [1]. The borderlayout component itself does not have the "border" attribute, only its children (north, west, etc..) do. north, east, center, west, and south, are allowed to have only one child component; therefore, you should use container components such as Div [2] or Window [1] to wrap multiple components when placing them in borderlayout's children components. The center component does not take any size attributes; its height is dependent on the dimensions of north/south, whereas its width is dependent on the dimensions of east/west. The splittable and collapsible attributes are set to "true" to allow the dimensions of east and south to be adjusted dynamically by the user. The apply attribute is used here to assign a controller to the container component so components within it are auto-wired with data objects and events are automatically forwarded to event handlers.
Using ZK Borderlayout
26
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Borderlayout. html# [2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Div. html#
27
Login Behavior
Before starting to implement the login, first let us define the behavior that is required in the use case. The use case below represents the flow we would like to deliver to users upon entry or login to the system.
The Basic Login <row> Password:<textbox id="passwordTxb" </rows> </grid> <button id="confirmBtn" label="confirm"/> <label id="mesgLbl"/> </window>
28 type="password"/></row>
The ZUL representation is very simple and consists of a Grid which aids the layout of the Textbox along with a Button to perform the confirm action and a Label to notify the user of any issues with the login process. At the moment the ZUL file represents a dumb login page as the button is not mapped to an action. The recommended way to add functionality to your UI is to follow the MVC pattern of development. The following section details how to implement ZK using the MVC pattern.
Implementing ZK MVC
The MVC is developers' favorite pattern as it neatly separates various application layers in a clear manner. ZK fosters this process by allowing UI declaration to be done using an XML declarative language. It should be noted, however, that we can create Swing programmatic UIs using Richlets. ZK MVC revolves around two key items, the SelectorComposer [1] and the apply attribute. The SelectorComposer [1] is a utility class which adds auto-wire functionality to provide access to UI objects from Java code without any effort. Lets see the demonstration of how to add this functionality into the login page.
Implementing ZK MVC </row> </rows> </grid> <button id="confirmBtn" label="confirm" /> <label id="mesgLbl" /> </window> As demonstrated in the code above, we set the apply attributes value to a full class name with path. For the above this class would be <mp>demo.web.ui.ctrl.LoginViewCtrl</mp>.
29
Implementing ZK MVC super.doAfterCompose(comp); //to be implemented, lets check for a login } Now we have the insights of what advantages ZK provides us with by wiring the components, events, and data automatically. We need to move on to deal with our goal of implementing a user management system to check credentials and redirect it according to our use case. The next section walks you through how user credentials is implemented using a session.
30
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ SelectorComposer. html# [2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Event. html# [3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ SelectorComposer. html#doAfterCompose(org. zkoss. zk. ui. Component)
Managing credentials using ZK Sessions { synchronized(zkSession){ UserCredentialManager userModel = (UserCredentialManager) zkSession.getAttribute(KEY_USER_MODEL); if(userModel==null){ zkSession.setAttribute(KEY_USER_MODEL, userModel = new UserCredentialManager()); } return userModel; } } } The manager is very standard exposing a login method which if successful sets the <mp>User</mp> object and a <mp>isAuthenticated</mp> method which checks to see whether the user is null and returns accordingly. Having put this into place we can now make use of it in our controller to change the page flow of the application.
31
if(UserCredentialManager.getIntance(session).isAuthenticated()){ Executions.sendRedirect("index.zul"); } This concludes the login topic, and the next session we will see how to display information to users using a Grid [6] and Listbox [7].
References
[1] [2] [3] [4] [5] [6] [7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Session. html# http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html# http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#getCurrent() http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html# http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#sendRedirect(java. lang. String) http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Grid. html# http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Listbox. html#
32
Displaying the Data For more information on the intracacies of this relationship please refer to link needed here. public class ProductViewCtrl extends SelectorComposer<Div> { @Wire private Grid prodGrid;
33
@Override public void doAfterCompose(Div comp) throws Exception { super.doAfterCompose(comp); List<Product> prods = DAOs.getProductDAO().findAllAvailable(); ListModelList<Product> prodModel = new ListModelList<Product>(prods); prodGrid.setModel(prodModel); } ... } Notice that the Grid reference "prodGrid" in ProductViewCtrl.java is auto-wired with the Grid declared in mark up whose ID is "prodGrid". <div id="PrdoDiv" apply="demo.web.ui.ctrl.ProductViewCtrl"> <grid id="prodGrid"> <columns sizable="true"> <column image="/image/Bullet-10x10.png" align="center" width="100px" /> <column label="Name" width="100px" /> <column label="Price" width="50px" /> <column label="Quantity" width="50px" /> <column label="Arrive Date" width="110px" /> <column label="operation" /> </columns> <template name="model"> ... </template> </grid> </div>
34
In this case our ZUML fragment needs to contain a row and then a control for each column. Here is the final template to recreate the interface. <grid id="prodGrid"> <columns sizable="true"> ... </columns> <template name="model"> <row value="${each}"> <image height="70px" width="70px" src="${each.imgPath}" /> <label value="${each.name}" /> <label value="${each.price}" />
35 <label value="${each.quantity}" /> <label value="${each.createDate}" /> <productOrder maximumQuantity="${each.quantity}" product="${each}" />
</row> </template> </grid> A template is a ZUML fragment that defines how to render data model in its children components and can contain any ZUML element you would like including other templates. It is generally used within a Listbox, Grid, Tree and Combobox to describe how repeating items are displayed. In this case the template defines the layout of each row. The row value is assigned as "each" where each refers to a bean provided by the model. The syntax of the template looks incredibly similar to what one observed in the databinding chapter except using a $ instead of an @. This is because the markup below makes use of EL. EL expressions differ from databinding in the fact that they are only interpreted once, whereas databidning will be re-interpreted whenever it is notified a value has changed. Seeing as the product list will remain relatively constant, we'll use EL here. For more information on EL please visit link needed here In this case, the Template component's output is relatively simple, however, one interesting item in the Template is the productOrder component. This is in fact a composite control which is created by developers which we'll look into in the next section.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModel. html# [2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModelList. html#
Composite Components in the MVC public class ProductOrder extends Cell implements IdSpace, AfterCompose { ... public void afterCompose() { // 1. Render the ZUML fragment Executions.createComponents("/WEB-INF/composite/productorder.zul", this, new HashMap<String, Object>() { private static final long serialVersionUID = 7141348964577773718L; { put("maximumQuantity", getMaximumQuantity()); } }); // 2. Wire variables, components and event listeners (optional) Selectors.wireVariables(this, this, null); Selectors.wireComponents(this, this, false); Selectors.wireEventListeners(this, this); } ... }
36
<spinner id="spnQuantity" constraint="min 1 max ${arg.maximumQuantity}" value="1"/> <button id="btnAdd" label="add" image="/image/ShoppingCart-16x16.png" /> <label id="lblError" /> </zk> In the above fragment one can see how the maximum quantity, which was passed to the fragment on creation, is accessed and used using EL expressions.
37
38
Strength of MVVM
Separation of data and logic from presentation The key feature is that ViewModel knows nothing about View's visual elements guarantees the one way dependency from View to the ViewModel thus avoiding mutual programming ripple effects between UI and the ViewModel. Consequently, it brings the following advantages: It's suitable for design-by-contract programming. [4] As long as the contract is made (what data to show and what actions to perform), the UI design and coding of ViewModel can proceed in parallel and independently. Either side will not block the other's way. Loose coupling with View. UI design can be easily changed from time to time without modifying the ViewModel as long as the contract does not change. Better reusability. It will be easier to design different views for different devices with a common ViewModel. For a desktop browser with a bigger screen, more information can be shown on one page; while for a smart phone with limited display space, designing a wizard-based step-by-step operation UI can be done without the need to change (much of) the ViewModel. Better testability. Since ViewModel does not "see" the presentation layer, developers can unit-test the ViewModel class easily without UI elements.
39
As stated in the paragraph earlier, the binder synchronizes data between UI and ViewModel. 1. A user presses a button on the screen (perform an action). 2. A corresponding event is fired to the binder. 3. The binder finds the corresponding action logic (It is Command) in the ViewModel and executes it. 4. The action logic accesses data from Model layer and updates ViewModel's corresponding properties. 5. ViewModel notify the binder that some properties have been changed. 6. Per what properties have been changed, the binder loads data from the ViewModel.
What is the MVVM 7. Binder then updates the corresponding UI components to provide visual feedback to the user. References
[1] WPF Apps With The Model-View-ViewModel Design Pattern http:/ / msdn. microsoft. com/ en-us/ magazine/ dd419663. aspx [2] Introduction to Model/View/ViewModel pattern for building WPF apps http:/ / blogs. msdn. com/ b/ johngossman/ archive/ 2005/ 10/ 08/ 478683. aspx [3] Presentation Model http:/ / martinfowler. com/ eaaDev/ PresentationModel. html [4] Design by contract http:/ / en. wikipedia. org/ wiki/ Design_by_contract [5] binder ZK Developer's Reference/MVVM/DataBinding/Binder
40
To start with the author will now give an overview of what the final ZUL fragment for the MVVM will look like. This section will then teach developers how to create this step by step. <div apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('demo.web.ui.ctrl.ShoppingCartViewModel')">
<listbox id="shoppingCartListbox" model="@load(vm.cartItems)" selectedItem="@bind(vm.selectedItem)"> <listhead sizable="true"> <listheader label="Name" /> <listheader label="Price" /> <listheader label="Amount" /> <listheader label="subTotal" /> <listheader align="center" /> </listhead> <template name="model" var="cartItem"> <listitem> <listcell label="@load(cartItem.product.name)" /> <listcell label="@load(cartItem.product.price)" /> <listcell label="@load(cartItem.amount)" /> <listcell label="@load(cartItem.product.price * cartItem.amount)" <listcell>
41
<button image="/image/DeleteCross-16x16.png" onClick="@comm </listcell> </listitem> </template> ... </listbox> ... </div>
42
<listbox id="shoppingCartListbox" model="@load(vm.cartItems)" selectedItem="@bind(vm.selectedItem)"> <listhead sizable="true"> ... </listhead> <template name="model" var="cartItem"> <listitem> <listcell label="@load(cartItem.product.name)" /> <listcell label="@load(cartItem.product.price)" /> <listcell label="@load(cartItem.amount)" /> <listcell label="@load(cartItem.product.price * cartItem.amount)" <listcell> <button image="/image/DeleteCross-16x16.png" onClick="@comm </listcell> </listitem> </template> ... </listbox>
Displaying Information from the View Model Line 6 brings about an intersting use case as instead of just specifying a bean's property it specifies an equation. The binder is capable of handling expressions. There are many more expressions available for more information please click here link needed. This takes care of basic loading and saving functionality, but what about actions such as clicking buttons? The next section introduces how to give commands based on component actions.
43
44
clearOrders(); } The method in the View Model will therefore be called when the button is clicked. A keen observer may have noticed the @NotifyChange annotation on the method, the following section discusses this and the uses of it.
Performing Actions on Events </listitem> </template> Line 12 demonstrates the syntax, @command is used as normal but there is an extra attribute added, in this case it is cartItem=cartItem, the basic syntax is as follows: nameThatYouAssign=variable In the above case the template is defining the variable name to access the active bean as cartItem. This value can then be retrieved in the View Model using the command function and a parameter prepended with the annotation @BindingParam. Below shows this method at work. @Command @NotifyChange({"cartItems", "shoppingCart"}) public void deleteOrder(@BindingParam("cartItem") CartItem cartItem) { getShoppingCart().remove(cartItem.getProduct().getId()); } The @BindingParam annotation specifies the name as "cartItem" this corresponds to the name as defined in the UI. After doing this the variable is accessible in the function with no further effort needed. This concludes the teaching of knowledge on how to implement both the MVC and MVVM patterns for the product view, shopping cart and orders list. The last question is how do these 3 sections communicate. The product view uses MVC but needs to tell the shopping cart (which uses MVVM) that a product has been added to the order and the shopping cart needs to tell the orders list that there is a new order even though they both use different View Models. The next session introduces how to do this.
45
Communicating between MVC and MVVM patterns and between View Models
Communication between the MVC, MVVM and view models sounds to be the most difficult task to perform, however, it is in fact very easy to do and extremely powerful. The first item of interest is dealing with communication between the MVC and MVVM.
Communicating between MVC and MVVM patterns and between View Models
46
ProductOrder po = (ProductOrder) fe.getTarget(); try { UserUtils.getShoppingCart() .add(po.getProduct(), po.getQuantity()); } catch (OverQuantityException e) { po.setError(e.getMessage()); } BindUtils.postGlobalCommand(null, null, "updateShoppingCart", null); } Line 17 shows the function "BindUtils" and its function "postGlobalCommand" is used to post a command to the binder. In this case the first two strings take the name of the binder and the scope of said binder, in this case it is set to null to use the default. In most cases one would want to set this to null. Then the string name for the command is given along with a map of arguments to pass, in this case null as there are no arguments. This globalCommand is then executed by the binder on any view model that has registered for it. In the case of this application the ShoppingCartViewModel needs to refresh the cart items when a product is added to a cart. Therefore a function is created in the ShoppingCartViewModel and registered as a global command, this function has the ability to do some processing and then notify the binder that the cartItems in that view have changed. The snippet below shows this in action. public class ShoppingCartViewModel { ... @GlobalCommand @NotifyChange("cartItems") public void updateShoppingCart() { //no post processing to be done } } This takes care of passing commmands from the MVC composer to MVVM view model, the next section discusses message passing between MVVM view models.
Communicating between MVC and MVVM patterns and between View Models //no post processing needed } This function will updated the orders and it has the option to do post processing if necessary. The main question is how to invoke this, it is possible to do so using the postGlobalCommand function of the BindUtils class, the call to do this would be as follows: BindUtils.postGlobalCommand(null, null, "updateOrders", null); However, an easier way is to place this command into the zul file, so the submit order button structure looks like this: <button id="submitOrderBtn" label="submit" onClick="@command('submitOrder') @global-command('updateOrders')" disabled="@load(empty vm.cartItems)" /> Here one notices that as well as having an @command assigned to the onClick attribute, it now also has an @global-command, which is equivalent to the java function above this snippet. This is the easiest way of providing the command. This is all one needs to know on sending messages between the MVC and view models. The next section deals with adding Hibernate and a database into the mix.
47
48
Setting up Hibernate
Hibernate requires configuration to work. Our sample application contains two configuration files placed in the folder included in the classpath: 1. log4j.properties 2. hibernate.cfg.xml The log4j.properties file is used to inform Log4j of the level we wish to log. In our case we set the lowest level of logging possible which in this case is DEBUG and TRACE for the hibernate classes. Setting this to such a fine grained setting enables us to see exactly what is going on during hibernate. The second file hibernate.cfg.xml contains detailed configuration for Hibernate in XML format. The content is provided and analyzed below.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//E <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property> <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property> <property name="hibernate.connection.url">jdbc:hsqldb:file:data/store</property> <property name="hibernate.connection.username">sa</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.current_session_context_class">thread</property>
49
<property name="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQuery
<property name="hibernate.hbm2ddl.auto">create-drop</property> <mapping class="demo.model.bean.Order"/> <mapping class="demo.model.bean.OrderItem"/> <mapping class="demo.model.bean.User"/> <mapping class="demo.model.bean.Product"/> </session-factory> </hibernate-configuration> Line 5 let hibernate know that we intend to use HSQLDialect as it is an HSQL database Line 6 specifies the jdbc driver class Line 7 is the path to the datastore, in this case informing HSQLDB that we will make use of a file named store in the folder data Line 8 is the username, in this case a default sa Line 9 dictates that we would like Hibernate to print out the SQL Line 10 states that the session context is thread based Line 11 states that the HQL style we would like to use is classic HQL is Hibernates own query language. For more information please click here [1] Lines 14-19 are to do with definition of the entity types within the application and generation of the database. These topics are covered in the following section.
References
[1] http:/ / docs. jboss. org/ hibernate/ core/ 3. 3/ reference/ en/ html/ queryhql. html
50
One of the most important properties in this XML file is hibernate.hbm2ddl.auto which will be set as create-drop. When Hibernates SessionFactory is set, it will automatically create an option which validates or exports the schema DDL to the database. This is very useful during the process of developing a data driven application as it eliminates the need to manually update SQL scripts when your schema needs to change. However, this setting is not recommended for production usage. NOTE: As the database schema is generated and dropped at the beginning and end of every session, this means that the data is not persisted at the end of the application. By changing this to update the data it will be persisted across multiple runs. The other properties enable the mapping of Java classes to Database tables. In general, this mapping is declared in two ways: By providing the class name using the attribute <mp>class</mp> By providing a URI to an XML resource which describes the mapping Both mapping by XML or annotation can achieve the same results but selecting which implementation method to deploy will depend on the developer's preference. Some developers prefer XML while others incline towards annotations. In this case we chose to use annotations.
51
@Temporal(TemporalType.TIMESTAMP) private Date createDate; private float price; private int quantity; private boolean available; private String imgPath; } The above code demonstrates how the <mp>Product</mp> class was annotated to allow persistence. Taking the annotations one at a time we will explore what they do. @Entity An entity can be considered as a lightweight persistence domain object which represents a table in a relational database. In this case we let Hibernate know that the class Product is an entity. This is required if you would like to introduce database persistence to classes. @Table(name="products") The table entity is set at the class level which enables us to define the table, catalog and schema names of the entity mappings. If names are not specified the default values will be used. However, in our case Product is an SQL reserved word therefore we have to change the table name to products. @Id We need to let Hibernate know which field of ours is the ID and this can be easily achieved by applying the annotation @Id. @GeneratedValue(strategy=GenerationType.AUTO) Along with the annotation, @Id, we also need to inform Hibernate that it is auto-incremented by the database. We let Hibernate know by using the @GeneratedValue annotation and setting the strategy to GenerationType AUTO. @Column(name="productname") The next annotation refers to the definition of a column. Under normal circumstances we do not need to provide a name however, in this case without specifying the name of the column it would default to "name." Just as "product", "name" is also an SQL reserved keyword hence we specify a valid column name, "productname". @Temporal(TemporalType.TIMESTAMP) Java APIs do not define the temporal precision of time. Sometimes we would like to describe the expected precision when placing data into persistent storage. To do this we use the Temporal attribute. Types include DATE, TIME and TIMESTAMP. The rest of the classes in our example use the same annotation strategies. There is only one exception present in Order.java which describes the relationship between an <mp>Order</mp> and its <mp>OrderItems</mp>.
52
In order for Hibernate to generate the database schema and map the relationship correctly we need to describe it. To do this we use two annotations: @OneToMany @JoinTable Lets explore the relative code. @OneToMany @JoinTable( name="OrderedItems", joinColumns=@JoinColumn(name="orderid"), inverseJoinColumns=@JoinColumn(name="orderitemid") ) @LazyCollection(value=LazyCollectionOption.FALSE) private List<OrderItem> items = new ArrayList<OrderItem>(); The "name" attribute tells Hibernate the table name which we would like to use or in our case to create. The "joinColumns" attribute requires the foreign key columns of the join table which reference the primary table of the entity owning the association, in this case the order id. On the other hand the "inverseJoinColumns" attribute requires the foreign key columns of the join table which reference the primary table of the entity that does not own the association, in this case the OrderItems id. We provide both attributes a "@joinColumn". Annotations require "name" and "referencedColumnName" which are the name of the column in the table OrderedItems and the name of the referenced column within the entity respectively. The <mp>Order</mp> owns the association (ie. Owns the Order items) so its key is passed to "joinColumns" and <mp>OrderItem</mp> does not own the association so its information is passed to "inverseJoinColumns."
53
Lazy Loading
There is one last thing to consider; when an <mp>Order</mp> object is loaded we want to load its <mp>OrderItems</mp> at the same time. Hibernate only loads the first object level which can lead to exceptions such as a LazyLoadException when we access the item list which has not been initialized. It means that when the Order object is loaded then the its OrderItems arent. In this case it is easy to fix by making use of the @LazyCollection annotation and setting the option to FALSE: @LazyCollection(value=LazyCollectionOption.FALSE) This will ensure that when an <mp>Order</mp> is loaded its <mp>OrderItems</mp> are too. When designing larger systems we have to take into account how much data should be loaded and whether we should follow lazy loading practices or not. For the purposes of this chapter lazy loading is not required. The final section introduces Hibernate Session management and how we implemented the DAOs.
Hibernate Session Management and Implementing the DAOs return sessionFactory; } public static Session openSession() { return sessionFactory.openSession(); } } In this class, the SessionFactory is built statically upon first accessing the object and then provides static utility methods to access the SessionFactory and then open a new Session. Please note that there is no need to build the SessionFactory when the application loads or close it when the application terminates. To do so, we implement two ZK interfaces, WebAppInit and WebAppCleanup and specify the implementing class in the zk.xml file: public class HibernateListeners implements WebAppInit, WebAppCleanup { public void init(WebApp webapp) throws Exception { //initialize Hibernate StoreHibernateUtil.getSessionFactory(); } public void cleanup(WebApp webapp) throws Exception { //Close Hibernate StoreHibernateUtil.getSessionFactory().close(); } } <zk> <listener> <listener-class>demo.model.HibernateListeners</listener-class> </listener> </zk> In this case, we named our class <mp>HibernateListeners</mp> and made sure that the <mp>SessionFactory</mp> was built on initialization of the web application and closed in the cleanup method. This initializes and closes Hibernate now and we need to make use of it in the DAOs.
54
Hibernate Session Management and Implementing the DAOs public List<Product> findAll(){ //return new ArrayList<Product>(dbModel.values()); Session session = StoreHibernateUtil.openSession(); Query query = session.createQuery("from products"); List<Product> products = query.list(); session.close(); return products; } In this code snippet, we utilize our <mp>HibernateUtil</mp> to open a new session at line 3 and then create a query. The query is written in HQL (Hibernate Query Language) retrieving all the rows from the table products. For more information on HQL please click here [1]. Once we create the Query object, we can use its listed method to retrieve the <mp>List</mp> of products, close the session and then return. At the same time sometimes we need to retrieve all available <mp>Products</mp>. This means we need to retrieve all the Products where available is equal to true. In our example, we do this by creating a Criteria object: public List<Product> findAllAvailable(){ Session session = StoreHibernateUtil.openSession(); Transaction t = session.beginTransaction(); Criteria criteria = session.createCriteria(Product.class).add(Restrictions.eq("available", true)); List<Product> products = criteria.list(); t.commit(); session.close(); return products; } The snippet follows the same session management strategy, however, this time we create a <mp>Criteria</mp> object to add a restriction that "available" must be true. Then we can use the method <mp>criteria.list()</mp> to retrieve a list of products which are available. The retrieval methods in each DAO follow a similar strategy so finally lets take a look at the add method of the <mp>OrderDAO</mp> which persists an <mp>Order</mp>. public void add(Order order){ Session session = StoreHibernateUtil.openSession(); Transaction t = session.beginTransaction(); session.persist(order); t.commit(); session.close(); }
55
Hibernate Session Management and Implementing the DAOs We see that the Session management strategy is exactly the same, however, now we use the sessions persist function to add the <mp>Order</mp> object to the session, then we commit the transaction and close the session. This will then add the Order object to the relative table.
56
References
[1] http:/ / community. jboss. org/ wiki/ sessionsandtransactions
Hibernate Summary
As demonstrated by this tutorial Hibernate is highly configurable and very powerful. This chapter covered the very basic annotations and usage of Hibernate, therefore when employing the power of Hibernate for a larger application we should further explore Hibernate's options such as not allowing null values for certain table columns. For a complete list of Hibernate's annotations please click here [1].
References
[1] http:/ / docs. jboss. org/ hibernate/ annotations/ 3. 5/ reference/ en/ html_single/
Components
Components are UI elements that an application developer put them together to build the application UI, just like LEGO bricks. Consult ZK_Component_Reference guide on what children components a particular component can have. Components can be declared in ZUML (ZK User Interface Markup Language, in XML syntax), or alternatively, in Java. Components (POJO) run on JVM on the server, and they have their counter parts (widgets - JS objects) on the client side. User activities on widgets are reflected to the components on the server. Component updates are reflected back to the widgets on the client side. A Page [8] is a "stage" where components come on and off to play their parts and fulfill their roles. Components are conceptually grouped in ID Spaces; we can call the getFellow() [7] method on one component to get another component as long as they are in the same ID Space. By default, components declared in Window [1] form an ID Space (including Window [1] itself) with Window [1] being the ID Space Owner. By default, a Page [8] is an ID Space Owner; any component can become the ID Space Owner by implementing IdSpace [9]
57
Events
User activities (eg. onClick) and system notifications (eg. server push) are abstracted to event objects. Event listeners on components must be registered so that event objects can be created and forwarded. By default, all events are sent to the server for processing. With ZK's Server+Client Fusion architecture, events could also be handled at the client directly. Event handling code can be declared as a component attribute in ZUL. Events can be forwarded to a controller class for handling. Event listeners can be added dynamically in Java.
ZK MVC
The ZK MVC approach provides a great separation among data (aka. model), UI and business logic (aka. control). Under the ZK MVC pattern, UI components are declared with ZUML in ZUL files. Tasks such as setting a component's data model and renderer, component manipulation are implemented in a Controller class in Java The controller class should extend SelectorComposer [1] so the components declared in ZUL can be referenced in Java code. Events are automatically forwarded to the controller class for handling.
58
Performance Tips
Please follow these guidelines for building performance critical enterprise applications: ZK Developer's Reference/Performance Tips
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zki/ ui/ util/ SelectorComposer. html# [2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ databind/ TypeConverter. html#
59
60