ZK 8.5.1 Developer's Reference
ZK 8.5.1 Developer's Reference
For ZK 8.5.1
PDF generated using the open source mwlib toolkit. See http://code.pediapress.com/ for more information.
PDF generated at: Tue, 06 Mar 2018 15:37:50 CST
Contents
Articles
ZK Developer's Reference 1
Overture 1
Architecture Overview 1
Technology Guidelines 5
Extensions 10
UI Composing 13
Component-based UI 14
ID Space 20
ZUML 24
XML Background 25
Basic Rules 28
EL Expressions 31
Scripts in ZUML 35
Conditional Evaluation 40
Iterative Evaluation 42
On-demand Evaluation 45
Include a Page 48
Load ZUML in Java 50
XML Namespaces 54
Richlet 55
Macro Component 61
Inline Macros 64
Implement Custom Java Class 66
Composite Component 70
Client-side UI Composing 75
Event Handling 76
Event Listening 76
Event Firing 81
Event Forwarding 83
Event Queues 85
Client-side Event Listening 92
MVC 93
Controller 94
Composer 95
Wire Components 102
Wire Variables 109
Wire Event Listeners 113
Subscribe to EventQueues 115
Model 117
List Model 119
Groups Model 124
Tree Model 134
Chart Model 144
Matrix Model 145
View 148
Template 148
Listbox Template 149
Grid Template 154
Tree Template 155
Combobox Template 156
Selectbox Template 157
Biglistbox Template 157
Chosenbox Template 158
Tabbox Template 159
Renderer 161
Listbox Renderer 162
Grid Renderer 163
Tree Renderer 164
Combobox Renderer 165
Selectbox Renderer 166
Biglistbox Renderer 167
Chosenbox Renderer 168
Tabbox Renderer 168
Annotations 169
Annotate in ZUML 169
Annotate in Java 171
Retrieve Annotations 172
Annotate Component Definitions 172
UI Patterns 174
Responsive Design 174
Message Box 184
Layouts and Containers 186
Hflex and Vflex 199
Grid's Columns and Hflex 209
Tooltips, Context Menus and Popups 215
Keystroke Handling 221
Drag and Drop 224
Page Initialization 227
Forward and Redirect 229
File Upload and Download 232
Browser Information and Control 234
Browser History Management 238
Session Timeout Management 241
Error Handling 244
Actions and Effects 249
Useful Java Utilities 252
HTML Tags 256
The html Component 256
The native Namespace 258
The XHTML Component Set 261
Long Operations 264
Use Echo Events 264
Use Event Queues 266
Use Piggyback 269
Communication 270
Inter-Page Communication 270
Inter-Desktop Communication 272
Inter-Application Communication 274
Templating 275
Composition 276
Templates 278
XML Output 280
Event Threads 282
Modal Windows 283
Message Box 284
File Upload 285
Theming and Styling 287
Molds 288
CSS Classes and Styles 289
Understanding the Theming Subsystem 291
Information about a Theme 292
Registering your Theme 293
Switching Themes 294
Providing Theme Resources 296
Resolving Theme URLs 302
Customizing Standard Themes 303
Creating Custom Themes 305
Archive-based Themes 306
Folder-based Themes 309
ZK Official Themes 313
ZK Bootstrap Theme 319
Internationalization 322
Locale 322
Time Zone 325
Labels 327
The Format of Properties Files 333
Date and Time Formatting 336
The First Day of the Week 338
Locale-Dependent Resources 340
Warning and Error Messages 342
Server Push 343
Event Queues 344
Synchronous Tasks 345
Asynchronous Tasks 347
Configuration 348
Clustering 352
ZK Configuration 352
Server Configuration 354
Programming Tips 355
Integration 359
Presentation Layer 359
JSP 359
Struts 363
Portal 366
ZK Filter 368
Foreign Templating Framework 370
Middleware Layer 374
Spring 374
CDI 380
EJB 384
Persistence Layer 386
JDBC 386
Hibernate 393
JPA 406
Security 415
Spring Security 415
Miscellenous 427
Google Analytics 427
Start Execution in Foreign Ajax Channel 428
Websocket Channel 430
Embed ZK Component in Foreign Framework 436
Performance Tips 440
Use Compiled Java Codes 440
Use Native Namespace instead of XHTML Namespace 443
Use ZK JSP Tags instead of ZK Filter 445
Defer the Creation of Child Components 447
Defer the Rendering of Client Widgets 448
Client Render on Demand 449
Listbox, Grid and Tree for Huge Data 451
Use Live Data and Paging 451
Turn on Render on Demand 452
Implement ListModel and TreeModel 454
Minimize Number of JavaScript Files to Load 458
Load JavaScript and CSS from Server Nearby 460
Specify Stubonly for Client-only Components 463
Reuse Desktops 466
Miscellaneous 467
Security Tips 468
Cross-site scripting 468
Block Request for Inaccessible Widgets 470
Denial Of Service 472
Cross-site Request Forgery 473
OWASP Top 10 Security Concerns In 2017 475
Performance Monitoring 477
Performance Meters 478
Event Interceptors 479
Loading Monitors 480
Step by Step Trouble Shooting 481
Testing 494
Testing Tips 495
ZATS 497
Customization 500
Packing Code 500
Component Properties 502
UI Factory 504
Init and Cleanup 506
AU Services 508
AU Extensions 509
How to Build ZK Source Code 509
Handle AU Request Resend 511
Supporting Utilities 512
Logger 512
DSP 516
iDOM 517
References
Article Sources and Contributors 518
Image Sources, Licenses and Contributors 523
ZK Developer's Reference 1
ZK Developer's Reference
If you are new to ZK, you might want to take a look at the Tutorial and ZK Essentials sections first.
Documentation:Books/ZK_Developer's_Reference
If you have any feedback regarding this book, please leave it here.
<comment>http://books.zkoss.org/wiki/ZK_Developer's_Reference</comment>
Overture
The ZK Developer's Reference is a reference of general features and advanced topics. If you are new to ZK, you
might want to start with the Tutorial [1] and ZK Essentials sections first. For information on individual components,
please refer to ZK Component Reference. For information on ZUML, please refer to the ZUML Reference.
References
[1] http:/ / www. zkoss. org/ documentation#Getting_Started
Architecture Overview
Architecture Overview 2
[1] It depends on the client. For Ajax-enabled browsers, it is a JavaScript object. For ZK Reach for Android (http:/ / code. google. com/ p/
zkreach/ ), it is a Java object running on an Android device.
[1] A widget could choose to stop this bubble-up propagation by use of UNIQ-javadoc-0-f6421cf86b2cc1a3-QINU
[2] . From the server's viewpoint, an Ajax request is another type of HTTP request.
Version History
Version Date Content
Technology Guidelines 5
Technology Guidelines
ZK provides end-to-end solutions from UI design, development, testing to production. Here is the technology
guidelines to help developers to make choices along the way.
If you are new to ZK and prefer to have some prior knowledge of ZK first, you could skip this section and come
back later when you understand more about ZK.
MVC Extractor
ZK Studio provides a tool called MVC Extractor that can convert zscript into MVC automatically. It simplifies the
process of transferring the code from prototyping to production.
Technology Guidelines 6
Documentation links
MVC: • ZK Developer's Reference: MVC
• ZK Developer's Reference: MVVM
• ZK Developer's Reference: Performance Tips
[2] [3]
• Composer and SelectorComposer
Data Binding
When to use
Data Binding automates the data-copy plumbing code (CRUD) between UI components and the data source. It is
strongly suggested to use Data Binding whenever applicable because it can help boost programmers' productivity
and the code is easy to read and maintain.
Documentation links
• ZK Developer's Reference: Data Binding
Documentation links
ZUML: • ZK Developer's Reference: ZUML
• ZK Developer's Reference: HTML Tags
Documentation links
Bookmarks: • ZK Developer's Reference: Browser History Management
• ZK Developer's Reference: Browser Information and Control
Documentation links
• ZK Developer's Reference: HTML Tags
• ZK Developer's Reference: Performance Tips|Native vs. XHTML
• ZK Developer's Reference: Performance Tips: Stubonly
Technology Guidelines 9
[1] Here is an example of composite components in ZK Demo (http:/ / www. zkoss. org/ zkdemo/ composite/ composite_component)
[2] There is a utility called ZK Composite (https:/ / github. com/ zanyking/ ZK-Composite). It allows to define a composite component with
Java annotations. Please refer to Small Talks: Define Composite Component using Java Annotation in ZK6 for the details.
JSF
When to use
JSF is a page-based framework. Because it is too complicated to use, we strongly recommend you to deploy ZK. ZK
can do whatever JSF can do or even better. However, if you have to use ZK with legacy JSF, please refer to the
Embed ZK Component in Foreign Framework section[1].
Version History
Version Date Content
Extensions
Here is an overview of the extensions of ZK. They are optional. If you are new to ZK and prefer have some
knowledge of ZK first, you could skip this section and come back later after you understand more about ZK.
There are hundreds of projects which extend ZK's functionality by boosting programmer productivity, providing
sample code and many others. For more projects, you could search ZK Project-Info [1], Google Code [2],
Sourceforge.net [3], GitHub [4], ZK Forge [5] etc.
ZK Studio [6]
ZK Studio is a visual integrated development environment for developing ZK applications with Eclipse IDE [7].
REM [8]
REM is a NetBeans [9] module for ZK. It simplifies the development of ZK with NetBeans IDE [9].
ZATS [10]
ZATS is a testing toll to automate ZK tests without the need of a browser or a server.
ZK CDT [11]
ZK CDT is a component development tool which provides wizards to simplify the creation of ZK components.
ZK Jet
Extensions 11
ZK Jet is a browser extension that works with Firefox and Google Chrome. This provides users with a ZK sandbox
environment.
run-jetty-run [12]
Use this plugin's embedded Jetty distribution to run web applications in Eclipse. This helps to speed up the ZK
development by minimizing the deployment time. The project is maintained by Tony Wang [13], a member of the ZK
Team.
ZK Spring [14]
[15] [16]
ZK Spring integrates ZK and Spring framework . It supports Spring, Spring Security , and Spring Web Flow
[17]
.
ZKGrails [19]
ZKGrails is a ZK plugin for the next generation rapid Web development framework, Grails [20].
ZEST [24]
ZEST is a lightweight MVC and REST framework which provides an additional page-level MVC pattern to isolate
the request's URI, controller and view (such as ZUML document).
Extensions 12
ZK CDI [25]
ZK CDI is integrated with ZK and JBoss Weld CDI RI [26].
ZK Seam [27]
ZK Seam is integrated with ZK and Seam [28].
ZK Themes [30]
ZK Themes is a collection of various themes, including breeze, silvertail and sapphire.
ZK Spreadsheet [31]
ZK Spreadsheet is a ZK component delivering functionalities found in Microsoft Excel to ZK applications.
ZK Pivottable [32]
ZK Pivottable is a ZK component for data summarization that sorts and sums up the original data layout.
ZK Calendar [33]
ZK Calendar is a ZK component enabling rich and intuitive scheduling functionality to ZK applications.
ZUSS
ZUSS (ZK User-interface Style Sheet) is an extension to CSS. It is compatible with CSS, while allows the dynamic
content, such as variables, mixins, nested rules, expressions, and Java methods with existing CSS syntax.
References
[1] http:/ / forum. zkoss. org/ questions/ scope:all/ sort:activity-desc/ tags:project-info/ page:1/
[2] http:/ / code. google. com/ query/ #q=zk
[3] http:/ / sourceforge. net/ search/ ?q=zk
[4] http:/ / github. com/ search?langOverride=& q=zk& repo=& start_value=1& type=Repositories
[5] http:/ / sourceforge. net/ projects/ zkforge/
[6] http:/ / www. zkoss. org/ product/ zkstudio
[7] http:/ / www. eclipse. org
[8] http:/ / rem1. sourceforge. net/
[9] http:/ / www. netbeans. org/
[10] http:/ / www. zkoss. org/ product/ zats
[11] http:/ / code. google. com/ a/ eclipselabs. org/ p/ zk-cdt/
[12] http:/ / code. google. com/ p/ run-jetty-run/
[13] https:/ / github. com/ tony1223
Extensions 13
UI Composing
[1]
Each UI object is represented by a component (Component ). In this section we will discuss how to declare UI,
including XML-based approach and pure-Java approach.
This section describes more general and overall concepts of UI composing. For more detailed and specific topics,
please refer to the UI Patterns section. For detailed information on each individual component, please refer to the ZK
Component Reference.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#
Component-based UI 14
Component-based UI
Overview
Each UI object is represented by a component (Component [1]). Thus, composing an UI object is like assembling
components. To alter UI one has to modify the states and relationships of components.
For example, as shown below, we declared a Window [1] component, enabling the border property to normal and
setting its width to a definite 250 pixels. Enclosed in the Window [1] component are two Button [2] components.
As shown above, there are two ways to declare UI: XML-based approach and pure-Java approach. You can mix
them if you like.
<zk>
<window/> <!-- the first root component -->
<div/> <!-- the second root component -->
</zk>
Component-based UI 15
getChildren()
Most of the collections returned by a component, such as Component.getChildren() [6], are live structures. It means
that you can add, remove or clear a child by manipulating the returned list directly. For example, to detach all
children, you could do it in one statement:
comp.getChildren().clear();
It is equivalent to
Note that the following code will never work because it would cause ConcurrentModificationException.
Collections.sort(comp.getChildren());
More precisely, a component has at most one parent and it has only one spot in the list of children. It means, the list
is actually a set (no duplicate elements allowed). On the other hand, Collections.sort() cannot handle a set
correctly.
Thus, we have to copy the list to another list or array and then sort it. java.util.Comparator)
Components.sort(java.util.List, java.util.Comparator) [7] is a utility to simplify the job.
Component-based UI 16
A desktop is also a logic scope that an application can access in a request. Each time a request is sent from the client,
it is associated with the desktop it belongs. The request is passed to boolean)
[11]
DesktopCtrl.service(org.zkoss.zk.au.AuRequest, boolean) and then forwarded to boolean)
[12]
ComponentCtrl.service(org.zkoss.zk.au.AuRequest, boolean) . This also means that the application can not access
components in multiple desktops at the same time.
Both a desktop and a page can be created automatically when ZK Loader loads a ZUML page or calls a richlet
(Richlet.service(org.zkoss.zk.ui.Page) [13]). The second page is created when the Include [14] component includes
another page with the defer mode. For example, two pages will be created if the following is visited:
Notice that if the mode is not specified (i.e., the instant mode), Include [14] will not be able to create a new page.
Rather, it will append all components created by another.zul as its own child components. For example,
<window>
<include src="another.zul"/> <!-- default: instant mode -->
</window>
is equivalent to the following (except div is not a space owner, see below)
Component-based UI 17
<window>
<div>
<zscript>
execution.createComponents("another.zul", self, null);
</zscript>
</div>
</window>
[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/ Grid. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Columns. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Rows. html#
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getChildren()
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Components. html#sort(java. util. List,
[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/ Desktop. html#
[10] Under portal environment, there might be multiple desktops in one browser window. However, it is really important in the developer's
viewpoint.
[11] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ DesktopCtrl. html#service(org. zkoss. zk. au. AuRequest,
[12] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ ComponentCtrl. html#service(org. zkoss. zk. au. AuRequest,
[13] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Richlet. html#service(org. zkoss. zk. ui. Page)
[14] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Include. html#
A component is a POJO object. If you do not have any reference to it, it will be recycled when JVM starts garbage
collection (http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)).
There are two ways to attach a component to a page:
1. Append it as a child of another component that is already attached to a page (
Component.appendChild(org.zkoss.zk.ui.Component) (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/
zk/ui/Component.html#appendChild(org.zkoss.zk.ui.Component)), org.zkoss.zk.ui.Component)
Component.insertBefore(org.zkoss.zk.ui.Component, org.zkoss.zk.ui.Component) (http://www.zkoss.org/
javadoc/latest/zk/org/zkoss/zk/ui/Component.html#insertBefore(org.zkoss.zk.ui.Component,), or
Component.setParent(org.zkoss.zk.ui.Component) (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/
ui/Component.html#setParent(org.zkoss.zk.ui.Component))).
2. Invoke Component.setPage(org.zkoss.zk.ui.Page) (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/
Component.html#setPage(org.zkoss.zk.ui.Page)) to attach it to a page directly. It is also another way to make a
component become a root component.
Since a component can have at most one parent and be attached at most one page, it will be detached automatically
from the previous parent or page when it is attached to another component or page. For example, b will be a child
of win2 and win1 has no child at the end.
Invalidate a Component
When a component is attached to a page, the component and all of its descendants will be rendered. On the other
hand, when a state of a attached component is changed, only the changed state is sent to client for update (for better
performance). Very rare, you might need to invoke Component.invalidate() (http://www.zkoss.org/javadoc/latest/
zk/org/zkoss/zk/ui/Component.html#invalidate()) to force the component and its descendants to be rerendered[1].
There are only a few reasons to invalidate a component, but it is still worthwhile to note them down:
1. If you add more than 20 child components, you could invalidate the parent to improve the performance. Though
the result Ajax response might be longer, the browser will be more effective to replace a DOM tree rather than
adding DOM elements.
2. If a component has a bug that does not update the DOM tree correctly, you could invalidate its parent to resolve
the problem[2].
[1] ZK Update Engine will queue the update and invalidate commands, and then optimize them before sending them back to the client (for
better performance)
[2] Of course, remember to let us know and we will fix it in the upcoming version.
package foo;
import org.zkoss.zk.ui.*;
import org.zkoss.zul.*;
org.zkoss.zk.ui.AbstractComponent.checkParentChild(AbstractComponent.java:1057)
org.zkoss.zk.ui.AbstractComponent.insertBefore(AbstractComponent.java:1074)
org.zkoss.zul.Window.insertBefore(Window.java:833)
org.zkoss.zk.ui.AbstractComponent.appendChild(AbstractComponent.java:1232)
foo.Foo.doAfterCompose(Foo.java:10)
[1] A composer ( Component (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#)) is a controller that can be
associated with a component for handling the UI in Java. For the information, please refer to the Composer section.
Component Cloning
All components are cloneable (java.lang.Cloneable). It is simple to replicate components by invoking
Component.clone() (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/Component.html#clone()).
main.appendChild(listbox.clone());
Notice
• It is a deep clone. That is, all children and descendants are cloned too.
• The component returned by Component.clone() (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/
Component.html#clone()) does not belong to any pages. It doesn't have a parent either. You have to attach it
manually if necessary.
• ID, if any, is preserved. Thus, you cannot attach the returned component to the same ID space without modifying
ID if there is any.
Similarly, all components are serializable (java.io.Serializable). Like cloning, all children and descendants are
serialized. If you serialize a component and then de-serialize it back, the result will be the same as invoking
Component.clone() (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/Component.html#clone())[1].
[1] Of course, the performance of Window (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#) is much better.
Version History
Version Date Content
ID Space 20
ID Space
ID Space
It is common to decompose a visual presentation into several subsets or ZUML pages. For example, you may use a
page to display a purchase order, and a modal dialog to enter the payment term. If all components are uniquely
identifiable in the same desktop, developers have to maintain the uniqueness of all identifiers for all pages that might
create in the same desktop. This step can be tedious, if not impossible, for a sophisticated application.
The concept of ID space is hence introduced to resolve this issue. An ID space is a subset of components of a
desktop. The uniqueness is guaranteed only in the scope of an ID space. Thus, developers could maintain the subset
of components separately without the need to worry if there is any conflicts with other subsets.
Window (Window [1]) is a typical component that is an ID space. All descendant components of a window
(including the window itself) form an independent ID space. Thus, you could use a window as the topmost
component to group components. This way developers only need to maintain the uniqueness of each subset
separately.
By and large, every component can form an ID space as long as it implements IdSpace [1]. This type of component is
called the space owner of the ID space after the component is formed. Components in the same ID space are called
"fellows".
When a page implements IdSpace [1], it becomes a space owner. In additions, the macro component and the include
component (Include [14]) can also be space owners.
Another example is idspace (Idspace [2]). It derives from div, and is the simplest component implementing
IdSpace [1]. If you don't need any feature of window, you could use idspace instead.
You could make a standard component as a space owner by extending it to implement IdSpace [1]. For example,
Tree of ID Space
If an ID space has a child ID space, the components of the child space are not part of the parent ID space. But the
space owner of the child ID space will be an exception in this case. For example, if an ID space, let's say X, is a
descendant of another ID space, let's say Y, then space X's owner is part of space Y. However, the descendants of X
is not a part of space Y.
For example, see the following ZUML page
<?page id="P"?>
<zk>
<window id="A">
<hbox id="B">
<button id="D" />
</hbox>
<window id="C">
<button id="E" />
</window>
</window>
<hbox id="F">
ID Space 21
As depicted in the figure, there are three spaces: P, A and C. Space P includes P, A, F and G. Space A includes A, B,
C and D. Space C includes C and E.
Components in the same ID spaces are called fellows. For example, A, B, C and D are fellows of the same ID space.
Then, you could associate this composer to a component by specifying the apply attribute as shown below.
<window apply="MyComposer">
<textbox id="input"/>
</window>
Once the ZUML document above is rendered, an instance of MyComposer will be instantiated and the input
member will also be initialized with the fellow named input. This process is called "auto-wiring". For more
information, please refer to the Wire Components section.
Selector
Component.query(java.lang.String) [6] and Component.queryAll(java.lang.String) [7]
are the methods to look for a
component by use of CSS selectors. For example,
Component.query(java.lang.String) [6] returns the first matched component, or null if not found. On the other hand,
Component.queryAll(java.lang.String) [7] returns a list of all matched components.
ID Space 23
Path
ZK provides a utility class called Path [8] to simplify the location of a component among ID spaces. The way of using
it is similar to java.io.File. For example,
Notice that the formal syntax of the path string is "/[/]<space_owner>/[<space_owner>...]/felow" and only the last
element could fellow because it is not space owner. For example,
If a component belongs to another page, we can retrieve it by starting with the page's ID. Notice that double slashes
have to be specified in front of the page's ID.
Notice that the page's ID can be assigned with the use of the page directive as follows.
<?page id="foo"?>
<window/>
UUID
A component has another identifier called UUID (Universal Unique ID). It is assigned automatically when the
component is attached to a page. UUID of a component is unique in the whole desktop (if it is attached).
Application developers rarely need to access it.
In general, UUID is independent of ID. UUID is assigned automatically by ZK, while ID is assigned by the
application. However, if a component implements RawId [9], ID will become UUID if the application assigns one.
Currently, only components from the XHTML component set implements RawId [9].
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ IdSpace. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Idspace. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getSpaceOwner()
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getFellow(java. lang. String)
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#setId(java. lang. String)
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#query(java. lang. String)
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#queryAll(java. lang. String)
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Path. html#
[9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkos/ zk/ ui/ ext/ RawId. html#
ZUML 24
ZUML
There are two ways to compose UI: XML-based approach and pure-Java approach. Here we will describe
XML-based approach. For pure-Java approach, please refer to the next chapter.
The declaration language is called ZK User Interface Markup Language (ZUML). It is based on XML. Each XML
element instructs ZK Loader to create a component. Each XML attribute describes what value to be assigned to the
created component. Each XML processing instruction describes how to process the whole page, such as the page
title. For example,
where the first line specifies the page title, the second line creates a root component with title and border, and the
third line creates a button with label and an event listener.
Auto-completion with Schema
[1]
When working with a ZUML document, it is suggested to use ZK Studio since it provides a lot of features to
simplify editing, such as content assist and visual editor'.
If you prefer not to use ZK Studio, you could specify the XML schema in a ZUML document as shown below. Many
XML editors works better, such as when with auto-complete, if XML schema is specified correctly.
<window xmlns="http://www.zkoss.org/2005/zul"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.zkoss.org/2005/zul
http://www.zkoss.org/2005/zul/zul.xsd">
The ZUL schema can be downloaded from http:/ / www. zkoss. org/ 2005/ zul/ zul. xsd [2]. In addition, you can find
zul.xsd under the dist/xsd directory in the ZK binary distribution.
This section is about the general use of ZUML. For a complete reference, please refer to ZUML Reference.
References
[1] http:/ / www. zkoss. org/ product/ zkstudio. dsp
[2] http:/ / www. zkoss. org/ 2005/ zul/ zul. xsd
XML Background 25
XML Background
Overview
This section provides the most basic concepts of XML to work with ZK. If you are familiar with XML, you could
skip this section. If you want to learn more, there are a lot of resources on Internet, such as http:/ / www. w3schools.
com/xml/xml_whatis.asp [1] and http://www.xml.com/pub/a/98/10/guide0.html [2].
XML is a markup language much like HTML but with stricter and cleaner syntax. It has several characteristics
worthwhile to take notes of.
Document
The whole XML content, no matter whether if it is in a file or as a string, is called a XML document.
Character Encoding
It is, though optional, a good idea to specify the encoding in your XML so that the XML parser can interpret it
correctly. Note: it must be on the first line of the XML document.
In addition to specifying the correct encoding, you have to make sure your XML editor supports it as well.
Elements
An XML element is everything from (including) the element's start tag to (including) the element's end tag.
An element can contain other elements, let it be simple text or a mixture of both. Elements can also have attributes.
For example,
<window title="abc">
<button label="click me"/>
</window>
where both window and button are elements, while title is an attribute of the window element. The button element is
nested in the window element. We call the window component the parent element of button, while the button
component is a child element of the window.
The document root is the topmost element (without any parent element). There is exactly one document root per
XML document.
Description Code
Result Code
Correct: <window>
<groupbox>
Hello World!
</groupbox>
</window>
Wrong: <window>
<groupbox>
Hello World!
</window>
</groupbox>
XML treats every tag as a node in a tree. A node without a parent node is a root component, and it is the root of a
tree. In each zul file, only ONE tree is allowed.
For example, for being a whole zul file, the following is allowed, for it must have only one root component.
<button/>
And for being a whole zul file, the following is not allowed, for it must have more than one root component.
<button/>
<button/>
You can solve the problem simply by adding a tag to enclose the whole zul file to serve as the parent node, so that
the zul file has one single tree again.
<window>
<button />
<button />
</window>
< <
> >
& &
" "
' '
Alternatively, you could tell XML parser not to interpret a piece of text by using CDATA. See the following:
<zscript>
<![CDATA[
void myfunc(int a, int b) {
if (a < 0 && b > 0) {
//do something
}
]]>
</zscript>
It is suggested to always add <![CDATA[ ]]> inside your <zscript> </zscript>. Thus you don't have to
worry about the escape sequences for special characters like "&", "<". In addition, the code can also become much
easier to read and for maintenance.
Result Code
Correct:
width="100%"
checked="true"
Wrong:
width=100%
checked
Both the single quote (') and the double quote (") can be used, so if the value has double quotes, you could use the
single quote to enclose it. For example,
Comments
A comment is used to leave a note or to temporarily disable a block of XML code. To add a comment in XML, use
<!-- and --> to mark the comment body.
<window>
<!-- this is a comment and ignored by ZK -->
</window>
Processing Instruction
A processing instruction is used to carry out the instruction to the program that processes the XML document. A
processing instruction is enclosed with <? and ?>. For example,
<?page title="Foo"?>
Processing instructions may occur anywhere in an XML document. However, most ZUML processing instructions
must be specified at the topmost level (the same level of the document root).
References
[1] http:/ / www. w3schools. com/ xml/ xml_whatis. asp
[2] http:/ / www. xml. com/ pub/ a/ 98/ 10/ guide0. html
Basic Rules
If you are not familiar with XML, please take a look at XML Background first.
<window>
<textbox/>
<button/>
</window>
In addition, the parent-child relationship of the created components will follow the same hierarchical structure of the
XML document. In the previous example, window will be the parent of textbox and button, while textbox is the first
child and button is the second.
Basic Rules 29
<zk>...</zk>
The zk element is a special element used to aggregate other components. Unlike a real component (say, hbox or
div), it is not part of the component tree being created. In other words, it does not represent any components. For
example,
<window>
<zk if="${whatever}">
<textbox/>
<textbox/>
</zk>
</window>
is equivalent to
<window>
<textbox if="${whatever}"/>
<textbox if="${whatever}"/>
</window>
For more information about special elements, please refer to ZUML Reference.
<window title="Hello"/>
Like JSP, you could use EL for the value of any attributes. The following example assigns the value of the request
parameter called name to window's title.
<window title="${param.name}"/>
<button onClick="do_something_in_Java())"/>
The attribute value must be a valid Java code, and it will be interpreted[2] when the event is received. You could
specify different languages by prefixing the language name. For example, we could write the event listener in
Groovy as follows.
Basic Rules 30
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#setTitle(java. lang. String)
[2] ZK uses BeanShell (http:/ / www. beanshell. org) to interpret it at run time
Special Attributes
There are a few special attributes dedicated to special functionality rather than assigning properties or handling
events. For example, the forEach attribute is used to specify a collection of object such that the XML element it
belongs will be evaluated repeatedly for each object of the collection.
<listbox>
<listitem forEach="${customers}" label="${each.name}"/>
</listbox>
For more information about special attributes, please refer to the Iterative Evaluation section and the ZUML
Reference
<window>
Begin ${foo.whatever}
</window>
is equivalent to
<window>
<label value="Begin ${foo.whatever}"/>
</window>
<html>Begin ${foo.whatever}</html>
is equivalent to
This is designed to make it easy to specify multiple-line value. This is usually used by a particular component that
requires a multiple-lines value. For a complete list of components that interprets the XML text as a property's value,
please refer to the ZUML Reference.
Basic Rules 31
Notice that there should be no whitespace between the question mark and the processing instruction's name (i.e.,
page in the above example).
The other directives include the declaration of components, the class for initializing a page, the variable resolver for
EL expressions, and so on. For more information about directives, please refer to ZUML Reference.
Version History
Version Date Content
EL Expressions
Overview
EL expressions are designed to make a ZUML document easier to access objects available in the application, such as
the application data and parameters.
An EL expressions is an expression enclosed with ${ and }, i.e., the syntax ${expr}. For example,
<element attr1="${bean.property}".../>
${map[entry]}
<another-element>${3+counter} is ${empty map}</another-element>
When an EL expression is used as an attribute value, it could return any kind of objects as long as the attribute
allows. For example, the following expressions will be evaluated to boolean and int respectively.
If the class does not match, ZK Loader will try to coerce it to the correct one. If a failure has occurred, an exception
is thrown.
Multiple EL expressions could be specified in a single attribute:
Example
EL Expressions 32
EL Expression Result
${10 mod 4} 2
${empty param.add} true if the request parameter named add is null or an empty string
<textbox id="tb" value="${self.parent.title}"/> <!-- self is an implicit object referring to itself -->
</window>
Furthermore, you could define a variable resolver to associate a name with an object, or map a function to a Java
static method as described in the following.
EL Expressions 33
Variable Resolver
If you would like to support many variables, you could implement a variable resolver: a class that implements
VariableResolver [2].
package foo;
public class CustomerResolver implements org.zkoss.xel.VariableResolver
{
public Object resolveVariable(String name) {
if ("customers".equals(name))
return Customer.getAll("*");
// if ("recent".equals(name))
// return something_else;
return null; //not a recognized variable
}
}
<?variable-resolver class="foo.CustomerResolve"?>
<listbox>
<listitem label="${each.name}" forEach="${customers}"/>
</listbox>
<listener>
<listener-class>foo.MyVariableResolver</listener-class>
</listener>
Then, when a page is created each time, an instance of the specified class will be instantiated and registered as if it is
specified in the variable-resolver element.
Notice that since a new instance of the variable resolver is created on each page, there will not be any concurrency
issues.
package foo;
public class Customer {
public static Collection<Customer> getAll(String condition) {
//...returns a collection of customers
}
public String getName() {
EL Expressions 34
return _name;
}
//...
}
<taglib>
<function>
<name>getAllCustomers</name>
<function-class>foo.Customer</function-class>
<function-signature>
java.util.Collection getAll(java.lang.String)
</function-signature>
<description>
Returns a collection of customers.
</description>
</function>
<!-- any number of functions are allowed -->
</taglib>
Version History
Version Date Content
References
[1] http:/ / download. oracle. com/ javaee/ 1. 4/ tutorial/ doc/ JSPIntro7. html
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ xel/ VariableResolver. html#
Scripts in ZUML 35
Scripts in ZUML
Embed Server-side Script Code
To make it easier to create a dynamic web page, the ZUML document allows you to embed the script code. Notice
that there are two types of script code: server-side and client-side. How the client-side code can be embedded is
discussed at the Client-side UI Composing and Client-side Event Listening sections. Here we will discuss how to
embed the server-side script code in a ZUML document.
Depending on the requirement, there are two ways to embed the server-side script code in a ZUML document: the
zscript element and the event handler. The zscript element is used to embed the code that will execute when
the page is loaded, while the event handler will execute when the event is received.
Notice that the performance of BeanShell is not good and, like any interpreter, typos can be found only when it is
evaluated[1]. However, embedding Java code in a ZUML page is a powerful tool for fast prototyping. For example,
business analysis could discuss the real UI with UI designers, modify it directly and get back the feeling
immediately without going through drawings and even recompiling.
[1] For more information, please refer to the Performance Tips section
zscript
First, you could embed the code inside the zscript element, such that they will be evaluated when the page is
rendered[1]. For example,
<zscript>
//inside is zscript
//you can declare variable, function, and even Java class here.
void foo(String msg) {
//...
}
comp.addEventListener("onClick",
new EventListener() {
public void onEvent(Event event) {
//...
}
});
</zscript>
Notice that, by default, the code inside the zscript element is Java but you could also use other languages, such
as Groovy. Keep in mind that it is interpreted at run time (by Beanshell (http:/ / beanshell. org)), so typo or syntax
error will be found only when it is interpreted. In addition, it runs at the server, so it could access any Java libraries.
You could even define variables, methods, classes with it, and they are visible to EL expressions of the same page.
Scripts in ZUML 36
CDATA
The code embedded in the zscript element must be a valid XML text. In other words, you must encode the special
characters well, such as < must be replaced with <, & with & and so on. In additions to encode individual
characters, you could also encode the whole code with XML CDATA as follows.
<script><![CDATA[
if (some < another && another < last) //OK since CDATA is used
doSomething();
]]></script>
[1] The zscript element has an attribute called deferred that could make the evaluation as late as possible
Class Declaration
You could define a class declared in a ZUML document, and the class is accessible only in the page it was defined.
For example,
<treecol label="Names"/>
</treecols>
</tree>
</zk>
Event Handlers
Second, you could put the code inside an event handler, such that it will execute when the event is received, as
depicted below.
Notice that the the name of the event must starts with on, and the third letter must be a upper case. Otherwise, it will
be considered as a property.
Again, the code is Java interpreted at run time and running on the server. For client-side listening, please refer to the
Client-side Event Listening section.
For the sake of discussion, we call it zscript no matter the code is embedded in the zscript element or in an event
handler.
Attribute
If the code is too complicate, you could specify the event handle in the attribute element. For example,
<button label="hi">
<attribute name="onClick"><![DATA[
if (anything > best)
best = anything;
]]></attribute>
</button>
<window>
<button label="ok" id="${self.label}"/>
${ok.label}
</window>
On the other hand, in the following example, alert(self.label) is not an EL expression. Rather, it's the
zscript code:
<window>
<button label="ok" onClick='alert(self.label)'/>
</window>
<window>
<!-- It's wrong, for java don't accept syntax as ${}-->
<button label="ok" onClick='alert(${self.label})'/>
Scripts in ZUML 38
</window>
Also notice that the evaluation of EL expressions is very fast, so EL can be used in a production system. On the other
hand, zscript is suggested to use only in prototyping or quick-fix.
<script>
Date now = new Date();
</script>
${now}
Java Interpreter
The default interpreter is based on BeanShell (http://www.beanshell.org). It is a Java Interpreter.
<window id="A">
<zscript>var1 = "abc";</zscript>
<window id="B">
<zscript>var2 = "def";</zscript>
</window>
</window>
<zscript>
void echo() {
String a_local_variable;
}
</script>
<window>
<zscript>
{
Scripts in ZUML 39
The result shows: 0:first 1:def 2: . It is because now is the local variable and it is invisible to EL
expressions. On the other hand, first and abc are both global variables that are visible to EL expressions.
Notice that abc is not declared but assigned directly, and it causes a global variable to be created.
Please refer to the Beanshell Documentation (http:/ / beanshell. org/ docs. html) and search "scoping" and "local" for
more information.
<?page zscriptLanguage="Groovy"?>
<window border="normal">
<vbox id="vb">
<label id="l" value="Hi"/>
<button label="change label" onClick="l.value='Hi, Groovy';"/>
<button label="add label" onClick="new Label('New').setParent(vb);"/>
</vbox>
<button label="alert" onClick="alert('Hi, Groovy')"/>
</window>
In addition, you could add your own interpreter by implementing Interpreter (http:/ / www. zkoss. org/ javadoc/
latest/zk/org/zkoss/zk/scripting/Interpreter.html#). For more information, please refer to ZUML Reference.
Version History
Version Date Content
Conditional Evaluation 40
Conditional Evaluation
If and Unless
The evaluation of an element could be conditional. By specifying the if, unless attribute or both, developers
could control whether to evaluate the associated element. It is also the most straightforward way.
For example, suppose that we want to use label, if readonly, and textbox, otherwise:
Here is another example: window is created only if a is 1 and b is not 2. If an element is ignored, all of its child
elements are ignored too.
<zk switch="${fruit}">
<zk case="apple">
Evaluated only if ${fruit} is apple
</zk>
<zk case="${special}">
Evaluated only if ${fruit} equals ${special}
</zk>
<zk>
Evaluated only if none of the above cases matches.
</zk>
</zk>
ZK Loader will evaluate from the first case to the last case, until it matches the switch condition, which is the value
specified in the switch attribute. The evaluation is mutually exclusive conditional. Only the first matched case is
evaluated.
The zk element without any case is the default – i.e., it always matches and is evaluated if all the cases above it
failed to match.
Conditional Evaluation 41
Multiple Cases
You can specify a list of cases in one case attribute, such that a section of a ZUML document has to be evaluated if
one of them matches.
<zk switch="${fruit}">
<zk case="apple, ${special}">
Evaluated if ${fruit} is either apple or ${special}
</zk>
</zk>
Regular Expressions
Regular expressions are allowed in the case attribute too, as shown below.
<zk switch="${fruit}">
<zk case="/ap*.e/">
Evaluate if the regular expression, ap*.e"., matches the switch
condition.
</zk>
</zk>
is equivalent to
<zk choose="">
<zk when="${fruit == 'apple'}">
Evaluated if the when condition is true.
</zk>
<zk><!-- default -->
Evaluated if none of above cases matches.
</zk>
</zk>
You don't have to assign any value to the choose attribute, which is used only to identify the range of the mutually
exclusive conditional evaluation.
Conditional Evaluation 42
Version History
Version Date Content
Iterative Evaluation
forEach
By default, ZK instantiates a component for each XML element. If you would like to generate a collection of
components, you could specify the forEach attribute. For example,
<listbox>
<listitem label="${each}" forEach="Apple, Orange, Strawberry"/>
</listbox>
is equivalent to
<listbox>
<listitem label="Apple"/>
<listitem label="Orange"/>
<listitem label="Strawberry"/>
</listbox>
When ZK Loader iterates through items of the given collection, it will update two implicit objects: each and
forEachStatus. The each object represents the item being iterated, while forEachStatus is an instance of
ForEachStatus [1], from which you could retrieve the index and the previous forEach, if any (nested iterations).
If you have a variable holding a collection of objects, you can specify it directly in the forEach attribute. For
example, assume that you have a variable called grades as follows.
Then, you can iterate them by the use of the forEach attribute as follows. Notice that you have to use EL expression
to specify the collection.
<listbox>
<listitem label="${each}" forEach="${grades}"/>
</listbox>
The iteration depends on the type of the value of the forEach attribute:
• If it is java.util.Collection iterates each element of the collection.
• if it is java.util.Map, it iterates each Map.Entry of the map.
• If it is java.util.Iterator, it iterates each element from the iterator.
• If it is java.util.Enumeration, it iterates each element from the enumeration.
• If it is Object[], int[], short[], byte[], char[], float[] or double[] is specified, it iterates each element from the array.
• If it is null, nothing is generated (it is ignored).
• If neither of the above types is specified, the associated element will be evaluated once as if a collection with a
single item is specified.
Iterative Evaluation 43
<hlayout>
<zscript>
classes = new String[] {"College", "Graduate"};
grades = new Object[] {
new String[] {"Best", "Better"}, new String[] {"A++", "A+", "A"}
};
</zscript>
<listbox width="200px" forEach="${classes}">
<listhead>
<listheader label="${each}"/>
</listhead>
<listitem label="${forEachStatus.previous.each}: ${each}"
forEach="${grades[forEachStatus.index]}"/>
</listbox>
</hlayout>
Notice that the each and forEachStatus objects can be accessible both in an EL expression and in zscript.
<zk forEach="${cond}">
${each.name}
<textbox value="${each.value}"/>
<button label="Submit"/>
</zk>
The zk element is a special element used to group a set of XML element nested. ZK Loader will not create a
component for it. Rather, it interprets the forEach, if and unless attribute it might have.
Iterative Evaluation 44
<window>
<button label="${each}" forEach="apple, orange">
<zscript>
self.parent.appendChild(new Label("" + each));
</zscript>
</button>
</window>
In a composer, you could retrieve them from the attributes, because these objects are actually stored in the parent
component's attributes (Component.getAttribute(java.lang.String) [3]). For example,
If the component is a root, you could retrieve them from the page's attributes (Page.getAttribute(java.lang.String)
[4]
).
When the onClick event is received, the each object no longer exists.
There is a simple solution: store the value in the component's attribute, so you can retrieve it when the event listener
is called. For example,
<grid>
<rows>
<row forEach="${foos}" forEachBegin="${param.begin}" forEachEnd="${param.end}">
${each.name} ${each.title}
</row>
</rows>
</grid>
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ForEachStatus. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ ui/ util/ ForEachStatus. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getAttribute(java. lang. String)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Page. html#getAttribute(java. lang. String)
On-demand Evaluation
By default, ZK creates components based on what are defined in a ZUML document when loading the document.
However, we can defer the creation of some sections of components, until necessary, such as becoming visible. This
technique is called load-on-demand or render-on-demand.
For example, you could split a ZUML document into multiple pages, and then load the required ones when
necessary. Please refer to the Load ZUML in Java section for how to load a ZUML document dynamically.
It improves the performance both at the server and client sides. It is suggested to apply this technique whenever
appropriate. In addition, ZK Loader provides a standard on-demand evaluation called fulfill to simplify the
implementation as described in the following section.
<combobox fulfill="onOpen">
<comboitem label="First Option"/>
</combobox>
In other words, if a XML element is specified with the fulfill attribute, all of its child elements will not be processed
until the event specified as the value of the fulfill attribute is received.
On-demand Evaluation 46
<tabpanel fulfill="self.linkedTab.onSelect">
</tabpanel>
<div fulfill="${foo}.onClick">
...
</div>
<zk>
<button id="btn" label="Click to Load"/>
<div fulfill="btn.onClick=another.zul"/>
</zk>
If you specify an URI without any conditions, the ZUML document of the URI will be loaded from the very
beginning. In other words, it has the same effect of using include.
<div fulfill="=another.zul"/>
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ FulfillEvent. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Components. html#
Include a Page 48
Include a Page
apply (recommended)
[1]
If you are using ZK 8, we suggest you using <apply> instead of Include. Comparing to Include, it has
several advantages:
1. Doesn't consume extra memory.
Because it's a shadow element, it doesn't create a corresponding component at the server side.
2. Doesn't render an extra surrounding its child components at the client-side.
Include renders a to enclose its child components. Sometimes the outer breaks the layout.
3. Doesn't create an ID space.
Include itself is an ID space owner.
4. It can render its child components upon parameters dynamically.
You can pass a parameter with data binding and bind to the parameter. When the parameter's value changes,
the content will changed accordingly.
Include
Include allows you to include a ZUML page, a static page, a JSP page or the result of a servlet. For example,
<include src="another.zul"/>
<include src="another.jsp"/>
[14]
When including a non-ZUML page (such as JSP), the output of the page will be the content of the Include
component. Thus, the output must be a valid HTML fragment.
When including a ZUML page, the components specified in the ZUML page will become the child components of
the Include [14] component.
For example, suppose we have two ZUL pages as follows:
and
component directive:
<another/>
image src:
<image src="~./zklogo.png" />
</vlayout>
</zk>
It can be used as a default shared folder path for a jar. When you create a sub-module project, you can put some
shared resources or reusable template zul files under this folder. Then, package the sub-module as a jar and include
the sub-module jar in the main project. The main project can easily access those reusable resources by this special
URL. Notice that the zul files under this special resource path are public accessed with a URL in a browser, you
should not put any sensitive data in it.
If you prefer to an application-wide named <apply> [3] element with a predefined templateURI and default
parameters, you could specify it in a language addon. For example, we could prepare a file called
WEB-INF/lang-addon.xml with the following content:
<language-addon>
<addon-name>myapp</addon-name>
<language-name>xul/html</language-name>
<component>
<component-name>mytemplatecomp</component-name>
<template-uri>~./template/mytemplate.zul<template-uri>
</component>
</language-addon>
Then, we could specify this file by adding the following content to WEB-INF/zk.xml:
<language-config>
<addon-uri>/WEB-INF/lang-addon.xml</addon-uri>
Include a Page 50
</language-config>
Version History
Version Date Content
References
[1] http:/ / books. zkoss. org/ zk-mvvm-book/ 8. 0/ shadow_elements/ shadow_elements. html
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Include. html#setMode(java. lang. String)
[3] http:/ / books. zkoss. org/ zk-mvvm-book/ 8. 0/ syntax/ apply. html
where parent (an instance of Component [1]) will become the parent of the components specified in the ZUML
document. If parent is null, the components specified in the ZUML documents will become the root components
of the current page. In other words, the components created by org.zkoss.zk.ui.Component, java.util.Map)
Execution.createComponents(java.lang.String, org.zkoss.zk.ui.Component, java.util.Map) [2] will be attached to the
current page.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html#createComponents(java. lang. String,
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html#createComponentsDirectly(java. lang. String,
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#createComponents(org. zkoss. zk. ui. WebApp,
[6] It means Execution (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html#) returns null. For example, it
happens when the application starts, or in a working thread.
lang. String,). Or, you could represent the content as a reader (say, representing BLOB in database) and then pass it
to java.lang.String, org.zkoss.zk.ui.Component, java.util.Map) Execution.createComponentsDirectly(java.io.Reader,
java.lang.String, org.zkoss.zk.ui.Component, java.util.Map) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zk/ui/Execution.html#createComponentsDirectly(java.io.Reader,)
For example, suppose we want to create a component from a remote site. Then, we could represent the resource as a
URL and do as follows.
Notices
There are a few notices worth to know.
No Page Created
When creating components from a ZUML document as described above, no page ( Page (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Page. html#)) is created. Components are attached to the current page, to a
component, or simply standalone. Since no page is created, there are a few differences than visiting a ZUML
document directly[1].
1. The <?page?>, <?script?>, <?link?>, <?header?> and other directives controlling a page ( Page (http://www.
zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/Page.html#)) have no function. It means that you could not
change the page's title, add JavaScript code, or add CSS with these directive in a ZUML document loaded in this
way.
2. On the other hands, when <?function-mapper?>, <?variable-resolver?> and <?component?> work correctly, they
decide how a ZUML document is parsed rather than how the current page ( Page (http://www.zkoss.org/
javadoc/latest/zk/org/zkoss/zk/ui/Page.html#)) should be.
3. The variables, functions and classes defined in zscript will be stored in the interpreter of the current page (
Page.getInterpreter(java.lang.String) (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/Page.
html#getInterpreter(java.lang.String))).
• If java.util.Map) Execution.createComponents(java.lang.String, java.util.Map) (http://www.zkoss.org/
javadoc/latest/zk/org/zkoss/zk/ui/Execution.html#createComponents(java.lang.String,), java.lang.String,
java.util.Map) Executions.createComponents(org.zkoss.zk.ui.WebApp, java.lang.String, java.util.Map) (http://
www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/Executions.html#createComponents(org.zkoss.zk.ui.
WebApp,) or similar is used to create components not attached to any page, the variables, functions and classes
defined in the ZUML document will be lost. Thus, it is a not a good idea to use zscript in this case.
Version History
Version Date Content
XML Namespaces 54
XML Namespaces
In a ZUML document, a XML namespace is used to identify either a special functionality or a component set. We
call the former as a standard namespace, while the later as a language.
Standard Namespaces
For example, the client namespace is used to identify that a XML attribute shall be interpreted as a client-side
control.
In the following example, w:onFocus is a client-side listener since w: is specified, while onChange is
The native namespace is another standard namespace used to indicate that a XML element should be generated
natively rather than a component. For example,
<n:table xmlns:n="native">
<n:tr>
<n:td>Username</n:td>
<n:td><textbox/></n:td>
</n:tr>
<n:tr>
<n:td>Password</n:td>
<n:td><textbox type="password"/></n:td>
</n:tr>
</n:table>
where n:table, n:tr and n:td are native, i.e., they are generated directly to the client without creating a
component for each of them.
For more information, please refer to ZUML Reference.
Languages
A language (LanguageDefinition [1]) is a collection of component definitions. It is also known as a component set.
For example, Window [1], Button [2] and Combobox [2] all belong to the same language called xul/html. It is a ZK
variant of XUL (and also known as zul).
Component designers are free to designate a component definition to any component sets they prefer, as long as
there is no name conflict.
When parsing a ZUML document, ZK Loader have to decide the language that a XML element is associated, so that
the correct component definition (ComponentDefinition [3]) can be resolved. For example, in the following example,
ZK needs to know window belongs to the xul/html language, so its component definition can be retrieved
correctly.
<window>
ZK Loader first decides the default language from the extension. For example, foo.zul implies the default
language is ZUL. The default language is used if an XML element is not specified with any XML namespace. For
example, window in the previous example will be considered as a component definition of the ZUL langauge.
XML Namespaces 55
If the extension is zhtml (such as foo.zhtml), the default language will be XHTML. Thus, window in the
previous example will be interpreted incorrectly. To solve it, you could specify the XML namespace explicitly as
follows.
For more information about identifying a language, pelase refer to ZUML Reference.
Version History
Version Date Content
5.0.4 August, 2010 The shortcut was introduced to make it easy to specify a standard namespace, such as native, client and zk.
5.0.5 October, 2010 The shortcut was introduced to make it easy to specify a component set, such as zul and zhtml.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ metainfo/ LanguageDefinition. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Combobox. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ metainfo/ ComponentDefinition. html#
Richlet
Overview
A richlet is a small Java program that composes a user interface in Java for serving the user's request. Before
composing UI in Java, we suggest you to know basic concept: UI Composing/Component-based UI first.
When a user requests the content of an URL, ZK Loader checks if the resource of the specified URL is a ZUML
page or a richlet. If it is a ZUML page, ZK Loader will create components automatically based on the ZUML page's
content as we described in the previous chapters.
If the resource is a richlet, ZK Loader hands over the processing to the richlet. What and how to create components
are all handled by the richlet. In other words, it is the developer's job to create all the necessary components
programmatically in response to the request.
The choice between the ZUML pages and richlets depends on your preference. However, the performance should not
cause any concern since parsing ZUML is optimized.
Richlet 56
Implement a Richlet
[1]
It is straightforward to implement a richlet. First, you have to implement the Richlet interface before mapping a
URL to the richlet.
package org.zkoss.reference.developer.uicomposing;
import org.zkoss.zk.ui.*;
import org.zkoss.zk.ui.event.*;
import org.zkoss.zul.*;
w.setPage(page);
}
@Override
public void init(RichletConfig config) {
super.init(config);
//initialize resources
}
@Override
public void destroy() {
Richlet 57
super.destroy();
//destroy resources
}
}
In Richlet, you have to compose UI by your own, but some components only support specific child components. We
recommend you to read ZK Component Reference before you start to build.
[3]
As shown above (line 27), we have to invoke page) Component.setPage(Page page) explicitly to attach a root
component to a page so it will be available at the client.
To have better control, you can even implement the Richlet.init(org.zkoss.zk.ui.RichletConfig) [4] and
Richlet.destroy() [5] methods to initialize and to destroy any resources required by the richlet when it is loaded.
In addition, you could implement Richlet.getLanguageDefinition() [6] to use a different language as default (for
example, implementing a richlet for mobile devices [7]). By default, ZUL (aka., xul/html) is assumed.
Why? Each deskop should have its own set of component instances[9]. When the URL associated MyRichlet is
requested a second time, an exception will be thrown because the main window is already instantiated and
associated with the first desktop created from the first request. We cannot assign it to the second desktop.
Richlet 58
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Richlet. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ GenericRichlet. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#setPage(Page
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Richlet. html#init(org. zkoss. zk. ui. RichletConfig)
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Richlet. html#destroy()
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Richlet. html#getLanguageDefinition()
[7] http:/ / code. google. com/ p/ zkreach/
[8] A normal HTTP request; not an Ajax request. Ajax requests are handled in the same way as ZUML. For more information please refer to
the Event Handling section
[9] For more information, please refer to Component-based UI section
Turn on Richlet
By default, richlets are disabled. To enable them, please add the following declaration to WEB-INF/web.xml.
Once enabled, you can add as many richlets as you want without modifying web.xml.
With servlet-mapping:
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>/zk/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>RichletFilter</filter-name>
<filter-class>org.zkoss.zk.ui.http.RichletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RichletFilter</filter-name>
<url-pattern>/zk/*</url-pattern>
</filter-mapping>
where you can replace /zk/* to any pattern you like, such as /do/*. Notice that you cannot map it to an
extension (such as *.do) since it will be considered as a ZUML page (rather than a richlet).
Richlet 59
After defining a richlet, you can map it to any number of URLs using the richlet-mapping element as shown
below.
<richlet-mapping>
<richlet-name>Test</richlet-name>
<url-pattern>/test</url-pattern>
</richlet-mapping>
<richlet-mapping>
<richlet-name>Test</richlet-name>
<url-pattern>/some/more/*</url-pattern>
</richlet-mapping>
Note: With Richlet Filter (since ZK 7.0.0), you should add the prefix of url-pattern of the filter-mapping into the
url-pattern of richlet-mapping. For example,
<richlet-mapping>
<richlet-name>Test</richlet-name>
<url-pattern>/test</url-pattern>
</richlet-mapping>
<richlet-mapping>
<richlet-name>Test</richlet-name>
<url-pattern>/zk/some/more/*</url-pattern>
</richlet-mapping>
As you can see the highlight above, the /zk is added which is according to the filter-mapping.
Then, you can visit http:/ / localhost:8080/ PROJECT_NAME/ zk/ test (http:/ / localhost:8080/ PROJECT_NAME/
zk/test) to request the richlet.
The URL specified in the url-pattern element must start with /. If the URI ends with /*, it is matched to all
request with the same prefix. To retrieve the request's actual URL, you can check the value returned by the
getRequestPath method of the current page.
Tip: By specifying /* as the url-pattern, you can map all unmatched URLs to your richlet.
Version History
Version Date Content
Macro Component 61
Macro Component
There are two ways to implement a component. One is to implement a component in a Java class, extending from
other component or one of the skeletal implementations with an optional JavaScript class. It is flexible and,
technically, is also able to implement any functionality you want. For more information please refer to ZK
Component Development Essentials.
On the other hand, we could implement a new component by using the others and composing them in a ZUML page.
In other words, we could define a new component by expressing it in a ZUML page. It works like composition,
macro expansion, or inline replacement.
For the sake of convenience, we call the first type of components as primitive components, while the second type as
macro components. In this section we will get into more details on how to implement a macro component and how to
use it.
There is a similar concept called composite components. Unlike macros, you could derive from any component but
you have to do the loading of ZUML manually. For more information please refer to the Composite Component
section.
<hlayout>
Username: <textbox/>
</hlayout>
It is done.
As shown, we have to declare the component's name (the name attribute) and the URI of the page defining the
macro component (the macroURI attribute).
If you prefer to make a macro component available to all pages, you could add the component definition to the
so-called language addon and add it to WEB-INF/zk.xml.
Macro Component 62
<window>
<username/>
</window>
All these properties specified are stored in a map that is then passed to the template (aka., the macro definition;
macroURI) via a variable called arg. Then, from the template, you could access these properties by the use of EL
expressions as shown below:
<hlayout>
${arg.label}: <textbox value="${arg.who}"/>
</hlayout>
arg.includer
In addition to properties (aka., attributes), a property called arg.includer is always passed. It refers the macro
component itself. With this, we could reference it to other information such as parent:
${arg.includer.parent}
Notice that arg.includer is different from the so-called inline macros. The inline macros are special macro
components and used for inline expansion. For more information please refer to Inline Macros section.
Therefore,
<mycomp/>
is equivalent to
HtmlMacroComponent ua = (HtmlMacroComponent)
page.getComponentDefinition("username", false).newInstance(page,
null);
ua.setParent(wnd);
ua.applyProperties(); //apply properties defined in the component
definition
ua.setDynamicProperty("who", "Joe");
ua.afterCompose(); //then the ZUML page is loaded and child components
are created
[7]
It is a bit tedious. If you implement you own custom Java class (instead of HtmlMacroComponent ), it will be
simpler. For example,
Please refer to the Implement Custom Java Class section for details.
<username id="ua"/>
<button onClick="ua.setMacroURI("another.zul")"/>
If the macro component was instantiated, all of its children will be removed first, and then the new template will be
appled (so-called recreation).
Macro Component 64
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Page. html#getComponentDefinition(java. lang. String,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ metainfo/ ComponentDefinition. html#newInstance(org. zkoss. zk. ui. Page,
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#setParent(org. zkoss. zk. ui. Component)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#applyProperties()
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ ext/ DynamicPropertied. html#setDynamicProperty(java. lang. String,
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ ext/ AfterCompose. html#afterCompose()
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlMacroComponent. html#
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlMacroComponent. html#setMacroURI(java. lang. String)
Inline Macros
Overview
An inline macro is a special macro component which behaves like inline-expansion. Unlike a regular macro
component, ZK does not create a macro component. Rather, it inline-expands the components defined in the macro
URI, as if the content of the in-line macro's template is entered directly into the target page.
<grid>
<rows>
<row>
Inline Macros 65
Username
<textbox id="ua" value="John"/>
</row>
</rows>
</grid>
Notice that all the properties, including id, are passed to the inline macro too.
[1] ZK Loader does create an component for an inline macro when rendering, and then drop it after expanding into the parent component.
Technically, an application can do the same thing but it is not recommended since we might change it in the future.
arg.includer
Unlike regular macros, arg.includer for a inline macro is the parent component of the macro (after all, the
inline macro does not really exist).
An Example
inline.zul: (the macro definition)
<row>
<textbox value="${arg.col1}"/>
<textbox value="${arg.col2}"/>
</row>
]]>
</zscript>
<grid>
<rows>
<myrow col1="${each.x}" col2="${each.y}" forEach="${infos}"/>
</rows>
</grid>
</window>
Version History
Version Date Content
<hlayout id="mc_layout">
Username: <textbox id="mc_who"/>
</hlayout>
package foo;
import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zul.Textbox;
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
public class Username extends HtmlMacroComponent {
@WireVariable
private User currentUser; //will be wired if currentUser is a
Spring-managed bean, when compose() is called
@Wire
Implement Custom Java Class 67
As shown, HtmlMacroComponent.compose() [1] will wire variables, components and event listener automatically, so
we could access them directly (such as the mc_who member). For more information, please refer to the the Wire
Components section, the Wire Variables section and Wire Event Listeners sections.
Also notice that the arg variable is still available to the template so as to represent properties set by
java.lang.Object) DynamicPropertied.setDynamicProperty(java.lang.String, java.lang.Object) [5], though it is really
useful if a custom implementation is provided.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlMacroComponent. html#compose()
[2] By default, HtmlMacroComponent (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlMacroComponent. html#) is
invoked when is called. In many cases, it is generally too late, so we suggest to invoke it in the contructor.
[3] HtmlMacroComponent.compose() (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlMacroComponent.
html#compose()) is available in 5.0.5. For 5.0.4 or earlier, please invoke UNIQ-javadoc-3-f6421cf86b2cc1a3-QINU instead.
In ZUML
The use of the macro component with a custom Java class in a ZUML page is the same as other macro components.
In Java
The main purpose of introducing a custom Java class is to simplify the use of a macro component in Java. For
example, you could invoke a more meaningful setter, say, setWho, directly rather than java.lang.Object)
DynamicPropertied.setDynamicProperty(java.lang.String, java.lang.Object) (http:/ / www. zkoss. org/ javadoc/
latest/ zk/ org/ zkoss/ zk/ ui/ ext/ DynamicPropertied. html#setDynamicProperty(java. lang. String,). In addition, the
instantiation could be as simple as follows:
Implement Custom Java Class 68
<hlayout>
Username: <textbox id="who" value="${arg.who}"/>
</hlayout>
Why? Like any ID space owner, the macro component itself is in the same ID space with its child components.
There are two alternative solutions:
1. Use a special prefix for the identifiers of child components of a macro component. For example, "mc_who"
instead of "who".
<hlayout>
Username: <textbox id="mc_who" value="${arg.who}"/>
</hlayout>
<window>
<hlayout>
Username: <textbox id="who" value="${arg.who}"/>
</hlayout>
</window>
package foo;
import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zul.Textbox;
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
public class Username extends HtmlMacroComponent {
@WireVariable
private User currentUser; //will be wired if currentUser is a
Spring-managed bean, when compose() is called
Also, you can add a forward event to the newly added component and forward the event to a macro component.
@Wire
private Hlayout mc_layout;
public void setGender(String gender) {
Textbox genderTbx = new Textbox();
genderTbx.setValue(gender);
genderTbx.setParent(mc_layout);
Implement Custom Java Class 70
</window>
Version History
Version Date Content
5.0.5 October, HtmlMacroComponent.compose() (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlMacroComponent.
2010 html#compose()) was introduced.
Composite Component
Like a macro component, a composite component is an approach to compose a component based on a template.
Unlike a macro component, a composite component has to create and wire the child components by itself, and
handle ID space if necessary. The advantage is that a composite component can extend from any component, such as
Row [1], such that it is easier to fit to any situation (and no need for the inline concept).
In short, it is suggested to use a macro component if applicable (since it is easier), while using a composite
component otherwise.
If you'd like to assemble UI at runtime (aka., templating), please refer to the Templating section for
more information.
Implement a Template
The implementation of a template is straightforward. There is nothing special to handle. Since it is rendered by
org.zkoss.zk.ui.Component, java.util.Map) Execution.createComponents(java.lang.String,
[2]
org.zkoss.zk.ui.Component, java.util.Map) , you could pass whatever data you prefer to it (through the arg
argument).
Suppose we have a template as follows, and it is placed at /WEB-INF/composite/username.zul.
<zk>
Usename: <textbox id="mc_who"/>
</zk>
package foo;
import org.zkoss.zk.ui.IdSpace;
import org.zkoss.zk.ui.select.Selectors;
import org.zkoss.zul.Row;
import org.zkoss.zul.Textbox;
public Username() {
//1. Render the template
Executions.createComponents("/WEB-INF/composite/username.zul",
this, null);
return mc_who.getValue();
}
public void setWho(String who) {
mc_who.setValue(who);
}
//public void onOK() {..} //Add event listeners if required, and
wired by Components.addForwards
}
Notice that there is a utility called ZK Composite [7]. With the help of ZK Composite [7], components
are created and wired automatically based on the Java annoataions you provide. In other words, Step 3
and 4 are done automatically. For more information, please refer to the Define Components with Java
Annotations section.
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
public class Username extends Row implements IdSpace {
@WireVariable
private User user;
public Username() {
Executions.createComponents("/WEB-INF/composite/username.zul",
this, null);
Selectors.wireVariables(this, this,
Selectors.newVariableResolvers(getClass(), Row.class));
Selectors.wireComponents(this, this, false);
Selectors.wireEventListeners(this, this);
}
...
Composite Component 73
ID Space
Unless you extend a component that is an ID space owner (such as Window [1]), all child components specified in
the template will be in the same ID space as its parent. It might be convenient at the first glance. However, it will
cause the ID conflict if we have multiple instances of the same composite component. Thus, it is generally suggested
to make the composite component as a space owner
It can be done easily by implementing an extra interface IdSpace [1]. No other method needs to be implemented.
Of course, if you prefer not to have an additional ID space, you don't need to implement an IdSpace [1].
<grid>
<rows>
<username who="Joe"/>
<username who="Hellen"/>
</rows>
</grid>
<language-addon>
<addon-name>myapp</addon-name>
<component>
<component-name>username</component-name>
<extends>rows</extends>
<component-class>foo.Username</component-class>
</component>
</language-addon>
@Composite(name="username", macroURI="/WEB-INF/partial/username.zul")
public class Username extends Rows implements IdSpace {
@Wire
private Textbox mc_who; //will be wired when
Components.wireVariables is called
This approach is suggested if you have to develop several composite components. As shown, it is more convenient
since you don't have to maintain a separated XML file (the language addon). Furthermore, it will create the
components and wire them automatically based on the annotations.
Notice that it requires additional JAR files [11], please refer to Small Talks: Define Composite Component using Java
Annotation in ZK6 for the details.
Composite Component 75
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Row. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Div. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#createComponents(java. lang. String,
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ Selectors. html#wireVariables(org. zkoss. zk. ui. Component,
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ Selectors. html#wireComponents(org. zkoss. zk. ui. Component,
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ Selectors. html#wireEventListeners(org. zkoss. zk. ui. Component,
[7] http:/ / github. com/ zanyking/ ZK-Composite|
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ Selectors. html#newVariableResolvers(java. lang. Class,
[9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ spring/ DelegatingVariableResolver. html#
[10] https:/ / github. com/ zanyking/ ZK-Composite
[11] http:/ / github. com/ zanyking/ ZK-Composite/ downloads
Client-side UI Composing
Though optional, you could have the total control of the client's functionality without the assistance of server-side
coding. Generally, you don't need to do it. You don't even need to know how ZK Client Engine and client-side
widgets communicate with the server. Their states can be synchronized automatically with ZK. However, you can
still control this type of synchronization if you want. It is the so-called Server-client fusion.
A good rule of thumb is that you should handle events and manipulate UI mostly, if not all, on the server, since it is
more productive. Then, you could improve the responsiveness and visual effects, and/or reduce server loading by
handling them at the client, when it is appropriate. Notice that JavaScript is readable by any user, so be careful not to
expose sensitive data or business logic when migrating some code from server to client.
• For information about client-side UI composing, please refer to ZK Client-side Reference: UI Composing.
• For information about customizing client-side widget's behavior, please refer to ZK Client-side Reference:
Widget Customization.
• For information about client-side markup language (iZUML), please refer to ZK Client-side Reference: iZUML.
• For information about client-side event handling, please refer to ZK Client-side Reference: Event Listening
Version History
Version Date Content
Event Handling 76
Event Handling
An event (Event [1]) is used to abstract an activity made by user, a notification made by an application, and an
invocation of server push. Thus, the application can handle different kind of notifications and sources with a
universal mechanism. By and large, developers can even use the same approach to handle, say, message queues.
In this section we will discuss how to handle events, such as listening, posting and forwarding.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Event. html#
Event Listening
Listen by the use of an Event Handler
An event handler is a method specified as an event attribute of a ZK page or as a member of a component class.
where the content of the event handler is the code snippet in Java. The event handler will be interpreted at the run
time (by use of BeanShell). If you prefer to use other language, you could specify the language name in front of it.
For example, the following uses Groovy as the interpreter:
If the event handler needs to handle the event, it can declare the event as the argument as follows.
Suggestions:
• It is suggested to use this approach for component development, since it is subtle for application developers to
notice its existence. In addition, it requires to extend the component class.
Event Listener
An event listener is a class implementing EventListener (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/
ui/event/EventListener.html#). For example,
Then, you can register an event listener to the component that might receive the event by the use of
org.zkoss.zk.ui.event.EventListener) Component.addEventListener(java.lang.String,
org.zkoss.zk.ui.event.EventListener) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component.
html#addEventListener(java.lang.String,). For example,
This is a typical approach to handle events. However, it is a bit tedious to register event listeners one-by-one if there
are a lot of listeners. Rather, it is suggested to use a composer as described in the following section.
Event Listening 78
As shown above, the method to listen is annotated with the Listen (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/ zk/ ui/ select/ annotation/ Listen. html#) annotation using the event name followed by a selector string
identifying the component(s) (for more selector syntax examples see SelectorComposer (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ SelectorComposer. html#)). The composer will register each annotated
method as an event listener to the selected component automatically. Then, in the ZUL page, you can specify the
apply attribute to associate the composer with a component.
If the listener needs to access the event, just declare it as the argument:
@Listen("onClick = button#hi")
public void showHi(MouseEvent event) {
Messsagebox.show("Hello, " + event.getName());
}
Though not limited, a composer is usually associated with an ID space (such as Window (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#)) to handle events and component within the give ID space. You
could associate any component that properly represents a scope of your application to manage.
For more information please refer to the Wire Event Listeners section.
Event Listening 79
When an event is fired at the client (e.g., the user selects a list item), ZK won't send the event if no event listener is
registered for it or only deferrable listeners are registered. instead, the event is queued at the client.
On the other hand, if at least one non-deferrable listener is registered, the event will be sent immediately with all
queued events to the server at once. No event is lost and the arriving order is preserved.
Tip: Use the deferrable listeners for maintaining the server status, while the non-deferrable listeners for
providing the visual responses for the user.
Precedence of Listeners
The order of precedence for listeners from the highest to the lowest is as follows.
1. Event listeners implemented with Express (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/event/
Express.html#), and registered by org.zkoss.zk.ui.event.EventListener)
Component.addEventListener(java.lang.String, org.zkoss.zk.ui.event.EventListener) (http://www.zkoss.org/
javadoc/latest/zk/org/zkoss/zk/ui/Component.html#addEventListener(java.lang.String,)
2. Event handlers defined in a ZUML document
3. Event listeners registered by org.zkoss.zk.ui.event.EventListener) Component.addEventListener(java.lang.String,
org.zkoss.zk.ui.event.EventListener) (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/Component.
html#addEventListener(java.lang.String,) (and without Express (http://www.zkoss.org/javadoc/latest/zk/
org/zkoss/zk/ui/event/Express.html#))
• It includes the method of a composer wired by GenericForwardComposer (http://www.zkoss.org/javadoc/
latest/zk/org/zkoss/zk/ui/util/GenericForwardComposer.html#) because the event listener is used.
4. Event handlers defined as a class's method
5. Event listeners registered to a page by org.zkoss.zk.ui.event.EventListener)
Page.addEventListener(java.lang.String, org.zkoss.zk.ui.event.EventListener) (http://www.zkoss.org/javadoc/
latest/zk/org/zkoss/zk/ui/Page.html#addEventListener(java.lang.String,)
Version History
Version Date Content
5.0.6 November SerializableEventListener (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/
2010 SerializableEventListener. html#) was introduced to simplify the instantiation of a serializable anonymous class
Event Firing 81
Event Firing
Events are usually fired (aka., triggered) by a component (when serving the user at the client). However, applications
are allowed to fire events too.
There are three ways to trigger an event: post, send and echo.
Post an Event
Posting is the most common way to trigger an event. By posting, the event is placed at the end of the system event
queue[1]. Events stored in the system event queue are processed one-by-one in first-in-first-out order. Each desktop
has one system event queue and all events are handled sequentially.
To trigger an event, you could invoke org.zkoss.zk.ui.Component, java.lang.Object)
[2]
Events.postEvent(java.lang.String, org.zkoss.zk.ui.Component, java.lang.Object) . For example,
In addition to posting an event to the end of the system event queue, you could specify a priority with
java.lang.String, org.zkoss.zk.ui.Component, java.lang.Object) Events.postEvent(int, java.lang.String,
org.zkoss.zk.ui.Component, java.lang.Object) [3]. By default, the priority is 0. The higher the priority the earlier an
event is processed.
Notice that the invocation returns after placing the event to the system event queue. In other words, the event won't
be processed unless all other events posted earlier or with higher priority are processed.
[1] Please don't confuse it with the event queues discussed in the event queues section, which are application-specific, while the system event
queue is invisible to application developers.
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Events. html#postEvent(java. lang. String,
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Events. html#postEvent(int,
Send an Event
If you prefer to trigger an event to a component directly and process it immediately, rather than placing in the system
event queue and waiting for execution, you could use org.zkoss.zk.ui.Component, java.lang.Object)
Events.sendEvent(java.lang.String, org.zkoss.zk.ui.Component, java.lang.Object) (http:/ / www. zkoss. org/ javadoc/
latest/zk/org/zkoss/zk/ui/event/Events.html#sendEvent(java.lang.String,) to trigger the event.
[1] By default, the event thread is disabled. Please refer to the Event Threads section for more information.
Echo an Event
Echoing is a way to delay event processing until the next AU request (aka., Ajax) is received.
More precisely, the event being echoed won't be queued into the system event queue. Rather, it asks the client to
send back an AU request immediately. Furthermore, after the server receives the AU request, the event is then posted
to the system event queue for processing.
In other words, the event won't be processed in the current execution. Rather, it is processed in the following request
when the event is echoed back from the client. Here is an example of using org.zkoss.zk.ui.Component,
java.lang.Object) Events.echoEvent(java.lang.String, org.zkoss.zk.ui.Component, java.lang.Object) (http:/ / www.
zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/event/Events.html#echoEvent(java.lang.String,):
Event echoing is useful for implementing a long operation. HTTP is a request-and-response protocol, so the user
won't receive any feedback until the request has been served and responsed. Thus, we could send back some busy
message to let the user know what has happened, and echo back an event to do the long operation. For more
information, please refer to the Long Operations: Use Echo Events section.
Version History
Version Date Content
Event Forwarding 83
Event Forwarding
Overview
For easy programming, ZK does not introduce any complex event flow. When an event is sent to a target component,
only the event listeners registered for the target component will be called. It is the application's job to forward an
event to another component if necessary.
For example, you might have a menu item and a button to trigger the same action, say, opening a dialog, and then it
is more convenient to have a single listener to open the dialog, and register the listener to the main window rather
than register to both the menu item and button.
<window id="mywin">
<button label="Save" forward="onSave"/>
<button label="Cancel" forward="onCancel"/>
</window>
Then, window will receive the onSave event when the Save button is clicked.
With this approach we could introduce an abstract layer between the event and the component. For example,
window needs only to handle the onSave event without knowing which component causes it. Therefore, you
could introduce another UI to trigger onSave without modifying the event listener. For example,
Of course, you can use the composer and ZUML's forward together to have more maintainable code.
@Listen("onCancel = #mywin")
public void doCancel() { //signature if you don't care the event
...
[1]
Notice that, as shown above, the event being forwarded is wrapped as an instance of ForwardEvent . To retrieve
the original event, you could invoke ForwardEvent.getOrigin() [2]
Now in your included page use Path [3] while forwarding event to mainWindow Window component
<button forward="//mainPage/mainWindow.onSave" /> <!-- default forward event is onClick -->
You could specify any application-specific data in the forward attribute by surrounding it with the parenthesis as
shown below.
@Listen("onSelectTab = #ctrl")
public void doChangeTab(ForwardEvent e) {
MouseEvent me = (MouseEvent) e.getOrigin();
System.out.println(me.getData());
}
If you want to forward several events at once, you can specify them in the forward attribute by separating them with
the comma (,). For example,
Event Forwarding 85
In addition, the target component and the event data can be specified in EL expressions, while the event names
cannot.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ ForwardEvent. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ ForwardEvent. html#getOrigin()
[3] http:/ / books. zkoss. org/ wiki/ ZK_Developer's_Guide/ ZK_in_Depth/ Component_Path_and_Accesibility/ Access_UI_Component
[4] http:/ / books. zkoss. org/ wiki/ ZK_Developer's_Reference/ UI_Composing/ ID_Space
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ ForwardEvent. html#getData()
Event Queues
Overview
An event queue is an event-based publish-subscriber solution for application information delivery and messaging. It
provides asynchronous communications for different modules/roles in a loosely-coupled and autonomous fashion.
By publishing, a module (publisher) sends out messages without explicitly specifying or having knowledge of
intended recipients. By subscribing, a receiving module (subscriber) receives messages that the subscriber has
registered an interest in it, without explicitly specifying or knowing the publisher.
Notice that if you want to locate an event queue in a working thread (rather than an event listener), you have to use
[2]
org.zkoss.zk.ui.Session) EventQueues.lookup(java.lang.String, org.zkoss.zk.ui.Session) or
org.zkoss.zk.ui.Application) EventQueues.lookup(java.lang.String, org.zkoss.zk.ui.Application) [2], depending on
your requirement.
session The event queue is visible only in the same session. The server push will be
java.lang.String)
enabled automatically if it subscribes a session-scoped event queue.
EventQueues.lookup(java.lang.String,
[2]
java.lang.String) Notice that the server push is disabled automatically if the current desktop doesn't
java.lang.String, boolean) subscribe to any session- or application-scoped event queue. Also notice that the
EventQueues.lookup(java.lang.String, locating and creating of an event queue and publishing an event won't start the
[2]
java.lang.String, boolean) server push.
org.zkoss.zk.ui.Session) ZK 5.0.5 and Prior: When a server push is enabled, a working thread is
EventQueues.lookup(java.lang.String, instantiated and started. It means this feature cannot be used in the environment
[2]
org.zkoss.zk.ui.Session) that doesn't allow working threads, such Google App Engine. No such limitation is
org.zkoss.zk.ui.Session, boolean) likely to occur in ZK 5.0.6 or later.
EventQueues.lookup(java.lang.String,
[2]
org.zkoss.zk.ui.Session, boolean)
Event Queues 87
application java.lang.String) The event queue is visible only in the whole application. The server push will be
enabled automatically.
EventQueues.lookup(java.lang.String,
[2]
java.lang.String) Notice that the server push is disabled automatically if the current desktop doesn't
java.lang.String, boolean) subscribe to any session- or application-scoped event queue. Also notice that the
EventQueues.lookup(java.lang.String, locating and creating of an event queue and publishing an event won't start the
[2]
java.lang.String, boolean) server push.
org.zkoss.zk.ui.WebApp) ZK 5.0.5 and Prior: When a server push is enabled, a working thread is
EventQueues.lookup(java.lang.String, instantiated and started. It means this feature cannot be used in the environment
[2]
org.zkoss.zk.ui.WebApp) that doesn't allow working threads, such Google App Engine. No such limitation is
org.zkoss.zk.ui.WebApp, boolean) likely to occur in ZK 5.0.6 or later.
EventQueues.lookup(java.lang.String,
[2]
org.zkoss.zk.ui.WebApp, boolean)
Publish an Event
To publish, just invoke one of the publish methods of EventQueue [3] (returned by lookup). For example,
The event listener is invoked just like a normal event. You can manipulate the component or do whatever as you
want.
Example: Chat
Here is an example: chat.
<zk>
<include src="page1.zul"></include>
<include src="page2.zul"></include>
</zk>
package demo;
public class WindowComposer1 extends SelectorComposer {
@Listen("onClick = button#btn")
Event Queues 89
package demo;
public class WindowComposer2 extends SelectorComposer {
By doing this, you can change data in ZUL page 2 by clicking the button in ZUL page 1 Note: Open main.zul page
in browser to run the above sample codes. If there are any other ZUL pages that have subscribed to the same name of
the EventQueues, the content in those ZUL pages will also be updated.
Event Queues 90
subscribe only in an event listener, or only in an event listener, or only in an event listener, or the only in an event listener, or the
the current execution is the current execution is current execution is available. current execution is available.
available. available.
multi-thread Not used Not used 5.0.5 or prior: Used 5.0.5 or prior: Used
(transparent) (transparent)
5.0.6 or later: Not used 5.0.6 or later: Not used
Availability CE EE CE CE
Clean Up
Remember to unsubscribe [5] a listener when you don't need it anymore to avoid performing duplicate actions.
It's better to remove an event queue manually as you don't need it. If you create a desktop scope event queue, since
it's stored as a desktop's attribute, it will be destroyed when the desktop is destroyed. The same rule applies to a
session-scoped event queue.
For example, let us say we want to introduce the JMS scope, then we can implement as follows (only pseudo-code):
<library-property>
<name>org.zkoss.zk.ui.event.EventQueueProvider.class</name>
<value>MyEventQueueProvider</value>
</library-property>
<device-config>
<device-type>ajax</device-type>
<server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class>
</device-config>
Event Queues 92
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueues. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueues. html#lookup(java. lang. String,
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueue. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#getCurrent
[5] https:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueue. html#unsubscribe(org. zkoss. zk. ui. event.
EventListener)
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ impl/ EventQueueProvider. html#
[7] Comet Programming (http:/ / en. wikipedia. org/ wiki/ Comet_(programming))
[8] Like most ZK features, you can provide your own implementation if you like.
Version History
Version Date Content
5.0.4 August 2010 The group scope was introduced to allow the communication among inline frames without Server Push (minimizing the
network bandwidth consumption).
5.0.6 November The event queue won't start any working threads and they are serializable, so it is safe to use them in a clustering
2010 environment.
Version History
Version Date Content
MVC 93
MVC
MVC (Model-View-Control[1]) is a design pattern designed to separate the model, view and controller. It is strongly
suggested to apply MVC pattern to your application, not only because it easy to develop and maintain, but also the
performance is great.
Alternative: MVVM
MVVM represents Model, View, and ViewModel[2]. It is a variant of the MVC design pattern. Unlike MVC, the
control logic is implemented in a POJO class called the view model. It provides the further abstraction that a view
model assumes nothing about any visual element in the view. It thus avoids mutual programming ripple effects
between UI and the view model. On the other hand, some developers might find it not as intuitive as MVC. For more
information, please refer to the MVVM section.
View
The view is UI -- a composition of components. As described in the UI Composing section, UI can be implemented
by a ZUML document or in Java. For sake of description, ZUML is used to illustrate the concept and features.
Controller
The controller is a Java program that is used to glue UI (view) and Data (model) together.
For a simple UI, there is no need to prepare a controller. For example, the data of a Listbox (http://www.zkoss.org/
javadoc/ latest/ zk/ org/ zkoss/ zul/ Listbox. html#) could be abstracted by implementing ListModel (http:/ / www.
zkoss.org/javadoc/latest/zk/org/zkoss/zul/ListModel.html#).
For typical database access, the glue logic (i.e., control) can be handled by a generic feature called Data Binding. In
other words, the read and write operations can be handled automatically by a generic Data Binding, and you don't
need to write the glue logic at all.
MVC 94
To implement a custom controller, you could extend from SelectorComposer (http:/ / www. zkoss. org/ javadoc/
latest/ zk/ org/ zkoss/ zk/ ui/ select/ SelectorComposer. html#), or implement Composer (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Composer. html#) from scratch. Then, specify it in the element it wants to
handle in a ZUML document.
Model
The model is the data an application handles. Depending on the application requirement, it could be anything as long
as your controller knows it. Typical objects are POJOs, beans, Spring-managed beans, and DAO.
In additions to handling the data in a controller, some components support the abstraction model to uncouple UI and
data. For example, grid, listbox and combobox support ListModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/ zul/ ListModel. html#), while tree supports TreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/zul/TreeModel.html#).
Controller
Overview
The controller is a Java program that is used to glue UI (view) and Data (model) together.
[1]
A simple UI does not require any controllers. For example, the data of a Listbox could be abstracted by
implementing ListModel [2] as described in the Model section.
For typical database access, the glue logic (i.e., controller) can be handled by a generic feature called Data Binding.
In other words, the create, read, update and delete operations (CRUD) can be handled automatically by a generic
Data Binding mechanism, and you don't need to write the glue logic at all as described in the Data Binding section.
If none of above fulfills your requirement, you could implement a custom controller(which is called a composer in
ZK terminology). In the following sections we will discuss how to implement a custom controller in details.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Listbox. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModel. html#
Composer 95
Composer
Custom Controller
[3]
A custom controller is called a composer in ZK. To implement it, you can simply extends SelectorComposer .
Then, specify it in the UI element that it wants to handle in a ZUML document.
A composer usually does, but not limited to:
• Load data to components, if necessary.
• Handle events and manipulate components accordingly, if necessary.
• Provide the data, if necessary.
In additions, a composer can be used to involve the lifecycle of ZK Loader for doing:
• Exception handling
• Component instantiation monitoring and filtering
A composer can be configured as a system-level composer, such that it will be called each time a ZUML document
is loaded.
Implement Composers
To simplify the implementation of the controller part of UI, ZK provides several skeleton implementations. For
example, SelectorComposer [3], as one of the most popular skeletons, wires components, variables and event
listeners automatically based on Java annotations you specify. For example, in the following controller and zul,
Controller:
package foo;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zul.*;
@Wire
Textbox input;
@Wire
Label output;
@Listen("onClick=#ok")
public void submit() {
output.setValue(input.getValue());
}
@Listen("onClick=#cancel")
public void cancel() {
output.setValue("");
}
}
Composer 96
ZUL:
<window apply="foo.MyComposer">
<div>
Input: <textbox id="input" />
</div>
<div>
Output: <label id="output" />
</div>
<button id="ok" label="Submit" />
<button id="cancel" label="Clear" />
</window>
the member fields input, output are automatically assigned with components with identifiers of "input" and
"output", respectively. The methods submit() and cancel() will be called when user clicks on the
corresponding buttons.
In additions to wiring via identifiers, you could wire by a CSS3-like selector (Selector [1]), such as
• @Wire("#foo")
• @Wire("textbox, intbox, decimalbox, datebox")
• @Listen("onClick = button[label='Clear']")
• @Wire("window > div > button")
For more information, please refer to the following sections: Wire Components, Wire Variables and Wire Event
Listeners.
Apply Composers
Once a composer is implemented, you could associate it with a component, such that the composer can control the
UI components that rooted the given component.
Associating a composer to a component is straightforward: just specify the class to the apply attribute of the XML
element you want to control. For example,
<grid apply="foo.MyComposer">
<rows>
<row>
<textbox id="input"/>
<button label="Submit" id="submit"/>
<button label="Reset" id="reset"/>
</row>
</rows>
</grid>
If you have to handle the components after ZK Loader initializing them, you could override
SelectorComposer.doAfterCompose(T) [2]. It is important to call back super.doAfterCompose(comp).
Otherwise, the wiring won't work. It also means that none of the data members are wired before calling
super.doAfterCompose(comp).
}
...
where comp is the component that the composer is applied to. In this example, it is the grid. As the name indicates,
doAfterCompose is called after the grid and all its descendants are instantiated.
<grid apply="${fooComposer}">
If a class name is specified, each time the component is instantiated, an instance of the specified composer class is
instantiated too. Thus, you don't have to worry about the concurrency issue. However, if you specify an instance, it
will be used directly. Thus, you have to either create an instance for each request, or make it thread-safe.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ Selector. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ SelectorComposer. html#doAfterCompose(T)
[3] It can be done by invoking SelectorComposer (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ SelectorComposer.
html#), because the component's attribute can be referenced directly in EL expressions. Notice that if you want to reference it in EL
expressions, you'd better to set the attribute in . UNIQ-javadoc-2-f6421cf86b2cc1a3-QINU was called after all child components are
instantiated.
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ GenericForwardComposer. html#
</window>
Notice that $composer is always assigned no matter the ID is, so it is more convenient to use. However, if there
are several components assigned with composers, you might have to use ID to distinguish them.
The second name (id$ClassName) is useful, If there are multiple composers applied.
<window apply="MyComposer">
<custom-attributes composerName="mc"/> <!-- name the composer as mc -->
<textbox value="${mc.title}"/>
</window>
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class UsersComposer extends SelectorComposer<Window> {
@WireVariable
private List<User> users;
}
}
where we register a variable resolver called DelegatingVariableResolver (http:/ / www. zkoss. org/ javadoc/ latest/
zk/ org/ zkoss/ zkplus/ spring/ DelegatingVariableResolver. html#) with the VariableResolver (http:/ / www. zkoss.
org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ VariableResolver. html#) annotation. As its name suggests,
DelegatingVariableResolver (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ spring/
DelegatingVariableResolver. html#) will be used to retrieve Spring-managed beans when @WireVariable is
encountered. For more information, please refer to the Wire Variables section.
Notice that the variables will be wired before instantiating the component and its children, so it is OK to access them
in the ZUML document, as below.
<panel apply="foo.MyComposer">
<div>
<datebox/>
<textbox/>
</div>
</panel>
Then, Composer.doAfterCompose(T) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/
Composer. html#doAfterCompose(T)) will be called for datebox, textbox, div and then panel (in the order of
child-first-parent-last). If FullComposer (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/
FullComposer.html#) is not implemented, only the panel will be called.
Notice that, because Composer.doAfterCompose(T) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/
util/ Composer. html#doAfterCompose(T)) will be called for each child, the generic type is better to Component
(http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#) rather than the component's type
which the composer is applied to. For example,
Lifecycle
Here is a lifecylce of the invocation of a composer:
System-level Composer
If you have a composer that shall be invoked for every page, you could register a system-level composer rather than
specifying it on every page.
It could be done by specifying the composer you implemented in WEB-INF/zk.xml[1]:
<listener>
<listener-class>foo.MyComposer</listener-class>
</listener>
Each time a ZK page, including ZK pages and richlets, is created, ZK will instantiate one instance for each registered
system-level composer and the invoke Composer.doAfterCompose(T) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/
org/ zkoss/ zk/ ui/ util/ Composer. html#doAfterCompose(T)) for each root component. The system-level composer
is usually used to process ZK pages after all components are instantiated successfully, such as adding a trademark. If
you want to process only certain pages, you can check the request path by calling Desktop.getRequestPath() (http:/ /
www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Desktop. html#getRequestPath()) (the desktop instance can
be found through the given component).
If the system-level composer also implements ComposerExt (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/
zk/ ui/ util/ ComposerExt. html#), it can be used to handle more situations, such as exceptions, like any other
composer can do.
If the system-level composer also implements FullComposer (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/ zk/ ui/ util/ FullComposer. html#), it will be invoked when each component is created. It provides the finest
grain of control but a wrong implementation might degrade the performance.
Composer 102
Notice that since a new instance of the composer is created for each page, there is no concurrency issues.
[1] For more information, please refer to ZK Configuration Reference
Richlet
A system-level composer can implement ComposerExt (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/
ui/ util/ ComposerExt. html#) to handle exceptions for a richlet, such as doCatch and doFinally. However,
doBeforeCompose and doBeforeComposeChildren won't be called.
FullComposer (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ FullComposer. html#) is not
applicable to richlets. In other words, system-level composers are called only for root components.
Version History
Version Date Content
5.0.8 June, GenericAutowireComposer (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ GenericAutowireComposer.
2011 html#) and its derives allow developers to specify a custom name by use of a component attribute called composerName.
Wire Components
Wire Components
In SelectorComposer [3], when you specify a @Wire annotation on a field or setter method, the SelectorComposer
will automatically find the component and assign it to the field or pass it into the setter method.
You can either give a string value, which is interpret as a component selector, as the matching criteria for wiring, or
leave it empty to wire by component id. For example,
ZUL:
<window apply="foo.MyComposer">
<textbox />
<button id="btn" />
</window>
Controller:
CSS3-like Selectors
The string value in @Wire annotation is a component selector, which shares an analogous syntax of CSS3 selector.
The selector specifies matching criteria against the component tree under the component which applies to this
composer.
Given a selector in @Wire annotation, the SelectorComposer will wire a field to the component of the first match
(in a depth-first-search sense) if the data type of the field is a subtype of Component. Alternatively, if the field type
is subtype of Collection, it will wire to an instance of Collection containing all the matched components.
The syntax element of selectors are described as the following:
Type
The component type as in ZUML definition, case insensitive.
@Wire("button")
Button btn; // wire to the first button.
Combinator
Combinator constraints the relative position of components.
@Wire("window button")
Button btn0; // wire to the first button who has an ancestor
window
@Wire("window > button") // ">" refers to child
Button btn1; // wire to the first button whose parent is a window
@Wire("window + button") // "+" refers to adjacent sibling (next
sibling)
Button btn2; // wire to the first button whose previous sibling
is a window
@Wire("window ~ button") // "~" refers to general sibling
Button btn3; // wire to the first button who has an older sibling
window
ID
The component id.
@Wire("label#lb")
Label label; // wire to the first label of id "lb" in the same id
space of root component
@Wire("#btn")
Button btn; // wire to the first component of id "btn", if not a
Button, an exception will be thrown.
Unlike CSS3, the id only refer to the component in the same IdSpace of the previous level or root component. For
example, given zul
Wire Components 104
<window apply="foo.MyComposer">
<div>
<window id="win">
<div>
<button id="btn" /><!-- button 1 -->
<textbox id="tb" /><!-- textbox 1 -->
</div>
</window>
<button id="btn" /><!-- button 2 -->
</div>
</window>
@Wire("#btn")
Button btnA; // wire to button 2
@Wire("#win #btn")
Button btnB; // wire to button 1
@Wire("#win + #btn")
Button btnC; // wire to button 2
@Wire("#tb")
Textbox tbA; // fails, as there is no textbox of id "tb"
// in the id space of the root window (who applies
to the composer).
@Wire("#win #tb")
Textbox tbB; // wire to textbox 1
Class
The sclass of component. For example,
<window apply="foo.MyComposer">
<div>
<button /><!-- button 1 -->
</div>
<span sclass="myclass">
<button /><!-- button 2 -->
</span>
<div sclass="myclass">
<button /><!-- button 3 -->
</div>
</window>
@Wire(".myclass button")
Button btnA; // wire to button 2
@Wire("div.myclass button")
Button btnB; // wire to button 3
Wire Components 105
Attribute
The attributes on components, which means the value obtained from calling corresponding getter method on the
component.
• Note: [id="myid"] does not restrict id space like #myid does, so they are not equivalent.
@Wire("button[label='submit']")
Button btn; // wire to the first button whose getLabel() call
returns "submit"
Pseudo Class
A pseudo class is a custom criterion on a component. There are a few default pseudo classes available:
The nth-child and nth-last-child pseudo classes parameter can also take a pattern, which follows CSS3
specification [1].
Asterisk
Asterisk simply matches anything. It is more useful when working with combinators:
@Wire("*")
Component rt; // wire to any component first met, which is the
root.
@Wire("window#win > * > textbox")
Textbox textbox; // wire to the first grandchild textbox of the
window with id "win"
@Wire("window#win + * + textbox")
Textbox textbox; // wire to the second next sibling textbox of
the window with id "win"
Wire Components 106
Multiple Selectors
Multiple selectors separated by comma refers to an OR condition. For example,
Shadow Selectors
since 8.0.1
<apply id="root" dynamicValue="true"><!-- set dynamicValue="true" to avoid being removed after render -->
<if id="if1" test="@load(vm.showLabel)"><!-- using @load will also prevent this element from being removed -->
</if>
<if id="if2" test="false"><!-- no dynamicValue or data binding expression, will be removed after render -->
<label id="lb2" value="some more text here" /><!-- will not render because the if test equals false -->
</if>
</apply>
</div>
Here are some examples of using shadow selector with the above zul
Wiring by Method
You can either put the @Wire annotation on field or method. In the latter case, it is equivalent to call the method
with the matched component as the parameter. This feature allows a more delicate control on handling auto-wires.
@Wire("grid#users")
private void initUserGrid(Grid grid) {
// ... your own handling
}
In the example above, the SelectorComposer will find the grid of id "users" and call initUserGrid with the grid
as parameter.
• If the method is static or has wrong signature (more than one parameter), an exception will be thrown.
• Wiring by method requires a selector on @Wire annotation, otherwise an exception will be thrown.
• If the component is not found, the method is still called, but with null value passed in.
• Do not confuse @Wire with @Listen, while the latter wires to events.
Wiring a Collection
You can also wire all matched components to a Collection field or by method, if the field is of Collection type or the
method takes a Collection as the parameter.
• If the field starts null or uninitialized or wiring by method, SelectorComposer will try to construct an appropriate
instance and assign to the field or pass to method call.
• If the field starts with an instance of Collection already, the collection will be cleared and filled with matched
components.
• If it wires by method and the selector matches no components, an empty collection will be passed into the method
call.
@Wire("textbox")
List<Textbox> boxes; // wire to an ArrayList containing all matched
textboxes
@Wire("button")
Set<Button> buttons; // wire to a HashSet containing all matched buttons
@Wire("grid#users row")
List<Row> rows = new LinkedList<Row>(); // the LinkedList will be filled
with matched row components.
@Wire("panel")
public void initPanels(List<Panel> panels) {
// ...
}
Wire Components 108
Wiring Sequence
While extending from SelectorComposer [3], the fields and methods with the proper annotations will be wired
automatically. Here is the sequence of wiring:
• In Composer.doAfterCompose(T) [2], it wires components to the fields and methods with the Wire [3] annotation.
• Before onCreate event of the component which applies to the composer, the SelectorComposer will attempt to
wire the null fields and methods again, for some of the components might have been generated after
doAfterCompose() call.
Performance Tips
The selector utility is implemented by a mixed strategy. In a selector sequence, the first few levels with id specified
are handled by Component.getFellow(Component) [4], and the rest are covered by depth first search (DFS). In brief,
the more ids you specify in the first few levels of a selector string, the more boost you can obtain in component
finding. For example,
• Note: specifying id via attribute (for instance, [id='myid']) does not lead to the same performance boost.
In the case of multiple selectors, only the first few identical levels with ids enjoy the performance gain.
In brief, it is recommended to specify id in selector when you have a large component tree. If possible, you can
specify id on all levels, which maximize out the performance gain from the algorithm.
Wire Components 109
Version History
Version Date Content
References
[1] http:/ / www. w3. org/ TR/ css3-selectors/ #nth-child-pseudo
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Composer. html#doAfterCompose(T)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ annotation/ Wire. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getFellow(Component)
Wire Variables
Wire Variables
[3]
SelectorComposer not only wires UI components, but also wires beans from implicit objects and registered
variable resolvers.
@WireVariable
private Page _page;
@WireVariable
private Desktop _desktop;
@WireVariable
private Session _sess;
@WireVariable
private WebApp _wapp;
@WireVariable("desktopScope")
private Map<String, Object> _desktopScope;
}
Wire Variables 110
@VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class})
public class FooComposer extends SelectorComposer<Gird> {
....
}
[3] [5]
To have SelectorComposer to wire a variable, you have to annotate it with the WireVariable annotation. For
example,
@VariableResolver({foo1.MyResolver.class, foo2.AnotherResolver.class})
public class FooComposer extends SelectorComposer<Gird> {
@WireVariable
Department department;
@WireVariable
public void setManagers(Collection<Manager> managers) {
//...
}
}
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class PasswordSetter extends SelectorComposer<Window> {
@WireVariable
private User user;
@Wire
private Textbox password; //wired automatically if there is a
textbox named password
@Listen("onClick=#submit")
public void submit() {
user.setPassword(password.getValue());
}
}
DelegatingVariableResolver [9] is a variable resolver used to retrieve the Spring-managed bean, so the variable will
be retrieved and instantiated by Spring.
Notice that the variables are wired before instantiating the component and its children, so you can use them in EL
expressions. For example, assume we have a composer as follows.
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class UsersComposer extends SelectorComposer<Window> {
@WireVariable
Wire Variables 111
Then, you could reference to getUsers() in the ZUML document. For example,
<window apply="UsersComposer">
<grid model="${$composer.users}">
...
where $composer is a built-in variable referring to the composer. For more information, please refer to the
Composer section.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Components. html#getImplicit(org. zkoss. zk. ui. Page,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Session. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ WebApp. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ annotation/ VariableResolver. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ annotation/ WireVariable. html#
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window apply="${passwordSetter}">
...
@Component
public class PasswordSetter extends SelectorComposer {
@Autowired User user;
...
Unfortunately, this approach is error-prone. The reason is that none of Spring's scopes matches correctly with the
lifecycle of the composers. For example, if the Session scope is used, it will cause errors when the user opens two
browser windows to visit the same page. In this case, the same composer will be used to serve all desktops in the
given session, and it is wrong.
The Prototype scope is a better choice since a new instance is instantiated for each request. However, it also implies
another new instance will be instantiated if the Spring variable resolver is called to resolve the same name again in
the later requests. It is unlikely, but it might be triggered implicitly and hard to debug. For example, it happens if
some of your code evaluates an EL expression that references the composer's name, when an event received.
ZK Spring (http:/ / www. zkoss. org/ product/ zkspring) is recommended if you want to use Spring intensively. It
extends Spring to provide the scopes matching ZK lifecycle, such as the IdSpace and Component scopes. Please
refer to ZK Spring Essentials for more detailed information.
Wire Variables 112
Wiring Sequence
When extending from SelectorComposer (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/
SelectorComposer. html#), the fields and methods with the proper annotations will be wired automatically. Here is
the sequence of wiring:
• In org.zkoss.zk.ui.Component, org.zkoss.zk.ui.metainfo.ComponentInfo)
ComposerExt.doBeforeCompose(org.zkoss.zk.ui.Page, org.zkoss.zk.ui.Component,
org.zkoss.zk.ui.metainfo.ComponentInfo) (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/util/
ComposerExt.html#doBeforeCompose(org.zkoss.zk.ui.Page,), it wires variables to the fields and methods
annotated with the WireVariable (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/select/
annotation/WireVariable.html#) annotation. Here is the sequence how it looks for the variable:
1. First, it will look for the variable resolver defined in the ZUML document first (by use of
Page.addVariableResolver(org.zkoss.xel.VariableResolver) (http://www.zkoss.org/javadoc/latest/zk/org/
zkoss/zk/ui/Page.html#addVariableResolver(org.zkoss.xel.VariableResolver))).
2. Second, it looks for the variable resolver annotated at the class with the VariableResolver (http://www.zkoss.
org/javadoc/latest/zk/org/zkoss/zk/ui/select/annotation/VariableResolver.html#) annotation.
3. If none is found, it looks for the implicit objects, such as session and page.
Wire Variables 113
Version History
Version Date Content
@Listen("onClick = #btn0")
public void submit(MouseEvent event) {
// called when onClick is received on the component of id btn0.
}
@Listen("onSelect = #listbox0")
public void select(SelectEvent event) {
// called when onSelect is received on the component of id
listbox0.
}
@Listen("onChange = textbox#input0")
public void change() {
// called when onChange is received on the textbox of id input0.
}
@Listen("onChange = textbox#input1")
public void change(InputEvent event) {
// called when onChange is received on the textbox of id input1.
}
@Listen("onChange = textbox#input2")
public void change(Event event) {
// called when onChange is received on the textbox of id input2.
}
Wire Event Listeners 114
Multiple Targets
If the selector matches multiple components, the event listener will be wired to all matched components. In such
case, if you need to know which component receives the event, you can retrieve it from Event#getTarget().
For example,
The execute order of an event listener could be declared by specifying a number after the event name. The event
listeners will be executed in order from largest to smallest of the declared priority number. Listeners without a
priority number will be set to 0 automatically.
For example,
@Listen("onClick(1) = #btn0")
public void submit1(MouseEvent event) {
// called before submit2 method when onClick is received on the
component of id btn0.
}
@Listen("onClick = #btn0")
public void submit2(MouseEvent event) {
// the priority of this listener will be set to 0 by default,
and will be called after submit1 and before submit2 method when onClick
is received on the component of id btn0.
}
@Listen("onClick(-1) = #btn0")
Wire Event Listeners 115
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ select/ annotation/ Listen. html#
Subscribe to EventQueues
Subscribe to EventQueues
• Available for ZK:
•
[1]
You can subscribe a method (as if in an EventListener) to an EventQueue by annotate it with Subscribe . For
example,
@Subscribe("queue1")
public void method1(Event event) {
// this method will be called when EventQueue "queue1" of Desktop
scope is published
Object data = event.getData();
Component target = event.getTarget();
}
public void publish() {
EventQueue<Event> eq = EventQueues.lookup("queue1", EventQueues.DESKTOP,
true);
eq.publish(new Event("onMyEvent", component, data));
}
In the example above, when you publish an event in the EventQueue, the subscribed method will be called. This is a
useful mechanism to communicate with other composers. See also EventQueue [3].
EventQueue Scope
You can subscribe to EventQueue of different scope.
}
public void publish() {
EventQueue<Event> eq = EventQueues.lookup("queue2", EventQueues.SESSION,
true);
eq.publish(new Event("onMyEvent", component, data));
}
Available scopes are: Desktop, Group, Session, Application. Note that Group scope requires ZK EE. See also
EventQueues [1].
Event Name
Since 7.0.3, you can also listen to a specified event name
@Subscribe("queue3")
public void method3() { // the event parameter can be omitted
// ...
}
Since 7.0.3, ZK automatically maps event data into the method parameters in order.
@Subscribe("queue3")
public void method3(int i, String s) {
// i will be 100, s will be "eventData"
// ...
}
If you put the event at the first one, it also works well.
Subscribe to EventQueues 117
@Subscribe("queue3")
public void method3(Event event, int i, String s) {
// ...
}
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ ui/ select/ annotation/ Subscribe. html#
[2] http:/ / tracker. zkoss. org/ browse/ ZK-2076
Model
The model is the data an application handles. Depending on the application requirement, it could be anything as long
as your controller knows it. Typical objects are POJOs, beans, Spring-managed beans, and DAO. Examples of
manipulating the model in the controller was discussed in the previous sections.
In this section and subsections, we will focus on the model that ZK components support directly without custom glue
logic. For example, implementing ListModel [2] to control the display of Listbox [1] and Grid [3], and ChartModel [1]
to control Chart [2].
In addition to implementing these models, you could use one of the predefined implementation such as
SimpleListModel [3] and SimplePieModel [4]. For detailed description, please refer to the following sections.
}
}
<grid apply="foo.FooComposer">
...
Use Databinder
If you are using data binding to handle the database, you could have the data binder to assign the model for you. For
example, assume that you have a collection called persons (an implementation of java.util.List), then:
<listbox model="@{persons}">
...
Use EL Expressions
EL is another common way to assign the model. For example, assume you have a variable resolver called
foo.FooVariableResolver implementing VariableResolver [2] as follows.
<?variable-resolver class="foo.FooVariableResolver"?>
<listbox model="${persons}">
...
The other approach is to use the function mapper. For example, assume you have an implementation called
foo.CustomerListModel, then you could use it to drive a listbox as follows.
Use zscript
If you are building a prototype, you could use zscript to assign the model directly. For example,
<zk>
<zscript>
ListModel infos = new ListModelArray(
new String[][] {
{"Apple", "10kg"},
Model 119
{"Orange", "20kg"},
{"Mango", "12kg"}
});
</zscript>
<listbox model="${infos}"/>
</zk>
Notice that, since the performance of zscript is not good and the mix of Java code in ZUML is not easy to maintain,
it is suggested not to use this approach in a production system. Please refer to Performance Tips for more
information.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ChartModel. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Chart. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ SimpleListModel. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ SimplePieModel. html#
List Model
Listbox [1], Grid [3], and Tabbox [1] allow developers to separate the view and the model by implementing ListModel
[2]
. Once the model is assigned (with Listbox.setModel(org.zkoss.zul.ListModel) [2]), 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.
Model-driven Display
As shown, the listbox retrieves elements from the specified model[3], and then invokes the renderer, if specified, to
compose the listitem for the element.
The retrieval of elements is done by invoking ListModel.getSize() [4] and ListModel.getElementAt(int) [5].
List Model 120
The listbox will register itself as a data listener to the list model by invoking
ListModel.addListDataListener(org.zkoss.zul.event.ListDataListener) [6]. Thus, if the list model is not mutable, the
implementation has to notify all the registered data listeners. It is generally suggested to extend from
AbstractListModel [7], or use any of the default implementations, which provide a set of utilities for handling data
listeners transparently. We will talk about it later in #Notify for Data Updates.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Tabbox. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Listbox. html#setModel(org. zkoss. zul. ListModel)
[3] The listbox is smart enough to read the elements that are visible at the client, such the elements for the active page. It is called Live Data
or Render on Demand'.'
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModel. html#getSize()
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModel. html#getElementAt(int)
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModel. html#addListDataListener(org. zkoss. zul. event.
ListDataListener)
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ AbstractListModel. html#
If the amount of your data is small, you could load them all into a list, map, set or array. Then, you could use one of
the default implementations as described above.
Alternatively, you could load all data when ListModel.getSize() (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/zul/ListModel.html#getSize()) is called. For example,
For more realist examples, please refer to Small Talks: Handling huge data using ZK.
Notice that if you use one of the default implementations, such as ListModelList (http:/ / www. zkoss.
org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModelList. html#), you don't need to worry about it. The
notification is handled transparently.
For example, (pseudo code)
Once a model is assigned to a component, the component will register itself as a data listener such that any changes
can be updated to UI.
Notice that you shall not update the component (such as listbox) directly. Rather, you shall update to the modal and
then the model shall fire the event to notify the components to update accordingly.
Selection
Interface: Selectable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Selectable. html#)
Implementation: Implemented by AbstractListModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ AbstractListModel. html#)
If your data model also provides the collection of selected elements, you shall also implement Selectable (http:/ /
www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Selectable. html#). When using with a component
supporting the selection (such as Listbox (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Listbox.
html#)), the component will invoke Selectable.isSelected(E) (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/
zul/ ext/ Selectable. html#isSelected(E)) to display the selected elements correctly. In additions, if the end user
selects or deselects an item, Selectable.addSelection(E) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
ext/ Selectable. html#addSelection(E)) and Selectable.removeSelection(java.lang.Object) (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Selectable. html#removeSelection(java. lang. Object)) will be called by the
component to notify the model that the selection is changed. Then, you can update the selection into the persistent
layer (such as database) if necessary.
On the other hand, when the model detects the selection is changed (such as Selectable.addSelection(E) (http:/ /
www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Selectable. html#addSelection(E)) is called), it has to fire
the event, such as ListDataEvent.SELECTION_CHANGED (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/ zul/ event/ ListDataEvent. html#SELECTION_CHANGED) to notify the component. It will cause the
component to correct the selection[1].
All default implementations, including AbstractListModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zul/ AbstractListModel. html#), implement Selectable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
ext/ Selectable. html#). Thus, your implementation generally doesn't need to handle the selection if it extends one of
these classes.
[1] Don't worry. The component is smart enough to prevent the dead loop, even though components invokes addSelection to notify the model
while the model fire the event to notify the component.
Selection Control
since 8.0.0
With the multiple selection function in a data model, you have to implement a class for the SelectionControl (http:/ /
www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ SelectionControl. html#) to tell the data model which items
are selectable and what it will perform a "select all" function with. The following implementation which extends
List Model 123
DefaultSelectionControl (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ AbstractListModel/
DefaultSelectionControl.html#) is a simple example to change "selectable" items.
Please note that if your data model is much larger, you may implement on your own to get rid of the performance
impact.
model.setSelectionControl(new
AbstractListModel.DefaultSelectionControl(model) {
public boolean isSelectable(Object e) {
int i = model.indexOf(e);
return i % 2 == 0;
}
});
Sorting
Interface: Sortable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Sortable. html#)
Implementation: You have to implement it explicitly
To support the sorting, the model must implement Sortable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zul/ ext/ Sortable. html#) too. Thus, when the end user clicks the header to request the sorting, boolean)
Sortable.sort(java.util.Comparator, boolean) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/
Sortable.html#sort(java.util.Comparator,) will be called.
For example, (pseudo code)
Notice that the ascending parameter is used only for reference and you usually don't need it, since the cmpr is
already a comparator capable to sort in the order specified in the ascending parameter.
Version History
Version Date Content
6.0.0 February All selection states are maintained in the list model. And, the application shall not access the component for the selection.
2012 Rather, the application shall invoke Selectable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Selectable.
html#) for retrieving or changing the selection.
6.0.0 February Sortable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Sortable. html#) was introduced and replaced
2012 ListModelExt.
Groups Model 124
Groups Model
• Available for ZK:
•
[1]
Here we describe how to implement a groups model (GroupsModel ). For the concept of component, model and
render, please refer to the Model-driven Display section.
A groups model is used to drive components that support groups of data. The groups of data is a two-level tree of
data: a list of grouped data and each grouped data is a list of elements to display. Here is a live demo [2]. Currently,
both Listbox [1] and Grid [3] support a list of grouped data.
Instead of implementing GroupsModel [1], it is suggested to extend from AbstractGroupsModel [3], or to use one of
the default implementations as following:
[4] [5]
SimpleGroupsModel GroupsModelArray
Usage The grouping is immutable, i.e., re-grouping is not Grouping is based on a comparator (java.util.Comparator)
allowed
Constructor The data must be grouped, i.e., data[0] is the first The data is not grouped, i.e., data[0] is the first element. The constructor requires
group, data[1] the second, etc. a comparator that will be used to group them.
<zk>
<zscript>
String[][] datas = new String[][] {
new String[] { //group 1
// Today
"RE: Bandbox Autocomplete Problem",
"RE: It's not possible to navigate a listbox' ite",
"RE: FileUpload"
},
new String[] { //group 2
// Yesterday
"RE: Opening more than one new browser window",
"RE: SelectedItemConverter Question"
},
new String[] { //group 3
"RE: Times_Series Chart help",
"RE: SelectedItemConverter Question"
}
};
GroupsModel model = new SimpleGroupsModel(datas,
new String[]{"Date: Today", "Date: Yesterday", "Date: Last
Groups Model 125
Week"});
//the 2nd argument is a list of group head
</zscript>
<grid model="${model}">
<columns sizable="true">
<column label="Subject"/>
</columns>
</grid>
</zk>
//Data
Object[][] _foods = new Object[][] { //Note: the order does not matter
new Object[] { "Vegetables", "Asparagus", "Vitamin K", 115, 43},
new Object[] { "Vegetables", "Beets", "Folate", 33, 74},
new Object[] { "Vegetables", "Bell peppers", "Vitamin C", 291, 24},
new Object[] { "Vegetables", "Cauliflower", "Vitamin C", 92, 28},
new Object[] { "Vegetables", "Eggplant", "Dietary Fiber", 10, 27},
new Object[] { "Vegetables", "Onions", "Chromium", 21, 60},
new Object[] { "Vegetables", "Potatoes", "Vitamin C", 26, 132},
new Object[] { "Vegetables", "Spinach", "Vitamin K", 1110, 41},
new Object[] { "Vegetables", "Tomatoes", "Vitamin C", 57, 37},
new Object[] { "Seafood", "Salmon", "Tryptophan", 103, 261},
new Object[] { "Seafood", "Shrimp", "Tryptophan", 103, 112},
Groups Model 127
//GroupsModel
package foo;
public class FoodGroupsModel extends GroupsModelArray {
public FoodGroupsModel(java.util.Comparator cmpr) {
super(_foods, cmpr); //assume we
//cmpr is used to group
}
protected Object createGroupHead(Object[] groupdata, int index, int
col) {
return ((Object[])groupdata[0])[col];
//groupdata is one of groups after GroupsModelArray groups
_foods
///here we pick the first element and use the col-th column as
the group head
}
private static Object[][] _foods = new Object[][] {
...tabular data as shown above
};
};
In additions, we have to implement a comparator to group the data based on the given column as follows.
package foo;
public class FoodComparator implements java.util.Comparator {
int _col;
boolean _asc;
Groups Model 128
Since the data will be displayed in a multiple column, we have to implement a renderer. Here is an example.
If it is not the behavior you want, you could override java.lang.Object[ [13], java.util.Comparator, boolean, int)
GroupsModelArray.sortGroupData(java.lang.Object, java.lang.Object[], java.util.Comparator, boolean, int)]. Of
course, you could extend from AbstractGroupsModel [3] to have total control.
<grid apply="foo.FoodComposer">
<columns menupopup="auto"> <!-- turn on column's menupopup -->
<column label="Category" sort="auto(0)"
sortDirection="ascending"/> <!-- since it is initialized as sorted -->
<column label="Name" sort="auto(1)"/>
<column label="Top Nutrients" sort="auto(2)"/>
<column label="% of Daily" sort="auto(3)"/>
<column label="Calories" sort="auto(4)"/>
</columns>
</grid>
package foo;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zul.*;
public class FoodComposer implements Composer {
public void doAfterCompose(Component comp) throws Exception {
Grid grid = (Grid)comp;
grid.setModel(new GroupsModelArray(_foods, new
ArrayComparator(0, true)));
//Initially, we group data on 1st column in ascending
order
grid.setRowRenderer(new FoodGroupRenderer());
}
}
Groups Model 130
public Food(String cat, String nm, String nutr, int pod, int cal) {
_category = cat;
_name = nm;
_nutrients = nutr;
_percentageOfDaily = pod;
_calories = cal;
}
public String getCategory() {
return _category;
}
public String getName() {
return _name;
}
public String getNutrients() {
return _nutrients;
}
public int getPercentageOfDaily() {
return _percentageOfDaily;
}
public int getCalories() {
return _calories;
}
}
Assume you want to use the value of the field that the user uses to group the data, then you could override
GroupsModelArray [5] as follows.
where
• We use FieldComparator [17] to initialize the groups at the category field.
• We use an object array as the group head that carries the first element of the given group (Food[]), and the
index of the column that causes the grouping. We will use the index later to retrieve the field's value
Groups Model 131
package foo;
import org.zkoss.lang.reflect.Fields;
import org.zkoss.zk.ui.*;
import org.zkoss.zul.*;
public class FoodGroupRenderer implements RowRenderer {
public void render(Row row, java.lang.Object obj, int index) {
if (row instanceof Group) {
Object[] data = (Object[])obj; //prepared by
createGroupHead()
row.appendChild(new Label(getGroupHead(row, (Food)data[0],
(Integer)data[1])));
} else {
Food food = (Food) obj;
row.appendChild(new Label(food.getCategory()));
row.appendChild(new Label(food.getName()));
row.appendChild(new Label(food.getNutrients()));
row.appendChild(new Label(food.getPercentageOfDaily() +
""));
row.appendChild(new Label(food.getCalories() + ""));
}
}
private String getGroupHead(Row row, Food food, int index) {
Column column =
(Column)row.getGrid().getColumns().getChildren().get(index);
String orderBy =
((FieldComparator)column.getSortAscending()).getOrderBy();
int j = orderBy.indexOf("name="),
k = orderBy.indexOf(' ');
try {
return Fields.get(food, orderBy.substring(j+1, k>0 ? k:
orderBy.length())).toString();
} catch (NoSuchMethodException ex) {
throw UiException.Aide.wrap(ex);
}
}
};
The retrieval of the field's value is a bit tricky: since we will use auto(fieldName) to group and sort data for a
given column (see the ZUML content listed below), we could retrieve the field's name by use of
FieldComparator.getOrderBy() [18], which returns something like "name=category ASC". Then, use java.lang.String)
Fields.get(java.lang.Object, java.lang.String) [19] to retrieve it. If the field name is in a compound format, such as
something.yet.another, you could use java.lang.String) Fields.getByCompound(java.lang.Object,
java.lang.String) [20]
Groups Model 132
For 5.0.6 or later, you could use FieldComparator.getRawOrderBy() [21] instead, which returns the field name
you passed to Column.setSort(java.lang.String) [16], i.e., "category".
Column column =
(Column)row.getGrid().getColumns().getChildren().get(index);
String field =
((FieldComparator)column.getSortAscending()).getRawOrderBy();
return Fields.get(food, field).toString();
<grid apply="foo.FoodComposer">
<columns menupopup="auto">
<column label="Category" sort="auto(category)" sortDirection="ascending"/>
<column label="Name" sort="auto(name)"/>
<column label="Top Nutrients" sort="auto(nutrients)"/>
<column label="% of Daily" sort="auto(percentageOfDaily)"/>
<column label="Calories" sort="auto(calories)"/>
</columns>
</grid>
package foo;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zul.*;
public class FoodComposer implements Composer {
Food[] _foods = new Food[] { //assume we have a collection of foods
new Food("Vegetables", "Asparagus", "Vitamin K", 115, 43),
new Food("Vegetables", "Beets", "Folate", 33, 74)
//...more
};
Group Foot
If the groups model supports a foot (such as a summary of all data in the same group), you could return an object to
represent the footer when GroupsModel.getGroupfoot(int) [22] is called (similar to GroupsModel.getGroup(int) [23]
shall return an object representing the group).
[5] [24]
If you use GroupsModelArray , you could override [ , int, int)
GroupsModelArray.createGroupFoot(java.lang.Object[], int, int)]. For example,
Version History
Version Date Content
5.0.6 December 2010 Enhanced the support of tabular data as described in #5.0.6 and Later.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModel. html#
[2] http:/ / www. zkoss. org/ zkdemo/ grid/ grouping
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ AbstractGroupsModel. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ SimpleGroupsModel. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModelArray. html#
[6] http:/ / www. zkoss. org/ javadoc/ 5. 0. 4/ zk/ org/ zkoss/ zul/ ArrayGroupsModel. html
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModelExt. html#
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModelExt. html#group(java. util. Comparator,
[9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModelExt. html#sort(java. util. Comparator,
[10] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupComparator. html#
[11] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Column. html#setSortAscending(java. util. Comparator)
[12] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Column. html#setSortDescending(java. util. Comparator)
[13] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModelArray. html#sortGroupData(java. lang. Object,
[14] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModelArray. html#createGroupHead(java. lang. Object
[15] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ArrayComparator. html#
[16] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Column. html#setSort(java. lang. String)
[17] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ FieldComparator. html#
[18] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ FieldComparator. html#getOrderBy()
[19] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ lang/ reflect/ Fields. html#get(java. lang. Object,
[20] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ lang/ reflect/ Fields. html#getByCompound(java. lang. Object,
[21] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ FieldComparator. html#getRawOrderBy()
[22] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModel. html#getGroupfoot(int)
[23] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModel. html#getGroup(int)
[24] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GroupsModelArray. html#createGroupFoot(java. lang. Object
Tree Model 134
Tree Model
Here we describe how to implement a tree model (TreeModel [1]). For the concepts of component, model and render,
please refer to the Model-driven Display section.
A tree model is used to control how to display a tree-like component, such as Tree [2].
Instead of implementing TreeModel [1] from scratch, it is suggested to extend from AbstractTreeModel [3], which
will handle the data listeners transparently, while it allows the maximal flexibility, such as load-on-demand and
caching.
In addition, if the tree is small enough to be loaded completely, you could use the default implementation,
DefaultTreeModel [4], which uses DefaultTreeNode [5] to construct a tree[6].
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Tree. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ AbstractTreeModel. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeModel. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#
[6] TreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#) is available since 5.0.6. For 5.0.5 or prior,
please use Tree (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Tree. html#), which is similar except it assumes the tree
structure is immutable
By extending from AbstractTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
AbstractTreeModel. html#), you have to implement three methods: int) TreeModel.getChild(java.lang.Object, int)
(http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#getChild(java. lang. Object,),
TreeModel.getChildCount(java.lang.Object) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
TreeModel. html#getChildCount(java. lang. Object)), and TreeModel.isLeaf(java.lang.Object) (http:/ / www. zkoss.
org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#isLeaf(java. lang. Object)). Optionally, you could
implement java.langObject) TreeModel.getIndexOfChild(java.lang.Object, java.langObject) (http:/ / www. zkoss.
Tree Model 135
org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#getIndexOfChild(java. lang. Object,)[1], if you have a
better algorithm than iterating through all children of a given parent. Likewise you could implement
TreeModel.getPath(E) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#getPath(E)) if
you have an efficient mechanism to deduce the sibling index of each ancestor of a node.
Here is a simple example, which generates a four-level tree and each branch has five children:
package foo;
public class FooModel extends AbstractTreeModel<Object> {
public FooModel() {
super("Root");
}
public boolean isLeaf(Object node) {
return getLevel((String)node) >= 4; //at most 4 levels
}
public Object getChild(Object parent, int index) {
return parent + "." + index;
}
public int getChildCount(Object parent) {
return isLeaf(parent) ? 0: 5; //each node has 5 children
}
public int getIndexOfChild(Object parent, Object child) {
String data = (String)child;
int i = data.lastIndexOf('.');
return Integer.parseInt(data.substring(i + 1));
}
private int getLevel(String data) {
for (int i = -1, level = 0;; ++level)
if ((i = data.indexOf('.', i + 1)) < 0)
return level;
}
};
[1] TreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#) is available in 5.0.6 and later.
If you prefer to use TreeNode (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeNode. html#) to
construct the tree dynamically, you could use DefaultTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/ zul/ DefaultTreeModel. html#) and DefaultTreeNode (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zul/ DefaultTreeNode. html#). The use is straightfoward, but it means that the whole tree must be constructed before
having it being displayed.
For example, suppose we want to show up a tree of file information, and the file information is stored as
FileInfo:
package foo;
public class FileInfo {
public final String path;
public final String description;
public FileInfo(String path, String description) {
this.path = path;
this.description = description;
}
}
Then, we could create a tree of file information with DefaultTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/
org/zkoss/zul/DefaultTreeModel.html#) as follows.
package foo;
import org.zkoss.zul.*;
public class FileInfoRenderer implements TreeitemRenderer<DefaultTreeNode<FileInfo>> {
public void render(Treeitem item, DefaultTreeNode<FileInfo> data, int index)
throws Exception {
FileInfo fi = data.getData();
Treerow tr = new Treerow();
item.appendChild(tr);
tr.appendChild(new Treecell(fi.path));
tr.appendChild(new Treecell(fi.description));
}
}
<div apply="foo.FileInfoTreeController">
<tree id="tree">
<treecols>
<treecol label="Path"/>
<treecol label="Description"/>
</treecols>
</tree>
</div>
where we assume you have a controller, foo.FileInfoTreeController, to bind them together. For example,
package foo;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Div;
import org.zkoss.zk.ui.util.GenericForwardComposer;
public class FileInfoTreeController extends GenericForwardComposer<Div> {
Tree Model 138
@Override
public void doAfterCompose(Div div) throws Exception{
super.doAfterCompose(div);
tree.setModel(new DefaultTreeModel(..../*as shown above*/));
tree.setItemRenderer(new FileInfoRenderer());
}
}
Notice that you could manipulate the tree dynamically (such as adding a node with
DefaultTreeNode.add(org.zkoss.zul.TreeNode) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
DefaultTreeNode. html#add(org. zkoss. zul. TreeNode))). The tree shown at the browser will be modified
accordingly.
To demonstrate the example, first we add create, update and delete buttons in the ZUML document:
<tree id="tree">
...
</tree>
<grid>
<auxhead>
</auxhead>
<columns visible="false">
<column />
<column />
</columns>
<rows>
<row>
</row>
<row>
Tree Model 139
index: <intbox id="index" /><button id="create" label="Add to selected parent node" />
</cell>
</row>
</rows>
</grid>
The intbox here is for specifying index to insert before the selected tree item.
Add/Insert
DefaultTreeNode (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#) provides
DefaultTreeNode.add(org.zkoss.zul.TreeNode) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
DefaultTreeNode. html#add(org. zkoss. zul. TreeNode)) and int) DefaultTreeNode.insert(org.zkoss.zul.TreeNode,
int) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#insert(org. zkoss. zul.
TreeNode,) that can manipulate the tree dynamically.
Here we register onClick event to create Button in foo.FileInfoTreeController:
if (selectedTreeNode.isLeaf())
Tree Model 140
selectedTreeNode = selectedTreeNode.getParent();
if (i == null)
selectedTreeNode.add(newNode);
else
selectedTreeNode.insert(newNode, i);
}
}
}
If index is not specified, we add a new node using DefaultTreeNode.add(org.zkoss.zul.TreeNode) (http:/ / www.
zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#add(org. zkoss. zul. TreeNode)) at the bottom
of the parent node by default, or we can also use int) DefaultTreeNode.insert(org.zkoss.zul.TreeNode, int) (http:/ /
www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#insert(org. zkoss. zul. TreeNode,) to
insert a new node before the specified index.
Update/Delete
DefaultTreeNode (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#) provides
DefaultTreeNode.setData(java.lang.Object) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
DefaultTreeNode. html#setData(java. lang. Object)) which can update selected tree items and
DefaultTreeNode.removeFromParent() (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
DefaultTreeNode.html#removeFromParent()) that can delete the selected tree item from its parent node.
Here we register onClick event to update and delete Button in foo.FileInfoTreeController:
Sorting
Interface: Sortable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Sortable. html#)
Implementation: You have to implement it explicitly
To support the sorting, the model must implement Sortable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zul/ ext/ Sortable. html#) too. Thus, when the end user clicks the header to request the sorting, boolean)
Sortable.sort(java.util.Comparator, boolean) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/
Sortable.html#sort(java.util.Comparator,) will be called.
For example, (pseudo code)
Notice that the ascending parameter is used only for reference and you usually don't need it, since the cmpr is
already a comparator capable to sort in the order specified in the ascending parameter.
Tree Model 142
Selection
Interface: TreeSelectableModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ TreeSelectableModel. html#)
Implementation: Implemented by AbstractTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ AbstractTreeModel. html#)
If your data model also provides the collection of selected elements, you shall also implement TreeSelectableModel
(http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ TreeSelectableModel. html#). When using with a
component supporting the selection (such as Tree (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Tree.
html#)), the component will invoke [ (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/
TreeSelectableModel. html#isPathSelected(int)) TreeSelectableModel.isPathSelected(int[])] to display the selected
elements correctly. In additions, if the end user selects or deselects an item, [ (http:/ / www. zkoss. org/ javadoc/
latest/ zk/ org/ zkoss/ zul/ ext/ TreeSelectableModel. html#addSelectionPath(int))
TreeSelectableModel.addSelectionPath(int[])] and [ (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
ext/TreeSelectableModel.html#removeSelectionPath(int)) TreeSelectableModel.removeSelectionPath(int[])] will be
called by the component to notify the model that the selection is changed. Then, you can update the selection into the
persistent layer (such as database) if necessary.
On the other hand, when the model detects the selection is changed (such as [ (http:/ / www. zkoss. org/ javadoc/
latest/ zk/ org/ zkoss/ zul/ ext/ TreeSelectableModel. html#addSelectionPath(int))
TreeSelectableModel.addSelectionPath(int[])] is called), it has to fire the event, such as
TreeDataEvent.SELECTION_CHANGED (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ event/
TreeDataEvent.html#SELECTION_CHANGED) to notify the component. It will cause the component to correct the
selection[1].
All default implementations, including AbstractTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zul/ AbstractTreeModel. html#) and DefaultTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
DefaultTreeModel. html#) implements TreeSelectableModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/ zul/ ext/ TreeSelectableModel. html#). Thus, your implementation generally doesn't have to implement it
explicitly.
It is important to note that, once a tree is assigned with a tree model, the application shall not manipulate the tree
items and/or change the selection of the tree directly. Rather, the application shall access only the list model to add,
remove and select data elements. Let the model notify the component what have been changed.
[1] Don't worry. The component is smart enough to prevent the dead loop, even though components invokes addSelectionPath() to
notify the model while the model fire the event to notify the component.
Implementation: Implemented by AbstractTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ AbstractTreeModel. html#)
By default, all tree nodes are closed. To control whether to open a tree node, you could implement
TreeOpenableModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ TreeOpenableModel. html#).
More importantly, to open a tree node, the application shall access the model's TreeOpenableModel (http:/ / www.
zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ TreeOpenableModel. html#) API, rather than accessing Treeitem
(http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/Treeitem.html#) directly.
All default implementations, including AbstractTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zul/ AbstractTreeModel. html#) and DefaultTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
DefaultTreeModel. html#) implements TreeOpenableModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zul/ext/TreeOpenableModel.html#). Thus, your implementation generally doesn't have to implement it explicitly.
Tree Model 143
Note: If your tree model contains a lot of nodes, please also implement TreeModel.getPath(E) (http:/ / www. zkoss.
org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeModel. html#getPath(E)) to get the better performance, by default it is
implemented by Depth-first search (http:/ / en. wikipedia. org/ wiki/ Depth-first_search) to get the path from a tree
node.
Leaf Node
The DefaultTreeNode (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#) has 2
constructors: DefaultTreeNode(data) and DefaultTreeNode(data, children). If you want to
display a leaf node, you should use DefaultTreeNode(data) constructor, otherwise even if you provide a
zero size of a list for DefaultTreeNode(data, children) constructor, ZK tree will still treat the node as a
non-leaf node, and display it as an empty folder.
DefaultTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeModel. html#) provides
a boolean argument to its constructor for configuring to treat the zero size of children node as a leaf node.
Version History
Version Date Content
5.0.6 January TreeNode (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeNode. html#), DefaultTreeNode (http:/ / www.
2011 zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#) and DefaultTreeModel (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeModel. html#) were intrdocued.
6.0.0 February TreeSelectableModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ TreeSelectableModel. html#) and
2012 TreeOpenableModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ TreeOpenableModel. html#) were
introduced to replace Selectable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Selectable. html#) and
Openable (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Openable. html#).
5.0.12 / October DefaultTreeModel (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeModel. html#) add a new
6.0.3 / 2012 constructor for configuring to treat the zero size of children node as a leaf node.
6.5.1
Chart Model 144
Chart Model
[1]
Here we describe how to implement a chart model (ChartModel ). For the concept of component, model and
render, please refer to the Model-driven Display section.
Depending on the type of the chart you want, you could implement one of PieModel [1], XYModel [2], GanttModel
[3]
, HiLoModel [4], etc. In addition, there are the default implementations for them you could use directly, such as
SimplePieModel [4], SimpleXYModel [5], etc.
For example, we could have a composer as follows.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ PieModel. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ XYModel. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ GanttModel. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ HiLoModel. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ SimpleXYModel. html#
Matrix Model 145
Matrix Model
• Available for ZK:
•
[1]
Here we describe how to implement a matrix model (MatrixModel ). For the concept of component, model and
renderer, please refer to the Model-driven Display section.
By default, ZK does not provide a built-in model implementation class for MatrixModel because Biglistbox is
designed to handle unlimited data set, therefore, there is no need to handle model data in memory. This usage is
application-dependent and varies from case to case. However, you can extend your own implementation from the
AbstractListModel [7] skeleton class.
To implement a MatrixModel needs to consider the performance issue that handles a huge data set in memory with
Java Collection Framework. The issue is when using the default implementation of Java Collection Framework as it
goes through every entry to gather the value of hashCode when searching the key in Map/Set or to check every
entry for equals and toString functions. This implementation method greatly reduces the performance of Biglistbox,
therefore, to use the Biglistbox component with MatrixModel, we need to implement a clever and simple List for
traversing huge data sets.
FakerKeyList
In this example, we create a FakerKeyList to implement the List interface for MatrixModel to handle the partial big
data in memory.
@Override
public T get(int index) {
// if changed, returns the changed value
Object val = _updateCache.get(String.valueOf(index));
if (val != null)
return (T) val;
return (T) _fn.apply(index);
}
@Override
public int hashCode() {
return _key.hashCode();
}
Matrix Model 146
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof FakerKeyList) {
return _key.equals(((FakerKeyList)(obj))._key);
}
return false;
}
@Override
public String toString() {
return _key;
}
// omitted...
}
As you can see, we use a key string as the key for toString, hashCode, and equals methods to speed up searching
time. Fun class on the other hand, is a handy class to render the model data for this example.
FakerMatrixModel
In this example, we create a FakerMatrixModel to implement MatrixModel. Following is the fragment code:
public class FakerMatrixModel<Head extends List, Row extends List, Cell, Header> extends
AbstractListModel<Row> implements MatrixModel<Row, Head, Cell, Header>, Sortable {
// omitted...
@Override
public Row getElementAt(int index) {
final int rowIndex = _sortDir ? index : getSize() - index -
1; // handle the sorting
final String key = String.valueOf(rowIndex);
List<String> value = _rowCache.get(key);
if (value == null) {
value = new FakerKeyList<String>(_colSize, rowIndex, new
Fun() {
@Override
public Object apply(int index) {
return "y = " + rowIndex;
}});
_rowCache.put(key, value);
}
return (Row) value;
Matrix Model 147
// omitted...
}
MatrixModel is extended from the ListModel interface and uses the getElementAt(int) method to receive row data
from the FakerKeyList object that implements the List interface.
Sortable Model
The MatrixModel can also support Sortable [2] interface. In your implementor class you can just implement the
Sortable [2] interface and provide boolean) Sortable.sort(java.util.Comparator, boolean) [3] and
Sortable.getSortDirection(java.util.Comparator) [4] methods.
For example,
@Override
public String getSortDirection(Comparator cmpr) {
if (Objects.equals(_sorting, cmpr))
return _sortDir ? "ascending" : "descending";
return "natural";
}
As you can see, we fire a data change event with ListDataEvent.STRUCTURE_CHANGED attribute to notify the
component that model data has been changed.
Resource
All of the example above can be found here - Github's FakerMatrixModel source code [5]
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ zul/ MatrixModel. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Sortable. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Sortable. html#sort(java. util. Comparator,
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ Sortable. html#getSortDirection(java. util. Comparator)
[5] https:/ / github. com/ zkoss/ zk/ blob/ master/ zktest/ src/ org/ zkoss/ zktest/ test2/ big/ FakerMatrixModel. java
View
The view is the UI of an application. It totally depends on the application's requirements.
As described in the Model section, many ZK components could operate based on models, such as Listbox [1]. There
are two approaches to customize the rendering of each item in model: Template and Renderer.
A template is a fragment of the ZUML document that defines how to render each item in ZUML. On the other hand,
a renderer is a Java class that renders each item in Java.
Template
A template is a ZUML fragment that defines how to create components. A template is enclosed with the template
element as shown below.
<window>
<template name="foo">
<textbox/>
<grid model=${data}>
<columns/>
<template name="model"> <!-- nested template -->
<row>Name: <textbox value="${each.name}"/></row>
</template>
</grid>
</template>
...
A template can contain any ZUML elements you want, including other templates. When a ZUML document is
interpreted, a template won't be interpreted immediately. Rather, it will be encapsulated as an instance of Template
[1]
, and be associated to a component. Then, the component or a tool can create the components repeatedly based on
the template by invoking org.zkoss.zk.ui.Component, org.zkoss.xel.VariableResolver,
org.zkoss.zk.ui.util.Composer) Template.create(org.zkoss.zk.ui.Component, org.zkoss.zk.ui.Component,
[2]
org.zkoss.xel.VariableResolver, org.zkoss.zk.ui.util.Composer) .
A component can be assigned with multiple templates. Each of them is identified by the name attribute.
<div>
<template name="t1">
<grid model="${foo}"/>
</template>
<template name="t2">
<listbox model="${foo}"/>
Template 149
</template>
How a template is used depends on the component it associates with and the tools you use. Currently, all components
that support the concept of model allow you to specify a template to control how to render each item. In the
following sections, we discuss them in details. If you'd like to know how to use templates manually in Java, please
refer to the UI Patterns: Templates section.
Notice that please read the Listbox Template section first, even though you're rendering other kind of UI. It
described the common concepts and tricks of using templates.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Template. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Template. html#create(org. zkoss. zk. ui. Component,
Listbox Template
The template used to control the rendering of each item must be named model and declared right inside the
listbox element. For example,
<window apply="foo.FruitProvider">
<listbox model="${$composer.fruits}">
<listhead>
<listheader label="Name" sort="auto"/>
<listheader label="Weight" sort="auto"/>
</listhead>
<template name="model">
<listitem>
<listcell label="${each[0]}"/>
<listcell label="${each[1]}"/>
</listitem>
</template>
</listbox>
</window>
The template's name is important because users are allowed to associate multiple templates to one component, and
listbox's default renderer looks only for the template called model.
When the template is rendered, a variable called each is assigned with the data being rendered. Thus, you could
retrieve the information to render with EL expressions, such as ${each[0]}, if it is an array, or ${each.name},
if it is a bean with a getter called name.
In this example, we assume the $composer.fruits expression returns a two-dimensional array[1], and is
provided by the foo.FruitProvider composer such as follows[2].
});
[1] Of course, it can be anything you like. Just make sure it matches the EL expressions specified in the template.
[2] Here we use UNIQ-javadoc-0-f6421cf86b2cc1a3-QINU for simplicity. There are several ways to implement a composer, such as wiring a
Spring-managed bean. For more information, please refer to the Composer section
Component's Value
By default, the data used to render a component will be stored to the component's value property automatically. For
listitem, it is Listitem.setValue(T) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Listitem.
html#setValue(T)). Thus, you retrieve it back easily by invoking Listitem.getValue() (http:/ / www. zkoss. org/
javadoc/latest/zk/org/zkoss/zul/Listitem.html#getValue()).
Of course, if you prefer to store other values, you can simply specify value="${whatever}" to the
listitem element in the template.
To work around, you have to store the value in, say, component's custom attributes ( Component.getAttributes()
(http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/Component.html#getAttributes()). For example,
Listbox Template 151
Nested Listboxes
The template can be applied recursively. Here is an example of a listbox-in-listbox:
<zk>
<zscript><![CDATA[
ListModel quarters = new ListModelArray(new String[] {"Q1", "Q2",
"Q3", "Q4"});
Map months = new HashMap();
months.put("Q1", new ListModelArray(new String[] {"Jan", "Feb",
"Mar"}));
months.put("Q2", new ListModelArray(new String[] {"Apr", "May",
"Jun"}));
months.put("Q3", new ListModelArray(new String[] {"Jul", "Aug",
"Sep"}));
months.put("Q4", new ListModelArray(new String[] {"Oct", "Nov",
"Dec"}));
ListModel qs = (quarters);
]]></zscript>
<listbox model="${quarters}">
<template name="model">
<listitem>
<listcell>${each}</listcell>
<listcell>
<listbox model="${months[each]}">
<template name="model">
<listitem label="${each}"/>
</template>
</listbox>
</listcell>
</listitem>
</template>
</listbox>
</zk>
Listbox Template 152
<template name="model">
<listitem>
<listcell>
<listbox model="${months[each]}">
<template name="model">
<listitem>
<listcell>${self.parent.parent.parent.parent.parent.value}</listcell>
<listcell>${each}</listcell>
</listitem>
</template>
</listbox>
</listcell>
</listitem>
</template>
</listbox>
If the component tree is deep, It is tedious and somehow error prone. Alternatively, you can store the information
into a custom attribute and then retrieve it later, as shown at line 4 and 10 below.
Listbox Template 153
<listbox model="${quarters}">
<template name="model">
<listitem>
<custom-attributes master="${each}"/>
<listcell>
<listbox model="${months[each]}">
<template name="model">
<listitem>
<listcell label="${forEachStatus.index}" />
<listcell>${master}</listcell>
<listcell>${each}</listcell>
</listitem>
</template>
</listbox>
</listcell>
</listitem>
</template>
</listbox>
[1] On the other hand, it returns the previous iteration information when using with the forEach attribute
<listbox model="${fooGroupsModel}">
<template name="model:group">
<listgroup open="${groupingInfo.open}" label="${each}"/>
</template>
<template name="model">
<listitem>....</listitem>
</template>
<template name="model:groupfoot">
<listgroupfoot>....</listgroupfoot>
</template>
<listbox>
• Note the groupingInfo is used to get the information of the grouping data. Please refer to GroupingInfo (http://
www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/ext/GroupingInfo.html#)
Listbox Template 154
Version History
Version Date Content
Grid Template
Similar to Listbox, you can define a customer rendering with a template for a grid:
<grid model="${books}">
<columns>
<column label="ISBN" sort="auto"/>
<column label="Name" sort="auto"/>
<column label="Description"/>
</columns>
<template name="model">
<row>
<label value="${each.isbn}"/>
<label value="${each.name}"/>
<label value="${each.description}"/>
</row>
</template>
</grid>
where books is assumed as an instance of ListModel [2] that contains a list of the Book instances while each Book
instances has at least three getter methods: getIsbn, getName and getDescription.
Notice that the template named model must be associated with the grid, i.e., it must be a direct child element of the
grid element as shown above. A common mistake is to put it under the rows element. Remember the template is a
ZUML fragment telling the grid how to render a row, and the template itself is not a component.
<grid mode="${fooGroupsModel}">
<template name="model:group">
<group open="${groupingInfo.open}">....</group>
</template>
<template name="model">
<row>....</row>
</template>
<template name="model:groupfoot">
<groupfoot>....</groupfoot>
</template>
Grid Template 155
<grid>
• Note the groupingInfo is used to get the information of the grouping data. Please refer to GroupingInfo [1]
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ext/ GroupingInfo. html#
Tree Template
Similar to Listbox, you can also define a customer rendering with a template for a tree:
<tree model="${files}">
<treecols>
<treecol label="Path"/>
<treecol label="Description"/>
</treecols>
<template name="model">
<treeitem context="menupopup">
<treerow>
<treecell label="${each.data.path}"/>
<treecell label="${each.data.description}"/>
</treerow>
</treeitem>
</template>
</tree>
assume files is an instance of DefaultTreeModel [4]. Notice since DefaultTreeModel [4] is used, each
references an instance of DefaultTreeNode [5]. Thus, to retrieve the real data, use DefaultTreeNode.getData() [1]
Tree Template 156
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ DefaultTreeNode. html#getData()
Combobox Template
Similar to Listbox, you can render a combobox with a template:
<combobox model="${infos}">
<template name="model">
<comboitem label="${each[0]}: ${each[1]}"/>
</template>
</combobox>
where we assume there is a list model (ListModel [2]) called infos such as:
Version History
Version Date Content
Selectbox Template
Similar to Listbox, you can render a selectbox with a template. However, notice that, unlike other components,
selectbox doesn't allow any child component, so you have to render each item as a string. For example,
where we assume there is a list model (ListModel [2]) called users such as:
Version History
Version Date Content
Biglistbox Template
Similar to Listbox, you can render a biglistbox with a template. However, notice that, unlike other components,
biglistbox doesn't allow any child component, so you have to render each item as a string. For example,
<biglistbox hflex="1" vflex="1" model="${data}" >
<template name="heads">
<html><![CDATA[
]]></html>
</template>
<template name="rows">
<html><![CDATA[
]]></html>
</template>
</biglistbox>
As you can see, we utilize two attributes - rowIndex & colIndex from the matrixInfo object to receive the current
index during template rendering phase.
where we assume there is a matrix model (FakerMatrixModel [5]) called data such as:
Version History
Version Date Content
Chosenbox Template
Similar to Listbox, you can render a chosenbox with a template. However, notice that, unlike other components,
chosenbox doesn't allow any child component, so you have to render each item as a string. For example,
where we assume there is a list model (ListModel [2]) called users such as:
Version History
Version Date Content
Tabbox Template
• Available for ZK:
•
[Since 7.0.0]
The template used to control the rendering of each tab and tabpanel must be named model:tab and
model:tabpanel and declared right inside the tabbox element. For example,
<div apply="foo.FruitProvider">
<tabbox id="mytab" model="${$composer.fruits}">
<template name="model:tab">
<tab iconSclass="z-icon-user">
${each[0]}
</tab>
</template>
<template name="model:tabpanel">
<tabpanel>
<div style="background:green">
${each[1]}
</div>
</tabpanel>
</template>
</tabbox>
</div>
The template's name is important because users are allowed to associate multiple templates to one component, and
tabbox's default renderer looks only for the template called model:tab and model:tabpanel.
When the template is rendered, a variable called each is assigned with the data being rendered. Thus, you could
retrieve the information to render with EL expressions, such as ${each[0]}, if it is an array, or ${each.name},
if it is a bean with a getter called name.
In this example, we assume the $composer.fruits expression returns a two-dimensional array[1], and is
provided by the foo.FruitProvider composer such as follows[2].
<zscript><![CDATA[
public class FruitProvider extends
org.zkoss.zk.ui.select.SelectorComposer {
public ListModelArray fruits = new ListModelArray(
new String[][] {
{"Apple", "10kg"},
{"Orange", "20kg"},
{"Mango", "12kg"}
});
]]></zscript>
[1] Of course, it can be anything you like. Just make sure it matches the EL expressions specified in the template.
[2] Here we use UNIQ-javadoc-0-f6421cf86b2cc1a3-QINU for simplicity. There are several ways to implement a composer, such as wiring a
Spring-managed bean. For more information, please refer to the Composer section
Component's Value
By default, the data used to render a component will be stored to the component's value property automatically. For
tab, it is Tab.setValue(T) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Tab. html#setValue(T)).
Thus, you retrieve it back easily by invoking Tab.getValue() (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/zul/Tab.html#getValue()).
Of course, if you prefer to store other values, you can simply specify value="${whatever}" to the tab
element in the template.
<div apply="foo.FruitProvider">
<tabbox id="mytab" model="${$composer.fruits}">
<template name="model:tab">
<tab label="${arg.foo}"/> <!-- Wrong! it is always empty -->
</template>
<template name="model:tabpanel">
<tabpanel>
${each[1]}
</tabpanel>
</template>
</tabbox>
</div>
To work around, you have to store the value in, say, component's custom attributes ( Component.getAttributes()
(http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/Component.html#getAttributes()). For example,
<div apply="foo.FruitProvider">
<tabbox id="mytab" model="${$composer.fruits}">
Tabbox Template 161
Version History
Version Date Content
7.0.0 November 2013 Tabbox support ListModel (http:/ / tracker. zkoss. org/ browse/ ZK-2002)
Renderer
A renderer is a Java class that is used to render the items specified in a data model[1]. The implementation of a
renderer depends on the component. For example, the display of Listbox [1] can be customized by an implementation
of ListitemRenderer [2], and Grid [3] by RowRenderer [3][4].
[1] If you prefer to define the rendering of each item in the ZUML document, you could use templates instead.
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListitemRenderer. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ RowRenderer. html#
[4] The same model usually can be shared by components having the same logic model. For example,
UNIQ-javadoc-0-f6421cf86b2cc1a3-QINU can be used in both Listbox (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/
Listbox. html#) and ListitemRenderer (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListitemRenderer. html#). However, a
renderer is usually specific to a particular component.
Listbox Renderer 162
Listbox Renderer
[2]
Here we describe how to implement a custom renderer for a listbox (ListitemRenderer ). For the concepts about
component, model and renderer, please refer to the Model-driven Display section.
When a listbox (Listbox [1]) is assigned with a model, a default renderer is assigned too. The default renderer will
assume that each list item has only one column, and it converts the data into a string directly[1]. If you want to
display multiple columns or retrieve a particular field of the data, you have to implement ListitemRenderer [2] to
handle the rendering.
For example,
[1] If the listbox is assigned a template called model, then the template will be used to render the listbox. For more information, please refer
to the Listbox Template section.
Version History
Version Date Content
Grid Renderer
When a grid (Grid [3]) is assigned with a model, a default renderer is assigned too[1]. The default renderer will
assume that each row has only one column, and it converts the data into a string directly[2]. If you want to display
multiple columns or retrieve a particular field of the data, you have to implement RowRenderer [3] to handle the
rendering.
For example,
[1] For the concept about component, model and renderer, please refer to the Model-driven Display section.
[2] If the grid is assigned a template called model, then the template will be used to render the grid. For more information, please refer to the
Grid Template section.
Version History
Version Date Content
Tree Renderer
When a tree (Tree [2]) is assigned with a model, a default renderer is assigned too[1]. The default renderer will
assume that each tree item has only one column, and it converts the data into a string directly[2]. If you want to
display multiple columns or retrieve a particular field of the data, you have to implement TreeitemRenderer [3] to
handle the rendering.
For example,
[1] For the concept about component, model and renderer, please refer to the Model-driven Display section.
[2] If the tree is assigned a template called model, then the template will be used to render the tree. For more information, please refer to the
Tree Template section.
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TreeitemRenderer. html#
Version History
Version Date Content
Combobox Renderer
When a combobox (Combobox [2]) is assigned with a model, a default renderer is assigned too[1]. The default
renderer will assume that the combobox displays the data as a string[2]. If you want to display more sophisticated
information or retrieve a particular field of the data, you have to implement ComboitemRenderer [3] to handle the
rendering.
For example,
[1] For the concept about component, model and renderer, please refer to the Model-driven Display section.
[2] If the tree is assigned a template called model, then the template will be used to render the tree. For more information, please refer to the
Tree Template section.
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ComboitemRenderer. html#
Version History
Version Date Content
Selectbox Renderer
The implementation of a custom renderer for a Selectbox (ItemRenderer [1]) is straightforward[2]:
Then, if we have a list model (ListModel [2]) called users, and an instance of a custom renderer called
userRenderer, then we can put them together in a ZUML document as follows:
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ItemRenderer. html#
[2] For the concept about component, model and renderer, please refer to the Model-driven Display section.
Version History
Version Date Content
Biglistbox Renderer
The implementation of a custom renderer for a Biglistbox (MatrixRenderer [1]) is straightforward[2]:
@Override
public String renderCell(Component owner, List<String> data,
int rowIndex, int colIndex) throws Exception {
String d = data.get(colIndex);
d = d.replace("ZK", "<span class='red' title='ZK'>ZK</span>")
.replace("Hello", "<span class='blue' title='Hello'>Hello</span>");
return "<div class='images_" + (colIndex%28) + "' title='x=" +
colIndex + ",y=" + rowIndex + "'>" + d + "</div>";
}
@Override
public String renderHeader(Component owner, List<String> data,
int rowIndex, int colIndex) throws Exception {
return "<div class='images_" + (colIndex % 28) + "' title='"
+ images[colIndex % 28] + "'>" + data.get(colIndex)
+ "</div>";
}
}
Then, if we have a list model (MatrixModel [1]) called data, and an instance of a custom renderer called
dataRenderer, then we can put them together in a ZUML document as follows:
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ zul/ MatrixRenderer. html#
[2] For the concept about component, model and renderer, please refer to the Model-driven Display section.
Version History
Version Date Content
Chosenbox Renderer
The implementation of a custom renderer for a Chosenbox (ItemRenderer [1]) is straightforward[1]:
Then, if we have a list model (ListModel [2]) called users, and an instance of a custom renderer called
userRenderer, then we can put them together in a ZUML document as follows:
[1] For the concept about component, model and renderer, please refer to the Model-driven Display section.
Version History
Version Date Content
Tabbox Renderer
• Available for ZK:
•
[Since 7.0.0]
[1]
Here we describe how to implement a custom renderer for a tabbox (TabboxRenderer ). For the concepts about
component, model and renderer, please refer to the Model-driven Display section.
When a tabbox (Tabbox [1]) is assigned with a model, a default renderer is assigned too. The default renderer will
assume that each tab has only one tabpanel, and it converts the data into a string directly[2]. If you want to display a
rich tabpanel or retrieve a particular field of the data, you have to implement TabboxRenderer [1] to handle the
rendering.
For example,
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ TabboxRenderer. html#
[2] If the tabbox is assigned a template called model:tab and model:tabpanel, then the template will be used to render the tabbox.
For more information, please refer to the Tabbox Template section.
Version History
Version Date Content
7.0.0 November 2013 Tabbox support ListModel (http:/ / tracker. zkoss. org/ browse/ ZK-2002)
Annotations
An annotation is a special form of syntactic metadata that can be added to components. The definitions, properties
and components themselves may be annotated. The annotations can be retrieved at the run time.
The annotations have no direct effect on the operation of the components. Rather, they are mainly used for UI
designers to annotate metadata, such that it controls how a tool or a utility shall do at run-time. The content and
meanings of annotations totally depend on the tools or the utilities the developer uses. For example, ZK Bind
examines annotations to know how to load and store the value of a component.
Annotate in ZUML
Annotations can be applied to the declarations of components and properties in ZUML pages.
Annotate Properties
To annotate a property, you could specify an annotation expression as the value of the property. In other words, if the
value of the property is an annotation expression, it is considered as an annotation for the property, rather than a
value to be assigned.
The format of an annotation expression:
@annotation-name ()
@annotation-name ( attr-name1 = attr-value1, attr-name2 = attr-value2 )
@annotation-name ( attr-name1 = { attr-value1-1, attr-value1-2 }, attr-name2 = attr-value2 )
As shown, an annotation consists of an annotation name and any number of attributes, and an attribute consists of an
attribute name and an attribute value. The name of an annotation must start with a letter ('a' - 'z' or 'A' - 'Z'), an
underscore ('_'), or a dollar sign ('$').
If an attribute has multiple values, these values have to be enclosed with the curly braces (as shown in the third
format).
For example,
<listitem label="@bind(datasource='author',value='selected')"/>
where an annotation called bind is annotated to the label property, and the bind annotation has two
attributes: datasource and value.
Annotate in ZUML 170
If the attribute name is not specified, the name is assumed to be value. For example, the following two statements
are equivalent:
<textbox value="@bind(vm.p1.firstName)"/>
<textbox value="@bind(value=vm.p1.firstName)"/>
where it annotates the value property with an annotation named save, and the annotation has two attributes:
value and before. The value of the before attribute is a two-element array: 'cmd1' and 'cmd2'. Notice
that the quotations, ' and ", will be preserved, so they will be retrieved exactly the same as they are specified in the
ZUML document.
To annotate the same property with multiple annotations, you could specify them one-by-one and separate them with
a space, as shown below.
<textbox value="@bind(vm.value1) @validator('validator1')" errorMessage="@bind(vm.lastMessage1)" />
In additions, you could annotate with multiple annotations that have the same name. For example,
Annotate Components
To annotate a component, you could specify an annotation expression in a specific attribute called self as shown
below.
where self is a keyword to denote the annotation which is used to annotate the component declaration, rather than
any property.
Then, the textbox's value property will be assigned with a value, "a property's value", and an annotation,
@save(vm.user).
Annotate in ZUML 171
Then, @value() will be considered as a value rather an annotation, and assigned to the textbox's value property
directly.
Version History
Version Date Content
6.0.0 December The new syntax was introduced. For ZK 5's syntax, please refer to ZK 5's Developer's Reference. Though not
2011 recommended, it is OK to use ZK 5's syntax in ZK 6.
Annotate in Java
You could annotate a component or a property in Java by the use of java.lang.String, java.util.Map)
ComponentCtrl.addAnnotation(java.lang.String, java.lang.String, java.util.Map) [1].
For example,
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ ComponentCtrl. html#addAnnotation(java. lang. String,
Retrieve Annotations 172
Retrieve Annotations
The annotations can be retrieved back at the run-time. They are designed to be used by tools or utilities, such as the
data-binding manager, rather than applications. In other words, applications annotate a ZUML page to tell the tools
how to handle components for a particular purpose.
The following is an example to dump all annotations of a component:
Version History
Version Date Content
<component>
<component-name>bandbox</component-name>
<extends>bandbox</extends>
<annotation>
<annotation-name>ZKBIND</annotation-name>
<property-name>value</property-name>
<attribute>
<attribute-name>ACCESS</attribute-name>
<attribute-value>both</attribute-value>
</attribute>
<attribute>
<attribute-name>SAVE_EVENT</attribute-name>
<attribute-value>onChange</attribute-value>
</attribute>
<attribute>
Annotate Component Definitions 173
<attribute-name>LOAD_REPLACEMENT</attribute-name>
<attribute-value>rawValue</attribute-value>
</attribute>
<attribute>
<attribute-name>LOAD_TYPE</attribute-name>
<attribute-value>java.lang.String</attribute-value>
</attribute>
</annotation>
<annotation>
<annotation-name>ZKBIND</annotation-name>
<property-name>open</property-name>
<attribute>
<attribute-name>ACCESS</attribute-name>
<attribute-value>both</attribute-value>
</attribute>
<attribute>
<attribute-name>SAVE_EVENT</attribute-name>
<attribute-value>onOpen</attribute-value>
</attribute>
</annotation>
</component>
Version History
Version Date Content
UI Patterns 174
UI Patterns
This section describes feature-specific UI handling topics. For introductory concepts, please refer to the UI
Composing section. For detailed information of individual components, please refer to ZK Component Reference.
Responsive Design
The Responsive Design [1] in ZK separates the following three sections, for more features in tablet's devices, please
refer to Component Reference.
Fluid Layouts
[Since 5.0]
You can adjust the component size using either vflex or hflex instead of giving components a fixed height and/or
width in pixels.
For example,
<hlayout vflex="1">
<window title="Column 25%" vflex="1" hflex="1" sclass="column1" border="normal">
toffee candy canes cheesecake gummies apple pie. Pie
cupcake cheesecake sugar plum tart donut
bear claw caramels. Sesame snaps candy candy faworki
sesame snaps chocolate wypas cheesecake.
Cupcake cupcake chupa chups dragée bonbon cotton
Responsive Design 175
candy pudding.
</window>
<window title="Column 25%" vflex="1" hflex="1" sclass="column2" border="normal">
toffee candy canes cheesecake gummies apple pie. Pie
cupcake cheesecake sugar plum tart donut
bear claw caramels. Sesame snaps candy candy faworki
sesame snaps chocolate wypas cheesecake.
Cupcake cupcake chupa chups dragée bonbon cotton
candy pudding.
</window>
<window title="Column 50%" vflex="1" hflex="2" sclass="column3" border="normal">
toffee candy canes cheesecake gummies apple pie. Pie
cupcake cheesecake sugar plum tart donut
bear claw caramels. Sesame snaps candy candy faworki
sesame snaps chocolate wypas cheesecake.
Cupcake cupcake chupa chups dragée bonbon cotton
candy pudding.
</window>
</hlayout>
Adaptive Layouts
[3]
[CSS 3 only ]
The adaptive layout is more advantageous than Fluid Layouts, the problem we met in the fluid layout is that its
content can only change to the screen's size, but the layout may break if the screen is not big enough. The adaptive
layout can solve this by using CSS 3 Media Query [4].
For example,
Responsive Design 176
height: 33%;
display: block;
}
}
</style>
<hlayout vflex="1">
<window title="Column 25%" height="100%" sclass="column1" border="normal">
toffee candy canes cheesecake gummies apple pie. Pie
cupcake cheesecake sugar plum tart donut
bear claw caramels. Sesame snaps candy candy faworki sesame
snaps chocolate wypas cheesecake.
Cupcake cupcake chupa chups dragée bonbon cotton candy
pudding.
</window>
<window title="Column 25%" height="100%" sclass="column2" border="normal">
toffee candy canes cheesecake gummies apple pie. Pie
cupcake cheesecake sugar plum tart donut
bear claw caramels. Sesame snaps candy candy faworki sesame
snaps chocolate wypas cheesecake.
Cupcake cupcake chupa chups dragée bonbon cotton candy
pudding.
</window>
<window title="Column 50%" height="100%" sclass="column3" border="normal">
toffee candy canes cheesecake gummies apple pie. Pie
cupcake cheesecake sugar plum tart donut
bear claw caramels. Sesame snaps candy candy faworki sesame
snaps chocolate wypas cheesecake.
Cupcake cupcake chupa chups dragée bonbon cotton candy
pudding.
</window>
</hlayout>
</zk>
As you can see, we remove the vflex and hflex for the Window component and replace it with a pure CSS style and
some condition statements with the @media query to switch the layout to fit the screen size. max-width: 1024px for
ipad or tablet devices and max-width: 750px for iphone or smartphones. But those changes are only client effects,
how ZK developer can do in server side if the orientation change? or how many component's stylings need to be
scaled when displaying in touch devices? These answers can be found in the following section.
The above example code can be downloaded here - Github [5].
Responsive Design 178
In ZK 6.5, we refined and polished all components so that they perform seamlessly whether they are on a PC's
browser or a Tablet device. In some of the use cases the default styling is not satisfied for user to adjust the layout
for different devices and screen sizes, therefore we can employ the ClientInfoEvent to detect whether the browser's
orientation change, and then switch some components' orientation to conform that.
For example,
<zscript><![CDATA[
if ("portrait".equals(evt.getOrientation())) {
main.setWidth("100%");
sv.setVisible(false);
Clients.resize(content);
} else {
if (!execution.isBrowser("mobile"))
main.setWidth("80%");
sv.setVisible(true);
Clients.resize(content);
}
Responsive Design 179
]]></zscript>
vflex="1" onClientInfo="doOrientationChange(event)"
tabscroll="false"
apply="org.zkoss.bind.BindComposer" viewModel="@id('vm')
@init('TweetsVM')">
<tabs>
<tab>
<caption>
Home
</caption>
</tab>
<tab>
<caption>
Connect
</caption>
</tab>
<tab>
<caption>
Discover
</caption>
</tab>
</tabs>
children="@init(vm.profiles)">
<vlayout>
<hlayout>
<vlayout>
</vlayout>
</hlayout>
<hlayout sclass="status">
<separator />
Responsive Design 180
</div>
<separator />
</div>
<separator />
</div>
</hlayout>
</vlayout>
</groupbox>
</template>
</scrollview>
<listitem>
<listcell>
<hlayout>
<div>
<separator />
value="@load(tweet.content)" />
</div>
</hlayout>
</listcell>
</listitem>
</template>
</listbox>
</hlayout>
</tabpanel>
</tabpanels>
</tabbox>
</zk>
In this example, we layout the page with ZUL Components and only register the ClientInfoEvent to handle the
display when re-orientating . We manage the main content of the listbox using vflex and hflex to expand the tweet's
content according to the max height, and then we apply the same concepts mentioned in Adaptive Layouts, with
Responsive Design 181
@Media Query to fine tune some areas in the page, for example making profile area invisible on smartphones. For
an example of this you can refer to the following CSS Content section for more details.
Note: Some of the components and features used above are available in ZK EE only [6].
CSS Content
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<%@ taglib uri="http://www.zkoss.org/dsp/web/theme" prefix="t" %>
<%-- For tablet or orientation in portrait devices --%>
@media only screen and (orientation:portrait) {
body {
margin: 0;
padding: 0;
${t:gradient('ver', '#cedce7 0%;#596a72 100%')};
}
<%-- Customize the default tabbox styling --%>
.z-tabs-header {
height: auto;
background: black;
}
.z-tabs-cnt > li.z-tab,
.z-tabs-cnt > .z-tab:active {
background: transparent;
${t:boxShadow('none')};
border: 0;
width: 128px;
height: 32px;
}
.z-tab .z-label {
display: none;
}
.z-tabs-cnt > li.z-tab.z-tab-seld,
.z-tabs-cnt > li.z-tab.z-tab-seld:first-child,
.z-tabs-cnt > li.z-tab.z-tab-seld:active,
.z-tabs-cnt > li.z-tab.z-tab-seld:active:first-child {
background: black;
border-color: transparent;
${t:boxShadow('1px 1px 0 black')};
}
.z-tabs-cnt > .z-tabs {
background: #555;
}
td.z-caption-r {
text-align: center;
}
.main-content {
max-height: 2048px;
}
Responsive Design 182
.z-tab .z-image,
.z-tab .home {
height: 32px;
line-height: 28px;
width: 80px;
}
tr.z-listbox-odd {
${t:gradient('ver', '#cedce7 0%;#596a72 100%')};
}
.z-scrollview-content-ver:first-child .profile {
margin: 10px;
}
.profile {
margin-left: 10px;
border: 3px solid #CFCFCF;
${t:borderRadius('15px')};
${t:boxShadow('0 0 7px rgba(0, 0, 0, 0.70)')};
}
.z-groupbox-3d-cnt {
border: 0px;
Responsive Design 183
}
}
<%-- For smartphones or small screen --%>
@media screen and (orientation:portrait) and (max-width: 720px) {
.main-content {
max-height: 1024px;
}
.z-tabs-cnt > li.z-tab,
.z-tabs-cnt > .z-tab:active {
background: transparent;
${t:boxShadow('none')};
border: 0;
width: 80px;
height: 32px;
}
.z-tab .z-image,
.z-tab .home {
height: 32px;
line-height: 28px;
width: 60px;
}
.home:before {
top: 12px;
left: 20px;
}
.home:after {
top: -1px;
left: 25px;
}
.main-content > .z-hlayout-inner:first-child {
display: none;
}
}
Version History
Version Date Content
References
[1] http:/ / en. wikipedia. org/ wiki/ Responsive_Web_Design
[2] https:/ / github. com/ jumperchen/ ZKResponsiveDesign/ blob/ master/ src/ main/ webapp/ layout/ layout1. zul
[3] http:/ / www. w3schools. com/ cssref/ css3_browsersupport. asp
[4] http:/ / www. w3. org/ TR/ css3-mediaqueries/
[5] https:/ / github. com/ jumperchen/ ZKResponsiveDesign/ blob/ master/ src/ main/ webapp/ layout/ layout2. zul
[6] http:/ / www. zkoss. org/ product/ edition. dsp
[7] https:/ / github. com/ jumperchen/ ZKResponsiveDesign/ blob/ master/ src/ main/ webapp/ layout/ layout3. zul
Message Box
In additions to composing your own window for displaying a message, ZK provide a simple utility: Messagebox
[1][2]
. For example,
If you could specify a different icon for difference scenario, such as alerting an error:
new EventListener<MouseEvent>() {
public void onEvent(MouseEvent event) {
if (Messagebox.ON_YES.equals(event.getName()))
;//delete the file
}
});
Notice that the invocation of show is returned immediately without waiting for user's clicks[3].
There are a lot of more utilities, such as the button's order and label. Please refer to ZK Component Reference:
Messagebox and Messagebox [1] for more information.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Messagebox. html#
[2] If you are using zscript, there is a shortcut called alert as follows UNIQ-source-0-f6421cf86b2cc1a3-QINU
[3] If you turned on the use of event thread, the invocation will be stalled until the user clicks a button. It is easier but threading is not cheap.
For more information, please refer to the Event Threads section.
Close on Exceptions
When an exception happens inside a message box's event listener and handled by a ZK error page. The message box
doesn't disappear, and it might cover the information on the error page. To avoid this, you can:
• create a modal window in an error page, then the modal window will be on top of the message box.
• catch an exception inside a message box's event listener and detach the message box.
Version History
Version Date Content
Layouts and Containers 186
Layouts
This section provides brief introductions for some of the layout components in ZK. For detailed information and the
complete list of layouts, please refer to ZK Component Reference: Layouts.
<hlayout>
<div width="100px" height="50px" style="background:blue">1</div>
<div width="80px" height="70px" style="background:yellow">2</div>
</hlayout>
<vlayout>
<div width="100px" height="50px" style="background:blue">1</div>
<div width="80px" height="70px" style="background:yellow">2</div>
</vlayout>
Scrolling
• To make Hlayout and Vlayout scrollable, specify "overflow:auto;" to "style" .
• The height of Hlayout and Vlayout depends on the size of their children, therefore, in order to keep the height of
Hlayout and Vlayout constant for the scroll bar to appear, specify a fixed height to Hlayout and Vlayout or place
them into a fixed height container, EX: "<window height="100px"...".
Alignment
Users are allowed to change sclass to control alignment.
<font size="7.99">
<zk>
<hlayout sclass="z-valign-top">
<label value="Text:"/>
<textbox/>
<window width="50px" height="50px" title="win" border="normal"/>
</hlayout>
<separator/>
<hlayout>
<label value="Text:"/>
<textbox/>
<window width="50px" height="50px" title="win" border="normal"/>
</hlayout>
<separator/>
<hlayout sclass="z-valign-bottom">
<label value="Text:"/>
<textbox/>
<window width="50px" height="50px" title="win" border="normal"/>
</hlayout>
</zk>
</font>
<hbox>
<div width="100px" height="50px" style="background:blue">1</div>
<splitter collapse="before"/>
<div width="80px" height="70px" style="background:yellow">2</div>
</hbox>
<vbox>
<div width="100px" height="50px" style="background:blue">1</div>
<splitter collapse="after"/>
<div width="80px" height="70px" style="background:yellow">2</div>
</vbox>
Layouts and Containers 188
Scrolling
• Hbox and Vbox are created by a table, however, HTML tables are not able to show scroll bars. Hence, to achieve
this, users will need to place them in a scrolling container.
Alignment
• Users are also allowed to specify align and pack to control alignment.
• Users are also allowed to use "cell" to control each cell's alignment.
<hbox width="500px">
<cell style="border:1px solid black;">
<button label="Help"/>
</cell>
<cell style="border:1px solid black;"
hflex="6" align="center">
<button label="Add"/>
<button label="Reomve"/>
<button label="Update"/>
</cell>
<cell style="border:1px solid black;"
hflex="4" align="right">
<button label="OK"/>
<button label="Cancel"/>
</cell>
</hbox>
<vbox width="300px" align="stretch">
<cell style="border:1px solid black;">
<button label="Help"/>
</cell>
<cell style="border:1px solid black;"
align="center">
<button label="Add"/>
<button label="Reomve"/>
<button label="Update"/>
</cell>
<cell style="border:1px solid black;"
align="right">
<button label="OK"/>
<button label="Cancel"/>
</cell>
</vbox>
Layouts and Containers 190
Borderlayout
Borderlayout divides its child components into to five areas: North, South, East, West and Center. The heights of
North and South are firstly decided, the remainder space is then given to Center as its height. Note that East and
West also takes on the height of Center.
flex
Layout region shares the height of Borderlayout with a distributing sequence of: North, South and Center while the
heights of East and West takes on the height of Center. In the previous sample, the div in the layout region does not
take up all of layout region's space. In order for the child to occupy the whole area, please set vflex="1" to the child
component.
Scrolling
• The height of Center depends on Borderlayout but not on its child, therefore, the height of Center will not be
expanded by the growing size of its child components. If Center's height is too short for it's child, Center will cut
out the contents of it's child, hence, to avoid this, specify autoscroll="true" to Center in order to assign Center to
handle the scrolling.
<font size="7.76">
<borderlayout width="300px" height="300px">
<north>
<div height="100px" style="background:#008db7;color:white;">N</div
</north>
<south>
<div height="100px" style="background:#112f37;color:white;">S</div
</south>
<center autoscroll="true">
<div height="200px">C</div>
</center>
<east flex="true">
<div width="30px" style="background:#f2f2f2;">E</div>
</east>
<west flex="true">
<div width="20px" style="background:#f2f2f2;">W</div>
</west>
</borderlayout>
</font>
Grown by children
• To make Borderlayout dependable on the size of its child components, vflex feature is applied. Specify
vflex="min" to each layout region and Borderlayout.
<font size="8.19">
<borderlayout width="300px" vflex="min">
<north vflex="min">
<div height="100px" style="background:#008db7;color:white;">N</div>
</north>
<south vflex="min">
<div height="100px" style="background:#112f37;color:white;">S</div>
</south>
<center vflex="min">
<div height="200px">C</div>
</center>
<east flex="true">
<div width="30px" style="background:#f2f2f2;">E</div>
</east>
<west flex="true">
<div width="20px" style="background:#f2f2f2;">W</div>
</west>
</borderlayout>
</font>
Layouts and Containers 192
Borderlayout in a container
• Almost all containers' heights depend on their child components, however, the height of Borderlayout does not
expand accordingly to the sizes of its child components, therefore, when placing Borderlayout in a container,
users have to specify a fixed height in order for Borderlayout to be visible.
<zk>
<window title="win" border="normal">
<borderlayout height="200px">
<north>
<div style="background:blue">N</div>
</north>
<south>
<div style="background:blue">S</div>
</south>
<center>
<div>C</div>
</center>
<east>
<div style="background:yellow">E</div>
</east>
<west>
<div style="background:yellow">W</div>
</west>
</borderlayout>
</window>
</zk>
• The default height of Borderlayout is dependent on its parent component, therefore, users can also put
Borderlayout in a container with a fixed height.
<zk>
<window title="win" border="normal" height="200px">
<borderlayout>
<north>
<div style="background:blue">N</div>
</north>
<south>
<div style="background:blue">S</div>
</south>
<center>
<div>C</div>
</center>
<east>
<div style="background:yellow">E</div>
</east>
<west>
<div style="background:yellow">W</div>
</west>
</borderlayout>
Layouts and Containers 193
</window>
</zk>
Columnlayout
Columnlayout places its child components into multiple columns while each column allows any numbers of child
components placed vertically with different heights (but with the same widths). Unlike portallayout, Columnlayout
does not allow end users the ability to move child components to different locations at will (although of course,
developers are allowed to use the ZK application to re-arrange the order of children components).
• Available for ZK:
•
<font size="8.46">
<columnlayout>
<columnchildren width="30%" style="padding: 5px 1px">
<panel height="60px" title="1" border="normal" maximizable="true">
<panelchildren>1</panelchildren>
</panel>
<panel height="80px" title="2" border="normal" closable="true">
<panelchildren>2</panelchildren>
</panel>
</columnchildren>
<columnchildren width="70%" style="padding: 5px 1px">
<panel height="100px" title="3" border="normal" collapsible="true">
<panelchildren>3</panelchildren>
</panel>
</columnchildren>
</columnlayout>
</font>
Portallayout
Portallayout places its child components into multiple columns while each column can allow any numbers of child
components to be placed vertically with different heights (but with the same widths). Users are also allowed to move
any of them to any area desired like that of a portal.
• Available for ZK:
•
Layouts and Containers 194
<font size="8.44">
<portallayout>
<portalchildren width="40%" style="padding: 5px 1px">
<panel height="60px" title="1" border="normal" maximizable="true">
<panelchildren>1</panelchildren>
</panel>
<panel height="90px" title="2" border="normal" closable="true">
<panelchildren>2</panelchildren>
</panel>
</portalchildren>
<portalchildren width="60%" style="padding: 5px 1px">
<panel height="100px" title="3" border="normal" collapsible="true">
<panelchildren>3</panelchildren>
</panel>
<panel height="55px" title="4" border="normal" closable="true">
<panelchildren>4</panelchildren>
</panel>
</portalchildren>
</portallayout>
</font>
Tablelayout
Tablelayout places its child components in a table. Ths implementation is based on a HTML TABLE tag.
• Available for ZK:
•
<tablelayout columns="2">
<tablechildren>
<panel title="1" border="normal"
collapsible="true" width="80px" height="60px">
<panelchildren>1</panelchildren>
</panel>
</tablechildren>
<tablechildren>
<panel title="2" border="normal"
collapsible="true" width="80px" height="60px">
<panelchildren>2</panelchildren>
</panel>
</tablechildren>
<tablechildren>
<panel title="3" border="normal"
collapsible="true" width="80px" height="60px">
<panelchildren>3</panelchildren>
</panel>
</tablechildren>
<tablechildren>
<panel title="4" border="normal"
collapsible="true" width="80px" height="60px">
<panelchildren>4</panelchildren>
</panel>
</tablechildren>
</tablelayout>
Layouts and Containers 195
Containers
This section provides a brief introduction for some of the container components in ZK. For detailed information and
a complete list of containers, please refer to ZK Component Reference: Containers.
Scrolling
Span:
• Span is an inline element that is not scrollable.
Div:
• To make Div scrollable, specify "overflow:auto;" to "style".
• The height of Div depends on the size of its children, therefore, in order to keep the height of Div constant for the
scroll bar to appear, specify a fixed height to Div.
Window
Window is a container providing captioning, bordering, overlapping, draggable, closable, sizable, and many other
features. Window is also the owner of an ID space, such that each child component and its IDs are in one
independent window so as to avoid the IDs of each child components conflicting with one another.
Scrolling
• To make Window scrollable, specify "overflow:auto;" from "contentStyle".
• The height of Window is dependent on the size of its children, therefore, in order to keep the height of Window
constant for the scroll bar to appear, specify a fixed height to Window.
Panel
Like Window, panel is another powerful container supporting captioning, bordering, overlapping and many other
features. However, IdSpace [1] is not implemented by this component, therefore, all of its children belongs to the
same ID space of its parent.
Scrolling
• To make Panel scrollable, specify "overflow:auto;" to "style" of "panelchildren".
• The height of Panel is dependent on the size of its children, therefore, in order to keep the height of the Panel
constant for the scroll bar to appear, specify a fixed height to Panel.
Groupbox
Groupbox is a light-weighted way to group child components together. It supports "caption" and "border", however,
it does not support overlapping or resizing. Like Panel, IdSpace [1] is not implemented by this component either.
<groupbox mold="3d">
<caption label="Fruits"/>
<radiogroup>
<radio label="Apple"/>
<radio label="Orange"/>
<radio label="Banana"/>
</radiogroup>
</groupbox>
Scrolling
3d mold only
Tabbox
Tabbox is a container used to display a set of tabbed groups of components. A row of tabs can be displayed at the top
(or left) of the tabbox; users can switch in between each tab group by a simple click. IdSpace [1] is not implemented
by this component either.
<tabbox height="80px">
<tabs>
<tab label="Tab 1"/>
<tab label="Tab 2"/>
</tabs>
<tabpanels>
<tabpanel>This is panel 1</tabpanel>
<tabpanel>This is panel 2</tabpanel>
</tabpanels>
</tabbox>
Scrolling
• To make Tabpanel scrollable, specify "overflow:auto;" to "style".
• The height of Tabpanel is dependent on the size of its children, therefore, in order to keep the height of the
Tabpanel constant for the scroll bar to appear, specify a fixed height to Tabbox.
Version History
Version Date Content
Hflex and Vflex 199
Fit-the-Rest Flexibility
The simplest use of flex is to have one component to take the rest of the space of its parent (or the page, if it is the
root component). For example,
<zk>
<datebox/>
<div vflex="1" style="background: yellow"/>
</zk>
Here is another example that we'd like to grow the tabbox to fit the rest of the space:
<zk>
<datebox/>
<tabbox vflex="1">
<tabs>
<tab label="Home"/>
<tab label="Direction"/>
</tabs>
<tabpanels>
<tabpanel style="overflow: auto">
<div height="500px" width="100%" style="background: yellow"/>
</tabpanel>
<tabpanel>
</tabpanel>
</tabpanels>
</tabbox>
</zk>
Hflex and Vflex 200
Notice you could specify style="overflow: auto" in the tabpanel such that the scrollbar will be inside the
tabbox rather than the browser window, if the content is too large to fit.
<datebox/>
<div vflex="1" style="background: yellow"/><!--height will be zero since height not specified in parent div-->
</div>
To solve it, you have to specify the height in the outer div, such as <div height="100%">, <div
height="200px">, or <div vflex="1">.
Proportional Flexibility
The absolute value of the vflex/hflex is not that important. It is used to determine the proportion among flexible
components. That is, you can give different integers to differentiate child components so they will take space
proportionally per the given vflex/hflex value. For example,
<hlayout width="200px">
<div style="background: blue" hflex="1">1</div>
<div style="background: yellow" hflex="2">2</div>
</hlayout>
Hflex and Vflex 201
Minimum Flexibility
Sometimes, you might wish that the parent component's size is determined by its children. Or I shall say, the size of
the parent component is just high/wide enough to hold all of its child components. We also support that. Just specify
vflex/hflex="min".
As you can see, the height of the north region of the outer borderlayout is determined by its child borderlayout. And
the height of the inner borderlayout, in this example, is determined by the height of its west child region.
[3]
Also notice that the flex property (LayoutRegion.setFlex(boolean) ) is unique to borderlayout (north and others).
Don't confuse it with hflex or vflex.
Hflex and Vflex 202
In the case below, we see nothing for the incorrect usage (min on the parent - vlayout, 1 on the child - div):
However, in the case below, because one of the children, red div has a fixed width, vlayout can determine its
width. So that yellow div can also determine its width upon its parent, which is 150px.
Normally, if the siblings of yellow div that have been defined height correctly, that yellow div height should be
equal to the max height of siblings, which is 30px in the following sample.
However, in the following use case, we should see nothing as it is an incorrect usage:
<grid width="300px">
<columns>
<column label="Name" hflex="1"/>
<column label="Value" hflex="2"/>
Hflex and Vflex 203
</columns>
<rows>
<row>username:<textbox hflex="1"/></row>
<row>password:<textbox hflex="1"/></row>
</rows>
</grid>
The result is
Notice that we also specify hflex="1" to the textbox, so it will take up the whole space.
Alignment
When we create a form, we will put some input elements in a Grid. We can set hflex="min" to Grid and each
Column for keep Grid with minimal size.
<grid hflex="min">
<columns>
<column hflex="min" align="right"/>
<column hflex="min"/>
</columns>
<rows>
<row>
<label value="Name:"/>
<textbox/>
</row>
<row>
<label value="Birthday:"/>
<datebox/>
</row>
</rows>
</grid>
If we need the Datebox's width the same with Textbox, we can specify hflex="1" to Datebox.
Hflex and Vflex 204
<grid hflex="min">
<columns>
<column hflex="min" align="right"/>
<column hflex="min"/>
</columns>
<rows>
<row>
<label value="Name:"/>
<textbox/>
</row>
<row>
<label value="Birthday:"/>
<datebox hflex="1"/>
</row>
</rows>
</grid>
Cell colspan
Sometimes we need to put some elements in cross column, we can put it in a Cell and set hflex="1" to the element.
<grid hflex="min">
<columns>
<column hflex="min" align="right" />
<column hflex="min" />
<column hflex="min" align="right" />
<column hflex="min" />
</columns>
<rows>
<row>
<label value="Name:" />
<textbox/>
<label value="Birthday:" />
<datebox/>
</row>
<row>
<label value="Address:" />
<cell colspan="3">
<textbox rows="5" hflex="1"/>
</cell>
</row>
</rows>
</grid>
For a complete list of controls that you could apply to the columns of grid, listbox and tree, please refer to ZK
Developer's Reference/UI Patterns/Grid's Columns and Hflex.
Hflex and Vflex 205
The advantage of percentage is that the performance will be a little better, since it is done by the browser. However,
hflex and vflex are recommended because of the following issues:
• The use of 100% will cause overflow (and then scrollbar appears if overflow:auto), if padding is not zero.
Moreover, some browsers might show mysterious scrollbars or overflow the parent's space even if padding is
zero.
• The percentage does not work, if any of the parent DOM element does not specify the width or height.
• The percentage does not support take-the-rest-space. For example, the following doesn't work:
body {
height: 100%;
padding: 0 5px;
}
Sometimes you might prefer to add some padding vertically, but it cannot be done by changing BODY's styling as
follows.
body {
height: 100%;
padding: 5px; /* WRONG! It causes vertical scrollbar to appear
since the 100% height is used with vertical padding */
}
As described in the previous section, a vertical scrollbar will appear, since both the vertical padding and the 100%
height are specified.
Solution: you shall not change the default CSS styling of BODY. Rather, you could enclose the content with the div
component, and then specify vflex="1" and the padding to the div component. For example,
</rows>
</grid>
</div>
<zk>
<zscript><![CDATA[
int[] str = new int[100];
for(int i=0;i<100;i++){
str[i]=i;
}
]]></zscript>
Note that the height proportion between the two trees are always 1 : 2, when we change the browser height.
Limitations
This limitation can be solved by the use of hlayout and div as follows.
<div style="background: blue" hflex="1">1</div><!-- not work since it won't be aligned with sibling div -->
</div>
As shown below, the second div is not aligned vertically with the first div, so is the width not as expected:
This limitation can be solved by use of hlayout and div as show in the previous subsection.
<style>
input.nomargin {
margin-left: 0;
margin-right: 0;
}
</style>
<div width="300px" style="border: 1px solid green">
<textbox sclass="nomargin" hflex="1" />
</div>
Hflex and Vflex 208
<zk>
<div id="div" vflex="1" hflex="1" style="background: blue">blue</div>
<button label="vflex to min">
<attribute name="onClick"><![CDATA[
div.setVflex("min");
Clients.resize(div);
]]></attribute>
</button>
</zk>
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setHflex(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setVflex(java. lang. String)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ LayoutRegion. html#setFlex(boolean)
[4] http:/ / www. quirksmode. org/ css/ display. html
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#resize(org. zkoss. zk. ui. Component)
Grid's Columns and Hflex 209
Span
If the total width of all columns is smaller than the grid, there will be some whitespace shown at the right side of the
grid.
If you prefer to make each column's width a bit wider to cover the whole grid, you could specify span="true" in
grid/listbox/tree (such as Grid.setSpan(java.lang.String) [2]). If you want to make a particular column larger to cover
the whole grid, you could specify a number, such as span="2" to expand the third column.
sizedByContent
If you want to make each column as minimal as possible, you could specify hflex="min" for each column.
Alternatively, you could specify sizedByContent="true" in the grid/listbox/tree
[3]
(Grid.setSizedByContent(boolean) ). In other words, it implies the width of a column that is not assigned with
width and hflex shall be minimal.
In general, you will specify span="true" too to cover the whole grid/listbox/tree.
Use Cases
Here we take listbox with listheaders as an example to show some different use cases.
]]></zscript>
<listbox width="800px">
<listhead>
<listheader label="Product" />
<listheader label="Description" />
<listheader label="Comment" />
</listhead>
<listitem>
<listcell><label value="${msgs2[0]}"></label></listcell>
<listcell><label value="${msgs2[1]}"></label></listcell>
<listcell><label value="${msgs2[2]}"></label></listcell>
</listitem>
<listitem>
<listcell><label value="${msgs2[3]}"></label></listcell>
<listcell><label value="${msgs2[4]}"></label></listcell>
<listcell><label value="${msgs2[5]}"></label></listcell>
</listitem>
</listbox>
</zk>
• Default : Data component will show the data correctly, with no width specification. (the exactly width of columns
are rendered by browser automatically)
Proportional Width
Sure you can use the hflex we have mentioned in previous section.
<listhead>
<listheader label="Product" hflex="1"/>
<listheader label="Description" hflex="2"/>
<listheader label="Comment" hflex="1" />
</listhead>
Grid's Columns and Hflex 211
Minimum Flexibility
In the case of hflex=min, column's width will be just fitted the contents. As you can see, there might be blank space
on the right of the listbox.
<zscript><![CDATA[
String[] msgs2 = {
"Application Developer's Perspective",
"Very Short Text",
"Server+client Fusion architecture",
"Execution Flow of Serving an Ajax Request",
"Very Short Text",
"When to Send an Ajax Request (Addition Text )"
};
]]></zscript>
<listbox width="800px">
<listhead>
<listheader label="Product" hflex="min"/>
<listheader label="Description" hflex="min"/>
<listheader label="Comment" hflex="min" />
</listhead>
• If you want your contents fill the whole grid to eliminate the blank space, you can set span=true to make it
proportionally expanded.
• If you want the rest of space to be assigned to one of the columns, set span to a number. The number is 0-based
index of columns.
• If you want the size of the Listbox determined by it content, assign hflex=min on the Grid, and make sure all the
Listheaders either have hflex=min or has a fixed width.
<listbox hflex='min'>
<listhead>
<listheader label="Product" hflex="min" />
<listheader label="Description" hflex="min" />
<listheader label="Comment" hflex="min" />
</listhead>
• Default : ZK data component will wrap the text to fit the width of column.
Grid's Columns and Hflex 213
Scrollbar
When the sum of content width is larger than Grid width. The scroll appears if and only if
1. The Columns and Column component are presented.
2. Each of the Column components is given an as hflex or width value.
Specify Width
This is simple way to avoid text wrapped by given proper width. However, it can be difficult if you don't know the
content length beforehand.
<listhead>
<listheader label="Product" width="250px"/>
<listheader label="Description" width="470px"/>
<listheader label="Comment" width="280px" />
</listhead>
Minimum Flexibility
• Set hflex=min and ZK will calculate the length of content and set proper width to the column accordingly.
• Notes: Remember to set every column with hflex=min or specify a specific width; otherwise those columns
without setting minimum hflex or specifying a width could disappear if not enough space in the listbox.
<listhead>
<listheader label="Product" hflex="min" />
<listheader label="Description" hflex="min" />
<listheader label="Comment" hflex="min" />
</listhead>
No Scrollbar Grid V X !
Fill whole Grid + No Content Wrapping V All Column Component Should have one of these attributes
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setWidth(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Grid. html#setSpan(java. lang. String)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Grid. html#setSizedByContent(boolean)
[3] When the user moves the mouse point over the [2]
Tooltips XulElement.setTooltip(java.lang.String) or
target component for a while [4]
XulElement.setTooltip(org.zkoss.zul.Popup)
Context When the user clicks the right button on the target [5]
XulElement.setContext(java.lang.String) or
Menus component [6]
XulElement.setContext(org.zkoss.zul.Popup)
Popups When the user clicks the left button on the target [7]
XulElement.setPopup(java.lang.String) or
component [8]
XulElement.setPopup(org.zkoss.zul.Popup)
Tooltips, Context Menus and Popups 216
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setTooltip(java. lang. String)
[3] Notice that if you'd like to have different text for the tooltip (rather than a fully customized look), you shall use XulElement (http:/ / www.
zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#) instead (which is easier to use).
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setTooltip(org. zkoss. zul. Popup)
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setContext(java. lang. String)
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setContext(org. zkoss. zul. Popup)
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setPopup(java. lang. String)
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setPopup(org. zkoss. zul. Popup)
Tooltips
To provide a custom tooltip, you could specify the ID of the custom tooltip in the target component's tooltip (
XulElement.setTooltip(java.lang.String) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/
XulElement. html#setTooltip(java. lang. String)) or XulElement.setTooltip(org.zkoss.zul.Popup) (http:/ / www.
zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setTooltip(org. zkoss. zul. Popup))). For
example,
<zk>
<image src="/img/earth.png" tooltip="msg"/>
<menupopup id="msg">
<menuitem label="Undo"/>
<menuitem label="Redo"/>
<menu label="Sort">
<menupopup>
<menuitem label="Sort by Name" autocheck="true"/>
<menuitem label="Sort by Date" autocheck="true"/>
</menupopup>
</menu>
</menupopup>
</zk>
Then, when the user moves the mouse pointer over the image for a while, the menupopup will be shown up as shown
below.
The time to wait before showing up the tooltip can be configured. Please refer to ZK Configuration Reference for
more information.
Tooltips, Context Menus and Popups 217
Context Menus
Providing a customized context menu is the same, except it uses the context property instead. For example,
<zk>
<listbox>
<listitem label="Right Click Me!" context="status"/>
</listbox>
As shown above, you could use Popup (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Popup. html#)
so the context menu is not limited to a menupopup.
Here is another example: context menus versus right clicks.
<zk>
<menupopup id="editPopup">
<menuitem label="Undo"/>
<menuitem label="Redo"/>
<menu label="Sort">
<menupopup>
<menuitem label="Sort by Name" autocheck="true"/>
<menuitem label="Sort by Date" autocheck="true"/>
</menupopup>
</menu>
</menupopup>
Notice that the menupopup is not visible until a user right-clicks on a component that is associated with its’ ID.
Tooltips, Context Menus and Popups 218
Tip: If you want to disable browser's default context menu, you can set the context attribute of a component to a
non-existent ID.
The popup component is generic popup and you are able to place any kind of components inside of popup. For
example,
<zk>
<label value="Right Click Me!" context="any"/>
</zk>
<zk>
<label value="Right Click Me!" context="any"/>
Popups
Providing a customized popup is the same, except it uses the popup property instead. For example,
<zk>
<label value="Click Me!" popup="status"/>
Position of Popup
The context-menu/tooltip/popups can be shown by a position from Popup or the location of x and y, you can specify
position with the following format:
<zk>
<label value="Click Me!" popup="status, at_pointer"/>
<label value="Click Me!" popup="status, after_pointer"/>
<label value="Click Me!" popup="status, position=before_start"/><!-- And other 20 positions -->
<label value="Click Me!" popup="status, x=30, y=30"/><!-- Fixed positions -->
since 6.5.2
Since 6.5.2, the position is now more customizable and can be done by specifying a formula that executes on client
side, for example,
<zk>
<vlayout>
</vlayout>
</popup>
</zk>
Toggle Popup
Since 7.0.0
The context-menu/popup support toggle type in ZK 7.0.0. Check Popup Component for detailed description.
Limitation of iOS
Tooltips
Since there is no mouse move event in iOS, the tooltip won't be shown.
Context Menu
[since 5.0.7]
ZK Client Engine will simulate the context menu, if the user touches the DOM element, associated with the
contextmenu property, and holds a while.
Tooltips, Context Menus and Popups 220
Popup
Like onClick, ZK Client Engine simulates the click, if the user touches the DOM element associated with the popup
property.
For more information, please refer to Safari Developer Library (http:/ / developer. apple. com/ library/ safari/
#documentation/ AppleApplications/ Reference/ SafariWebContent/ HandlingEvents/ HandlingEvents. html#/ /
apple_ref/doc/uid/TP40006511-SW1).
Version History
Version Date Content
Keystroke Handling
[1]
Keystroke handling is generic. Any component inherited from XulElement can handle the key event in the same
way.
<rows>
</rows>
</grid>
package foo;
import org.zkoss.zul.*;
public class Login extends org.zkoss.zk.ui.util.GenericForwardComposer {
Textbox username;
Textbox password;
public void onOK() {
//handle login
}
public void onCancel() {
username.setValue("");
password.setValue("");
}
}
Notice that the onOK and onCancel events are sent to the nearest ancestor of the component that has the focus.
In other words, if you press ENTER in a textbox, then ZK will look up the textbox, its parent, its parent's parent and
so on to see if any of them has been registered a listener for onOK. If found, the event is sent to it. If not found,
nothing will happen.
Also notice that, if a button gains the focus, ENTER will be intercepted by the browser and interpreted as pressed.
For example, if you move the focus to the Reset button and press ENTER, you will receive onCancel rather than
onOK (since onClick will be fired and it is converted to onCancel because of the forward attribute specified).
Keystroke Handling 222
Control Keys
To handle the control keys, you have to specify the keystrokes you want to handle with
XulElement.setCtrlKeys(java.lang.String) [1]. Then, if any child component gains the focus and the user presses a
keystroke matches the combination, the onCtrlKey will be sent to the component with an instance of KeyEvent
[2]
.
Like ENTER and ESC, you could specify the listener and the ctrlKeys property in one of the ancestors. ZK will
search the component having the focus, its parent, its parent's parent and so on to find if any of them specifies the
ctrlKeys property that matches the keystroke.
For example,
As shown, you could use KeyEvent.getKeyCode() [3] to know which key was pressed.
^k The control key, i.e., Ctrl+k, where k can be a~z, 0~9, #n and ~n.
@k The alt key, i.e., Alt+k, where k can be a~z, 0~9, #n and ~n.
#fn A function key. #f1, #f2, ... #f12 for F1, F2,... F12.
%k since 8.5
The Mac command key, i.e., command+k, where k can be a~z, 0~9, #n and ~n.
Document-level Keystrokes
[5.0.6]
When you set the library property org.zkoss.zk.ui.invokeFirstRootForAfterKeyDown.enabled [4] to true. If there is
no widget gaining a focus when an end user presses a keystroke, ZK can forward a key event to the first root
component. For example, when visiting the following page, the <mp>div</mp> component will receive the
<mp>onOK</mp> event.
In other words, doSomething() will be called if the user presses ENTER, even though no widget ever gains the
focus.
Nested Components
Keystrokes are propagated up from the widget gaining the focus to the first ancestor widget that handles the
keystroke. For example,
<div onOK="doFirst()">
<textbox id="t1"/>
<div onOK="doSecond()">
<textbox id="t2"/>
</div>
</div>
Then, doSecond() is called if t2 is the current focus, and doFirst() is called if t1 has the focus.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ impl/ XulElement. html#setCtrlKeys(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ KeyEvent. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ KeyEvent. html#getKeyCode()
[4] https:/ / www. zkoss. org/ wiki/ ZK_Configuration_Reference/ zk. xml/ The_Library_Properties/ org. zkoss. zk. ui.
invokeFirstRootForAfterKeyDown. enabled
Drag and Drop 224
<image draggable="true"/>
Similarly, you could make a component droppable by assigning "true" to the droppable property by the use of
HtmlBasedComponent.setDroppable(java.lang.String) [2].
<hbox droppable="true"/>
Then, the user could drag a draggable component, and then drop it to a droppable component.
[3]
Since the draggable and droppable properties are implemented in HtmlBasedComponent , almost all the
components can become draggable or droppable.
Notice that the dragged item may not be selected. Thus, you may prefer to change the selection to the dragged item
for this case, as shown below.
Drag and Drop 226
Listitem li = (Listitem)evt.getDragged();
if (li.isSelected()) {
Set selected =
((Listitem)evt.getDragged()).getListbox().getSelectedItems();
//then, you can handle the whole set at once
} else {
li.setSelected(true);
//handle li only
}
<listitem draggable="email"/>
...
<listitem draggable="contact"/>
Then, you could specify a list of identifiers to the droppable property to limit what can be dropped. For example,
the following image accepts only email and contact.
To accept any kind of draggable components, you could specify "true" to the droppable property. For
example, the following image accepts any kind of draggable components.
On the other hand, if the draggable property is "true", it means the component belongs to anonymous type.
Furthermore, only components with the droppable property assigned to "true" could accept it.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setDraggable(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setDroppable(java. lang. String)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ DropEvent. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ DropEvent. html#getDragged()
Page Initialization 227
Page Initialization
Sometimes it is helpful to run some code before ZK Loader instantiates any component. For example, check if the
user has the authority to access, initialize some data, or prepare some variables for EL expressions.
This can be done easily by implementing Initiator [1]/InitiatorExt [2], and then specifying it with the init directive.
<?init class="com.foo.MyInitial"?>
Exception Handling
The initiator can be used to handle the exception when ZK Loader renders a page by implementing
InitiatorExt.doCatch(java.lang.Throwable) [3]
Notice that it does not cover the exception thrown in an event listener, which could be handled by the
use of ExecutionCleanup [4].
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.util.Initiator;
import org.zkoss.zk.ui.util.InitiatorExt;
Initiator and EL
To prepare a variable for EL expression in an initiator, you could store the variable in the page's attributes.
Notice that the provision of variables for EL expression is generally better to be done with
VariableResolver [2] (and then specified it with the variable-resolver directive).
For example, suppose we have a class, CustomerManager, that can be used to load all customers, then we could
prepare a variable to store all customers as follows.
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.util.Initiator;
import org.zkoss.zk.ui.util.InitiatorExt;
Page Initialization 228
System-level Initiator
[since 5.0.7]
If you have an initiator that shall be invoked for each page, you could register a system-level initiator, rather than
specifying it on every page.
It could be done by specifying the initiator you implemented in WEB-INF/zk.xml as follows. For more
information, please refer to ZK Configuration Reference.
<listener>
<listener-class>foo.MyInitiator</listener-class>
</listener>
[8]
Once specified, an instance of the given class will be instantiated for each page (Page ), and then its method will
be called as if they are specified in the page (the init directive).
Page Initialization 229
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Initiator. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ InitiatorExt. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ InitiatorExt. html#doCatch(java. lang. Throwable)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ExecutionCleanup. html#
It is done at the client without Java code, so it is efficient. However, you could control it on the server (in Java) too,
such that you could redirect it based on some information that is available only at the server.
if (someCondition())
Executions.sendRedirect("/ready.zul");
You could also ask the browser to open another browser window from a given URL by the use of java.lang.String)
Execution.sendRedirect(java.lang.String, java.lang.String) [2].
if (!isLogin()) {
Execution exec = Executions.getCurrent();
HttpServletResponse response =
(HttpServletResponse)exec.getNativeResponse();
response.sendRedirect(response.encodeRedirectURL("/login"));
//assume there is /login
exec.setVoided(true); //no need to create UI since redirect will
take place
}
Forward and Redirect 230
Notice that we invoke Execution.setVoided(boolean) [5] to void an execution, such that ZK Loader will abort the
evaluation of a ZUML document (if you prefer not to generate any UI when redirecting).
Also notice that the casting to javax.servlet.http.HttpServletResponse in the above example does
not work in a portlet, since the native response is an instance of javax.portlet.RenderResponse.
To check whether to redirect can be packed as Initiator [1], see below for an example:
response.sendRedirect(response.encodeRedirectURL("/login")); //assume
there is /login
exec.setVoided(true); //no need to create UI since redirect
will take place
}
}
}
<?init class="foo.AuthenticateInit"?>
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#sendRedirect(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html#sendRedirect(java. lang. String,
[3] http:/ / download. oracle. com/ javaee/ 1. 4/ api/ javax/ servlet/ http/ HttpServletResponse. html#sendRedirect%28java. lang. String%29
[4] It actually sets the refresh header (http:/ / www. metatags. org/ meta_http_equiv_refresh).
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html#setVoided(boolean)
where we assume isLogin is an EL function that returns whether the user has logged in. For more information,
please refer to the Conditional Evaluation section.
You could forward to another page by the use of Executions.forward(java.lang.String) (http:/ / www. zkoss. org/
javadoc/latest/zk/org/zkoss/zk/ui/Executions.html#forward(java.lang.String)) too.
Notice that forwarding can be called only when loading a page. You cannot call it when handling an Ajax request
(such as when a user clicks a button). For handling an Ajax request, you have to use redirect as described in the
previous section.
Unlike redirect, forward does not change the URL that the browser knows. Rather, it is purely server-side activity:
using another page's content instead of the original one to render the output of the given (and the same) URL.
Forward and Redirect 231
Usually, onBeforeUnload will be triggered by outgoing navigation or by closing a browser tab. However, file
download may also cause a browser’s window to unload if they are performed in the page’s main context. In this
case, the initial page still exists after navigation as most browsers will handle file download in a separate download
manager without closing the page.
For example, the following zul code will trigger a file download by causing navigation to the targeted file. Since the
href attribute is a valid URL, most browsers will start a navigation workflow (which include triggering unload).
Once the target replies with a non-document content (ie a file to download), the browser will interrupt navigation and
handle the file while remaining on the previous page.
<zk>
<a href="./myFile.zip">my file</a>
</zk>
At this point however, the client engine has already triggered rmDesktop and the current desktop is no longer
available.
New tab
Using taget=”_blank” will open a new blank browser tab and use it as the target for the URL. This will prevent the
ZK page from unloading since no navigation is performed in its context. Since the result of navigating to this url is
not a document, the new blank tab will automatically be closed as soon as the download starts.
<zk>
<a href="./myFile.zip" target="_blank">my file</a>
</zk>
Forward and Redirect 232
Hidden iframe
To avoid the new tab flickering in the client browser, it is also possible to target a different context in the same page.
To do so, the page should contain a hidden iframe, and perform the download through it.
<zk>
<a href="./myFile.zip" target="myHiddenIframe">my file</a>
<iframe name="myHiddenIframe" visible="false"/>
</zk>
[1] In additions to forwarding, we could popup a window to ask him to login, i.e., without leaving the current desktop
Version History
Version Date Content
<zk>
<zscript>
void upload(UploadEvent event) {
org.zkoss.util.media.Media media = event.getMedia();
if (media instanceof org.zkoss.image.Image) {
org.zkoss.zul.Image image = new
org.zkoss.zul.Image();
image.setContent( (org.zkoss.image.Image) media);
image.setParent(pics);
} else {
Messagebox.show("Not an image: "+media, "Error",
Messagebox.OK, Messagebox.ERROR);
}
File Upload and Download 233
}
</zscript>
<button label="Upload" upload="true" onUpload="upload(event)"/>
<vlayout id="pics" />
</zk>
You could control the maximal allowed number of files, the maximal allowed size and other information by use of
Button.setUpload(java.lang.String) [4].
<menupopup>
<menuitem label="Upload" upload="true,maxsize=-1,native"/>
</menupopup>
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Toolbarbutton. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Menuitem. html#
[3] If you enabled the use of event threads, you could use Button (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Button. html#)
and related. For more information, please refer to the Event Thread section.
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Button. html#setUpload(java. lang. String)
File Download
Filedownload provides a set of utilities to prompt a user for downloading a file from the server to a local folder. For
example,
<button label="Download calendar.pdf" onClick='Filedownload.save("/resources/calendar.pdf", null);'/>
The file could be a static resource, an input stream, a file, a URL and others. Please refer to ZK Component
Reference and Filedownload (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Filedownload. html#) for
more information.
File Upload and Download 234
Version History
Version Date Content
Browser Information
Sometimes an application needs to know the client's information, such as time zone. Then, you can add an event
listener for the onClientInfo event. Once the event is added, the client will send back an instance of the
ClientInfoEvent [2] class, from which you can retrieve the information of the client.
For example,
<grid onClientInfo="onClientInfo(event)">
<rows>
<row>Time Zone <label id="tm"/></row>
<row>Screen <label id="scrn"/></row>
</rows>
<zscript>
void onClientInfo(ClientInfoEvent evt) {
tm.setValue(evt.getTimeZone().toString());
scrn.setValue(
evt.getScreenWidth()+"x"+evt.getScreenHeight()+"x"+evt.getColorDepth());
}
</zscript>
</grid>
Notice that the onClientInfo event is meaningful only to the root component (aka., a component without any
parent).
The client information is not stored by ZK, so you have to store it manually if necessary. Since a session is
associated with the same client, you can store the client info in the session's attribute.
For example, we coud use it to control the default time zone[3].
session.setAttribute("org.zkoss.web.preferred.timeZone",
event.getTimeZone());
Notice that the onClientInfo event is sent from the client after the UI is rendered at the client. Thus, if some of
your component's data depends on the client's info, say, screen resolution, it is better to handle it (say, adjust UI's
size) when the onClientInfo event is received.
If it is, though rarely, too late (i.e., it has to be done at beginning), you could ask the client to re-send the request
again with Executions.sendRedirect(java.lang.String) [1]. For example,
Browser Information and Control 235
import org.zkoss.util.TimeZones;
...
if (!TimeZones.getCurrent().equals(event.getTimeZone()) {
session.setAttribute("org.zkoss.web.preferred.timeZone",
event.getTimeZone()); //update to the session
Executions.sendRedirect(null); //ask to re-send (i.e., redo)
}
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ ClientInfoEvent. html#
[3] For more information about time zone, please refer to the Time Zone section.
Browser Control
Clients (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ ui/ util/ Clients. html#) has utilities to control the
client's visual presentation (more precisely, the browser window), such as printing, submitting, resizing and so on.
For example, you can scroll the browser window (aka., the desktop) as follows.
Clients.scrollBy(100, 0);
Here we describe some special controls that are worth to notice. For complete functionality, please refer to Clients
(http://www.zkoss.org/javadoc/latest/zk/org/zkoss/ui/util/Clients.html#).
if (mail.isDirty()) {
Clients.confirmClose("Your message has not been sent.\nDiscard
your message?");
} else {
Clients.confirmClose(null); //reset. no more confirmation.
}
As shown, it is done by passing the warning message to the client with Clients.confirmClose(java.lang.String) (http:/
/ www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#confirmClose(java. lang. String)). Now
when the user tries to close the browser window, reload, or browse to another URL -> a confirmation dialog is
shown.
Browser Information and Control 236
To disable the confirmation, just invoke Clients.confirmClose(java.lang.String) (http:/ / www. zkoss. org/ javadoc/
latest/zk/org/zkoss/zk/ui/util/Clients.html#confirmClose(java.lang.String)) with null.
After the loading is completed, you could invoke Clients.clearBusy(org.zkoss.zk.ui.Component) (http:/ / www.
zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#clearBusy(org. zkoss. zk. ui. Component)) (or
Clients.clearBusy() (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients.
html#clearBusy()))to clean it up. For more information, please refer to the Use Echo Events section.
In addition to logging messages to the console, you could log the messages to the browser for debugging by the use
of Clients.log(java.lang.String) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients.
html#log(java.lang.String)). For example,
//in Java
void doSomething() {
Clients.log("doSomething called");
}
Control in JavaScript
If you are familiar with JavaScript, you could have more control by sending any JavaScript code to the client for
evaluation. It can be done by preparing the JavaScript code in AuInvoke (http:/ / www. zkoss. org/ javadoc/ latest/
zk/ org/ zkoss/ zk/ au/ out/ AuInvoke. html#) or AuScript (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/
zk/ au/ out/ AuScript. html#), and then send back to client by calling Clients.response(org.zkoss.zk.au.AuResponse)
(http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#response(org. zkoss. zk. au.
AuResponse)).
For example, we could use AuScript (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ out/ AuScript.
html#) to inject any code, while AuInvoke (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ out/
AuInvoke.html#) is better if you want to invoke a function of a client-side widget.
In order to develop power and CPU efficient web applications, W3C publishes a specification named Page Visibility
(http:/ / www. w3. org/ TR/ page-visibility/ ) in HTML 5 which defines a means for site developers to
programmatically determine the current visibility state of the page. In this specification, there are two attributes
defined: hidden and visibilityState, where hidden is a boolean value representing whether the current page is visible
or not and visibilityState represents that the current page have four states: hidden, visible, prerender, and unloaded.
To get the two attributes, you can add an event listener for the onVisibilityChange event. Once the event is
added, the client will send back an instance of the VisibilityChangeEvent (http:/ / www. zkoss. org/ javadoc/ latest/
zk/ org/ zkoss/ zk/ ui/ event/ VisibilityChangeEvent. html#) class, from which you can retrieve the page visibility
state of the current page.
<zk>
<label>
Open/Change to another tab in the browser and go back this tab
</label>
<window title="window" border="normal" onVisibilityChange="onVisibleChange(event)">
<label id="lbl" />
<zscript><![CDATA[
import org.zkoss.zk.ui.event.VisibilityChangeEvent;
void onVisibleChange(VisibilityChangeEvent event) {
if
("visible".equals(event.getVisibilityState()) || !event.isHidden()) {
lbl.setValue("Welcome back");
}
}
]]></zscript>
</window>
</zk>
Notice that the onVisibilityChange event is meaningful only to the root component (aka., a component
without any parent) and the browsers that support this HTML 5 API.
Version History
Version Date Content
5.0.8 June, 2010 Clients.log(java.lang.String) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#log(java.
lang. String)) was introduced.
6.5.1 November, VisibilityChangeEvent (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ VisibilityChangeEvent.
2012 html#) was introduced.
Browser History Management 238
If you look carefully at the URL, you will find ZK appends #Step-2 to the URL.
If you press the BACK button, you will see as follows.
Browser History Management 239
<window onBookmarkChange="goto(event.bookmark)">
<zscript>
void goto(String bookmark) {
if ("Step-2".equals(bookmark)) {
...//create components for Step 2
} else { //empty bookmark
...//create components for Step 1
}
</zscript>
</window>
Like handling any other events, you can manipulate the UI any way you want, when the onBookmarkChange
event is received. It is totally up to you.
A typical approach is to use one of the createComponents methods of the Executions (http://www.zkoss.org/
javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#) class. In other words, you could represent each state with
one ZUML page, and then use createComponents to create all components in it when onBookmarkChange
is received.
if ("Step-2".equals(bookmark)) {
//1. Remove components, if any, representing the previous state
try {
self.getFellow("replacable").detach();
} catch (ComponentNotFoundException ex) {
//not created yet
}
Example
In this example, we bookmarks each tab selection.
wnd.getFellow(wnd.desktop.bookmark).setSelected(true);
} catch (ComponentNotFoundException ex) {
tab1.setSelected(true);
}
}
});
</zscript>
The onURIChange event is sent as an instance of URIEvent (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/
zkoss/zk/ui/event/URIEvent.html#).
Notice that the onURIChange event is sent only if the iframe contains another ZK page. If it contains non-ZK
page, you have to handle it manually. Please refer to ZK Component Reference: iframe for more information.
Browser History Management 241
Version History
Version Date Content
Show a Message
By default, a message is shown up to prompt the user and prevent from further accessing as depicted below.
Custom Message
You could show a custom message by specifying timeout-message in WEB-INF/zk.xml. For example,
<session-config>
<device-type>ajax</device-type>
<timeout-message>Session timeout. Please reload.</timeout-message>
</session-config>
Session Timeout Management 242
Internationalization
If you want to specify a Locale-dependent message, you could specify the key and prefix it with label: as
follows.
<session-config>
<device-type>ajax</device-type>
<timeout-message>label:timeout</timeout-message>
</session-config>
Then, you have to prepare the zk-label properties files as described in the Labels section.
#zk-label.properties
timeout={
Session timeout.
(multi-line is allowed)
}
<session-config>
<device-type>ajax</device-type>
<timeout-uri>/timeout.zul</timeout-uri>
</session-config>
In addition to WEB-INF/zk.xml, you could change the redirected URI manually as follows.
Devices.setTimeoutURI("ajax", "/timeout.zul");
About Device: A device represents the client device, such as Ajax browsers and Android devices. Each
desktop is associated with one device, and vice versa.
If you prefer to reload the page instead of redirecting to other URI, you can specify an empty URI as follows.
<session-config>
<device-type>ajax</device-type>
<timeout-uri></timeout-uri>
</session-config>
Session Timeout Management 243
<session-config>
<device-type>ajax</device-type>
<automatic-timeout>true</automatic-timeout>
<timeout-message>script:<![CDATA[foo.timeout('Session Timeout');]]></timeout-message>
</session-config>
The code depends on the client. For Ajax devices, it has to be JavaScript.
Automatic Timeout
By default, the session-timeout mechanism is triggered only if the client sends back a request (such as clicking on a
button). If you prefer to prompt the user even if it doesn't do anything, you could specify the automatic-timeout
element in WEB-INF/zk.xml as follows.
<session-config>
<device-type>ajax</device-type>
<automatic-timeout/>
</session-config>
Then, ZK Client will trigger the session-time mechanism (showing a message, redirecting to another page, or
running some JavaScript code).
Never Timeout
Though not recommended, you could prevent the session from timeout by making a "keep-alive" timer, such that the
desktop keeps alive until the user surfs away.
To do that, you first configure WEB/zk.xml as follows.
<session-config>
<timer-keep-alive>true</timer-keep-alive>
</session-config>
This will prevent the session to time out when the ZUL page is opened in the browser. The session still timeouts
when the user has navigated the browser away. The delay (600000 is 10 minutes) shall be as long as possible but
smaller than your session timeout.
The timer-keep-alive element is used to specify whether the session shall consider timer as a normal request. If it is
considered as a normal request, the session timeout mechanism will be restarted when it is received. Otherwise, the
timer, by default, won't restart the timeout mechanism.
Version History
Version Date Content
5.0.5 October 2010 The support of Custom Message and JavaScript was introduced.
References
[1] http:/ / www. zkoss. org/ zkdemo
Error Handling
Here we describe how to handle errors. An error is caused by an exception that is not caught by the application. An
exception might be thrown in two situations: when loading a ZUML document or when serving an AU request (aka,
an Ajax request).
You can customize the error handling by specifying the error page in WEB-INF/web.xml as follows[1].
Note: When exceptions are thrown during the ZK UI Lifecycle they are wrapped into a UiException [2]. If you want
to handle your own exceptions you can implement the Expectable [3] on your exception type. Exceptions
implementing this interface will not be wrapped and can be handled using the <exception-type> element directly.
Error Handling 245
Then, when an error occurs on loading a page, the Web server forwards the error page you specified, /error/error.zul.
Upon forwarding, the Web server passes a set of request attributes to the error page to describe what happens. These
attributes are as follows.
javax.servlet.error.status_code java.lang.Integer
javax.servlet.error.exception_type java.lang.Class
javax.servlet.error.message java.lang.String
javax.servlet.error.exception java.lang.Throwable
javax.servlet.error.request_uri java.lang.String
javax.servlet.error.servlet_name java.lang.String
Then, in the error page, you can display your custom information by the use of these attributes. For example,
Tips:
• The error page can be any kind of servlets. In addition to ZUML, you can use JSP or whatever servlet you
preferred.
• From java code the request attributes are accessible via Execution.getAttribute(java.lang.String) [4] or from the
requestScope (implicit object) [5].
@WireVariable
private Map<String, Object> requestScope;
@Override
public void doAfterCompose(Component comp) throws Exception {
//via execution.getAttribute()
Execution execution = Executions.getCurrent();
Exception ex1 = (Exception)
execution.getAttribute("javax.servlet.error.exception");
[1] Please refer to Chapter 10.9 of Java Servlet Specification (http:/ / download. oracle. com/ otn-pub/ jcp/ servlet-3. 0-fr-eval-oth-JSpec/
servlet-3_0-final-spec. pdf) for more details.
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ UiException. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ lang/ Expectable. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Execution. html#getAttribute(java. lang. String)
[5] https:/ / www. zkoss. org/ wiki/ ZUML_Reference/ EL_Expressions/ Implicit_Objects_(Predefined_Variables)/ requestScope
Then, if you click the button, the following error message will be shown.
Error Handling 247
You can customize the error handling by specifying the error page in WEB-INF/zk.xml as described in ZK
Configuration Reference. For example,
Then, when an error occurs in an event listener, the ZK Update Engine will create a dialog by the use of the error
page you specified, /error/error.zul.
Like error handling in loading a ZUML page, you can specify multiple <error-page> elements. Each of them is
associated with a different exception type (the value of <exception-type> element). When an error occurs, ZK will
search the error pages one-by-one until the exception type matches.
In addition, ZK passes a set of request attributes to the error page to describe what happens. These attribute are as
follows.
javax.servlet.error.exception_type java.lang.Class
javax.servlet.error.message java.lang.String
javax.servlet.error.exception java.lang.Throwable
For example, you can specify the following content as the error page.
<!-- optional: record the error for improving the app -->
<zscript>
org.zkoss.util.logging.Log.lookup("Fatal").error(
requestScope.get("javax.servlet.error.exception"));
</zscript>
</window>
Tips:
• The error page is created at the same desktop that causes the error, so you can retrieve the relevant information
from the desktop.
• The order to handle the thrown exception according to it's type is based on the <error-page>'s declaration
sequence in zk.xml.
Version History
Version Date Content
Actions and Effects 249
The client-side action (CSA) is used to control how to perform an action at the client. Typical use is to control the
effect of showing or hiding a widget. For example, with CSA, you could use the so-called slide-down effect to
display a widget.
It is a generic feature available to HtmlBasedComponent [3], so you could apply to almost all widgets.
CSA allows the developer to control some actions without JavaScript. If you want to have the full control (and are
OK to write some JavaScript code), please refer to ZK Client-side Reference for the complete control of the
client-side behavior.
The action name (e.g., action1) has to be one of the predefined names, such as show and hide. The action effect
(e.g., effect1) has to be one of the predefined effects, such as slideDown and slideUp.
For example, we could use the slide-down effect to display a window as follows[2].
<zk>
<button label="Show a modal window" onClick="wnd.doModal()"/>
<window id="wnd" title="Modal" border="normal" width="300px"
action="show: slideDown" visible="false">
This is a modal window.
</window>
</zk>
In addition, you could specify additions options by enclosing it with the parentheses as follows.
which specifies the duration of sliding down is 100 milliseconds, and the duration of sliding-up is 300 milliseconds.
Security Note: the optios is actually a JavaScript object (i.e., a map, Map [3]), and ZK passes whatever being
specified to the client for evaluation. Thus, if you allow the user to specify the effect, you shall encode it first to
avoid cross-site scripting.
Actions and Effects 250
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setAction(java. lang. String)
[2] If you are using the effects with a modal window, it is important to specify the width. Otherwise, the calculation of the position might be
wrong.
[3] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ _global_/ Map. html#
Predefined Actions
Here is a list of predefined actions.
Name Description
show The show action is used to display a widget (making a widget visible). When a visible widget is attached to a page, the show action
will take place too.
hide The hide action is used to hide a widget (making a widget invisible). When a visible widget is detached from a page, the hide action
will take place too.
invalidate The invalidate action is invoked when a visible widget is invalidated, i.e., when Component.invalidate() (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#invalidate()) is called. Example, action="invalidate: slideDown".
Predefined Effects
Here is a list of predefined actions.
Name Description
slideDown Slides down to display this widget (making a wdiget visible). Options:
• duration - the number of milliseconds to slide down the widget
slideOut Slides out to hide this widget (making a widget invisible). Options:
• duration - the number of milliseconds to slide out the widget
Custom Actions
If you want to take some actions other than the predefined actions listed above, you have to override the correspond
method at client. For example, suppose you'd like to change the color when a label's value (
Label.setValue(java.lang.String) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Label.
html#setValue(java.lang.String))) is changed. Then, you could do as follows:
<label id="inf2">
<attribute w:name="setValue">
function (value, fromServer) {
this.$setValue(value, fromServer);
if (this.desktop) {
this._red = !this._red;
this.setStyle('background:'+(this._red ?
'red':'green'));
}
}
Actions and Effects 251
</attribute>
</label>
Custom Effects
For adding your custom effects, please refer to ZK Client-side Reference: Customization: Actions and Effects for
details.
Version History
Version Date Content
Executions
org.zkoss.zk.ui.Executions [1]
getCurrent
Executions.getCurrent() [2]
Retrieves the current execution (request/response).
createComponents
Executions.createComponents() [3]
With this method, you can create components defined in another zul file and attach them to the current page.
sendRedirect
Executions.sendRedirect() [4]
Redirects to another URL. If the parameter is left null, it will redirect to the current page.
Sessions
org.zkoss.zk.ui.Sessions [5]
getCurrent
Sessions.getCurrent() [6]
Retrieves the current session.
Clients
org.zkoss.zk.ui.util.Clients [7]
This class offers a collection of methods which manipulate client side via AU Response.
evalJavaScript
Clients.evalJavaScript() [8]
This method sends an AU Response to execute the given JavaScript on client side, which is the standard way of
calling JavaScript from server side in ZK. For example,
Clients.evalJavaScript("zk.log('Hi.');");
Useful Java Utilities 253
scrollIntoView
Clients.scrollIntoView() [9]
Scrolls the parent of the given component, so the given one become visible in the view.
showBusy/clearBusy
Clients.showBusy() [10]
Clients.clearBusy() [11]
Display/dismiss a busy icon, so user knows server is working or has finished working on something. For example,
showNotification
[since 6.0.1]
Clients.showNotification() [12]
Show a notification box, which is dismissed upon left click (like Popup). You can either display a global notification
(bigger) or one specific to another component (smaller with an arrow pointing to it).
You can also specify its position, style, and duration (for auto-dismiss):
Closable
[since 6.5.0]
Notification now supports closable to let user close the notification box manually.
Multiline
To show a multiline message, just append
in the message string.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#getCurrent()
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#createComponents%28java. lang. String,%20org. zkoss. zk.
ui. Component,%20java. util. Map%29
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#sendRedirect%28java. lang. String%29
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Sessions. html
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Sessions. html#getCurrent()
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#evalJavaScript%28java. lang. String%29
[9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#scrollIntoView(org. zkoss. zk. ui. Component)
[10] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#showBusy(java. lang. String)
[11] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#clearBusy()
[12] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#showNotification(java. lang. String)
[13] http:/ / tracker. zkoss. org/ browse/ ZK-1145
HTML Tags 256
HTML Tags
Here we discuss how to use HTML tags directly in a ZUML document. There are several ways as described in the
following sections, and you could choose one based on your requirement.
In addition, you could use iframe to embed a complete HTML document which might be from a different website
with different technology. Or, use include to include a HTML fragment.
[1] We cannot update content dynamically at the server. However, we could modify the DOM tree directly at client. Please refer to the
Client-side UI Composing section.
[2] Technically you could modify the browser's DOM tree dynamically at the client.
[3] You could mix HTML tags with ZK components, if ZK JSP Tags (http:/ / www. zkoss. org/ product/ zkjsp. dsp) is used. Otherwise, you
could only have a JSP page to include other ZUL pages, or vice versa.
As shown above, we enclose them with <![CDATA[ and ]]> to prevent ZK Loader from interpreting the HTML
tags embedded in the html element. In other words, they are not the child component. Rather, they are stored in the
content property (by use of Html.setContent(java.lang.String) [1][2]. In other words, <h4>...</p> will become
the content of the html element.
Also notice that EL expressions are allowed.
The html Component 257
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Html. html#setContent(java. lang. String)
[2] Fore more information please refer to ZUML Reference.
<span id="z_4a_3">
<h4>Hi, Html Demo</h4>
<p>It is the content of the html component.</p>
</span>
It's a Component
The html component is no different to other XUL components. For example, you specify the CSS style and change
its content dynamically.
<zk>
<html id="h" style="border: 1px solid blue;background: yellow">
<![CDATA[
<ul>
<li>Native browser content</li>
</ul>
]]>
</html>
<button label="change" onClick="h.setContent("Hi, Update")" />
</zk>
htm.setContent("<ul><li>New content</li></ul>");
Limitation
Since SPAN is used to enclose the embedded HTML tags, the following code snippet is incorrect.
<zk>
<html><![CDATA[
<ul>
<li> <!-- incorrect since <ul><li> is inside <span> -->
]]>
</html>
<textbox />
<html><![CDATA[
</li>
</ul>
The html Component 258
]]>
</html>
</zk>
If you need to generate the embedded HTML tags directly without the enclosing SPAN tag, you can use the xhtml
component set or the native namespace as described in the following section.
Version History
Version Date Content
<n:ul xmlns:n="native">
<n:li>
<textbox/>
</n:li>
<n:li>
<textbox/>
</n:li>
</n:ul>
will attach the following HTML tags to the browser's DOM tree:
<ul>
<li>
<input id="z_a3_2"/>
</li>
<li>
<input id="z_a3_5"/>
</li>
</ul
where <input> is the HTML tag(s) generated by the textbox component. Unlike textbox in the example
above, ZK Loader doesn't really create a component for each of ul and li.[1] Rather, they are sent to the client
directly. Of course, they must be recognizable by the client. For a HTML browser, they must be the valid HTML
tags.
The native Namespace 259
[1] ZK ZK actually creates a special component to represent as many XML elements with the native namespace as possible.
Dynamic Update
The XML elements associated with the native namespace will be considered as tags that the client accepts, and they
are sent directly to the client to display. They are not ZK components, and they don't have the counterpart (widget) at
the client either. The advantage is the better performance in term of both memory and processing time.
However, the disadvantage that is you cannot access or change them (neither component nor widget) dynamically.
For example, the following code snippet is incorrect, since there is no component called x.
<n:ul id="x" xmlns:n="native"/>
<button label="add" onClick="new Li().setParent(x)"/> <!-- Failed since x is not available at the server -->
The rule of thumb is to use the native namespace if possible. If you need to change the content dynamically, you
might consider the html component first. If still not applicable, use the XHTML component set.
<n:ul xmlns:n="native">
</n:ul xmlns:n="native">
</window>
In Pure Java
You could use the native namespace in Java too. For example, you could create a native table directly as follows.
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlNativeComponent;
import org.zkoss.zul.Datebox;
<element xmlns="native:URI-of-another-namespace">
For example, if you want to output the XAML tags directly to the client, you can specify XAML's XML namespace
as follows.
<div>
<Canvas xmlns="native:http://schemas.microsoft.com/client/2007">
<TextBlock>Hello World!</TextBlock>
</Canvas>
</div>
<div id="zk_uuid">
<canvas xmlns="http://schemas.microsoft.com/client/2007">
<textblock>Hello World!</textblock>
</canvas>
</div>
[1] The real DOM structure of a component (div in this example) depends on its implementation. Here is only a simplified version.
Version History
Version Date Content
<h:ul xmlns:h="xhtml">
since 8.0.3
XHTML component supports HTML5 tag attributes, and these attributes could be accessed by MVVM. About
MVVM, please refer to the MVVM document [2].
Dynamic Update
Because Components are instantiated for XML elements specified with the XHTML namespace, you could update
its content dynamically on the server. For example, we could allow users to click a button to add a column as shown
below.
On the other hand, the native namespace will cause native HTML tags being generated. It means you can not modify
the content dynamically on the server. Notice that you still can handle them dynamically at the client.
However, when a XHTML component are used, a component running on the server has to be maintained. Thus, you
should use the XHTML component set only if there is no better way for doing it.
For example, we could rewrite the previous sample with the native namespace and some client-side code as follows.
<window title="mix HTML demo" xmlns:n="native">
<n:table border="1">
<n:tr id="row1">
<n:td>column 1</n:td>
<n:td>
<listbox id="list" mold="select">
<listitem label="AA"/>
<listitem label="BB"/>
</listbox>
</n:td>
</n:tr>
</n:table>
<button label="add" w:onClick="jq('#row1').append('<td></td>')" xmlns:w="client"/>
</window>
ID and UUID
Unlike other components, if you assign ID to a XHTML component, its UUID (Component.getUuid() [3]) is changed
accordingly. It means you cannot have two XHTML components with the same ID, no matter if they are in different
ID spaces.
Filename Extension
As described in ZUML, the XHTML component set is associated with zhtml, xhtml, html and htm. It means you
could name a ZUML page as foo.zhtml if you map *.zhtml to ZK Loader. However, when this kind of file is
interpreted, ZK Loader assumes it will have its own HTML, HEAD, BODY tags. On the other hand, these tags are
generated automatically if the filename extension is zul.
For example, suppose we have a file called foo.zhtml, then the content might look as follows.
<?link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"?>
<head>
<title>ZHTML Demo</title>
<zkhead/><!-- a special tag to indicate where to generate ZK CSS and JS files -->
</head>
<body style="height:auto">
<h1>ZHTML Demo</h1>
<ul id="ul">
</ul>
</body>
</html>
where
1. Since the extension is zhtml, the default namespace is XHTML. Thus, we have to specify the zk and zul
namespace explicitly.
• Notice that we have to specify the zk namespace too, because XHTML will cause ZK Loader to consider any
unrecognized element as native HTML tag.
2. We have to specify HTML, HEAD and BODY to make it a valid HTML document.
3. We could specify zkhead (line 5) to indicate where to generate ZK CSS and JavaScript files. It is optional. If not
specified, ZK will try to identify the proper location for ZK CSS and JavaScript files. Specify it if you want some
CSS or JavaScript file to be evaluated before or after ZK's default ones.
4. By default, BODY's CSS is width:100%;height:100%. However, it is appropriate for Web-look page[4]
For Web-look, we could specify height:auto to reset it back to the browser's default.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zhtml/ Ul. html#
[2] http:/ / books. zkoss. org/ zk-mvvm-book/ 8. 0/ introduction_of_mvvm. html
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getUuid()
[4] height:100% is more for desktop-application-look, such as using with <javadoc>org.zkoss.zul.Borderlayout.
Version History
Version Date Content
Long Operations 264
Long Operations
Events for the same desktop are processed sequentially. It simplifies the GUI programming and component
development. However, it means an event handler that spends a lot of time to execute will block any following
handlers. Worse of all, the user, due to the limitation of HTTP, got no hint but the small busy dialog shown at the
left-top corner on the browser.
There are basically two approaches:
1. Handle everything in an event thread and have the user to wait but show more visible message to notice him
2. Handle the long operation in an independent thread, such that the user can access other functions
The first approach could be done with a technique called echo events as describe in the Use Echo Events section.
The second approach can be done in several ways, such as starting a working thread to do the long operation and
then using a timer to check if the data ready and show to the client. However, there is a simple approach: use an
event queue to run an asynchronous listener as described in the Use Event Queues section.
In additions to above approaches, there is a special mechanism called piggyback, which could be used to piggy back
UI updates without extra network traffic.
<attribute name="onClick">
Clients.showBusy("Execute..."); //show a busy message to user
Events.echoEvent("onLater", w, null); //echo an event back
</attribute>
</button>
</window>
Then, the button will be disabled automatically when it is pressed, and enabled automatically when the request has
been served.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Events. html#echoEvent(java. lang. String,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Button. html#setAutodisable(java. lang. String)
Use Event Queues 266
((DesktopCtrl)desktop).enableServerPush(
new org.zkoss.zk.ui.impl.PollingServerPush(2000,5000,-1));
Example
We provide two implementations to illustrate how to use the event queue's asynchronous listener for executing a
long operation. The first approach is more generic that you can modify it to use more diverse situations. On the other
hand, the second approach is much simpler. If you don't have time, you could skip the first approach and study the
second approach.
A Generic Approach
A typical use case is to subscribe an asynchronous event listener for doing the long operation, and to subscribe a
synchronous event listener to update the user interface. Then, when starting a long operation, an event is posted to
the asynchronous event listener for processing. Since the invocation is asynchronous, the user can still interact with
ZK smoothly. At the end of the invocation of the asynchronous event listener, it published an event to the
synchronous event listener to update the result of the long operation back to the browser.
For example,
return; //busy
}
An asynchronous event listener is not allowed to access the desktop, but it is allowed to invoke
EventQueue.publish(org.zkoss.zk.ui.event.Event) [3] to publish an event.
A Simpler Approach
While subscribing the asynchronous and synchronous event listeners separately is geneic, as illustrated above, the
event queue provides a simple method to allow you register them in one invocation:
org.zkoss.zk.ui.event.EventListener) EventQueue.subscribe(org.zkoss.zk.ui.event.EventListener,
[4]
org.zkoss.zk.ui.event.EventListener) . In additions, you don't need to publish an event at the end of the
asynchronous event listener -- the synchronous event listener is invoked automatically.
new Label(msg).setParent(inf);
}
</zscript>
<button label="async long op">
<attribute name="onClick"><![CDATA[
if (EventQueues.exists("longop")) {
print("It is busy. Please wait");
return; //busy
}
Then, the button will be disabled automatically when it is pressed. Notice that we prefix self with +, and it means
you have to enable it manually (once it is OK to run again).
if (ready)
button.setDisabled(false); //enable it when ready
Use Event Queues 269
Version History
Version Date Content
References
[1] http:/ / code. google. com/ appengine/
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ DesktopCtrl. html#enableServerPush(org. zkoss. zk. ui. sys. ServerPush)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueue. html#publish(org. zkoss. zk. ui. event. Event)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueue. html#subscribe(org. zkoss. zk. ui. event. EventListener,
Use Piggyback
Sometimes there is no hurry to update the result to a client. Rather, the UI update could be sent back when the user,
say, clicks a button or triggers some request to the server. This technique is called piggyback.
In piggyback, all you need to do is to register an event listener for the onPiggyback event to one of the root
components. Then, the listener will be invoked each time ZK Update Engine has processed an AU request.
For example, suppose we have a long operation which is processed in a working thread, then:
void checkResult() {
while (!result.isEmpty())
main.appendChild(result.remove(0));
}
</zscript>
<timer id="timer" />
<button label="Start Working Thread">
<attribute name="onClick">
timer.start();
new test.WorkingThread(desktop, result).start();
</attribute>
</button>
</window>
The advantage of the piggyback is no extra traffic between the client and the server. However, the user sees no
updates if he doesn't have any activity, such as clicking. Whether it is proper is really up to the application
requirements.
Use Piggyback 270
Note: A deferrable event won't be sent to the client immediately, so the onPiggyback event is
triggered only if a non-deferrable event is fired. For more information, please refer to the Deferrable
Event Listeners section.
Version History
Version Date Content
Communication
Here we dicuss how to communicate among pages, desktops and Web applications.
Inter-Page Communication
Communicating among pages in the same desktop is straightforward. First, you can use attributes to share data.
Second, you can use event to notify each other.
Identify a Page
To communicate among pages, we have to assign an identifier to the target page. In ZUML, it is done by the use of
the page directive:
Then we could retrieve it by use of Desktop.getPage(java.lang.String) [1] or by use of a utility class called Path [8]
.
For example, the following statements could access the main window above:
comp.getDesktop().getPage("foo").getFellow("main");
Path.getComponent("//foo/main");
As shown, Path.getComponent(java.lang.String) [2] considers an ID starting with double slashes as a page's ID.
Use Attributes
Each component, page, desktop, session and Web application has an independent map of attributes. It is a good place
to share data among components, pages, desktops and even sessions.
In Java , you could use "setAttribute()","removeAttribute()" and "getAttribute()" of Component [1], Page [8] and so
on to share data. Another way is using the scope argument to identify which scope you want to access. (In the
following example, assuming comp is a component.)
comp.setAttribute("some","anyObject");
comp.getAttribute("some", comp.DESKTOP_SCOPE);
comp.getDesktop().getAttribute("some"); //is equivalent to previous
line
Inter-Page Communication 271
In zscript and EL expressions, you could use the implicit objects: componentScope, pageScope,
desktopScope, sessionScope, requestScope and applicationScope.
<window>
<zscript><![CDATA[
desktop.setAttribute("some","anyObject");
desktopScope.get("some");
]]></zscript>
1:${desktopScope["some"]}
</window>
Events.postEvent(new Event("SomethingHappens",
comp.getDesktop().getPage("foo").getFellow("main"));
You can also pass the data with the event object. The third parameter in
[3] [5]
Events.postEvent(org.zkoss.zk.ui.event.Event) will be put into Event.getData() . You could the data you want
with it.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Desktop. html#getPage(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Path. html#getComponent(java. lang. String)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Events. html#postEvent(org. zkoss. zk. ui. event. Event)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Events. html#sendEvent(org. zkoss. zk. ui. event. Event)
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ Event. html#getData()
Inter-Desktop Communication 272
Inter-Desktop Communication
Unlike pages, you cannot access two desktops at the same time. You cannot send or post an event from one desktop
to another directly either. Rather, we have to use an event queue with a proper scope, such as group, session or
application -- depending on where the other desktop is located.
Notice that the desktop-scoped event queue does not require Server Push, so there is no performance impact at all.
Here is a dumb example: chat among iframes.
<separator/>
<iframe src="includee.zul" height="500px" width="30%"/>
<iframe src="includee.zul" height="500px" width="30%"/>
<iframe src="includee.zul" height="500px" width="30%"/>
</window>
Version History
Version Date Content
Inter-Desktop Communication 274
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueues. html#DESKTOP
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueues. html#APPLICATION
Inter-Application Communication
An EAR file or the installation of Web server could have multiple WAR files. Each of them is a Web application.
There are no standard way to communicate between two Web applications. However, there are a few ways to work
around it.
<include src="~app2/foreign.zul"/>
<style src="~app2/foo.css"/> <!-- assume foo.css is in the context called app2 -->
<image src="~/foo.png"/> <!-- assume foo.png is in the root context -->
Note: Whether you can access a resource located in another Web application depends on the configuration of the
Web server. For example, you have to specify crossContext="true" in conf/context.xml, if you are using
Tomcat.
Limitation
[1]
Cross-context access is not always allowed in a container, e.g. Tomcat, you need to enable crossContext first
before including another context resources.
Use Cookie
Cookie [2] is another way to communicate among Web applications. It can be done by setting the path to "/", such
that every Web application in the same host will see it.
HttpServletResponse response =
(HttpServletResponse)Executions.getCurrent().getNativeResponse();
Cookie userCookie = new Cookie("user", "foo");
userCookie.setPath("/");
response.addCookie(userCookie);
Inter-Application Communication 275
<image src="~./my/jar.gif"/>
Then, it tries to locate the resource, /my/jar.gif, at the /web directory by searching resources from the
classpath. Notice that WEB-INF/classes is also part of the classpath, so you could put it under
WEB-INF/classes/web/my/jar.gif too.
Version History
Version Date Content
References
[1] https:/ / tomcat. apache. org/ tomcat-9. 0-doc/ config/ context. html
[2] http:/ / en. wikipedia. org/ wiki/ HTTP_cookie
Templating
Templating is a technique that allows developers to define UI fragments, and how to assemble them into a complete
UI at runtime. With ZK, it can be done by the use of annotations and composers (or initators, utilInitiator [1]).
In general, templating can be done by specifying the name of a fragment as annotations in a ZUML document that
shall represent a complete UI, and a composer that is capable to parse annotations and replace them with the
fragment. For example,
<div apply="foo.MyTemplateManager"><!-- your template manager -->
<include src="@header()"/><!-- you could use any component as long as your manager knows how to handle it -->
<include src="@content()"/>
<include src="@footer()"/>
</div>
Here is a list of the implementations that ZK supports by default. You could implement your own, if it does not
fulfill your requirement. If the templating is stateful and dynamical, you might consider ZK Spring [2] for using
Spring Web Flow instead.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ utilInitiator. html#
[2] http:/ / www. zkoss. org/ product/ zkspring. dsp
Composition 276
Composition
Composition [1] is one of the built-in templating implementations. The concept is simple:
1. Define a template (a ZUML document representing a complete UI)
2. Define a ZUML document that contains a collections of fragments that a template might reference
Notice that the user shall visit the ZUML document with a collection of fragments rather than the template
document.
The advantage of Composition [1] is that you don't need additional configuration file.
Note: the composition doesn't support to mix up with ZUML and ZHTML language, that is, if you define a ZHTML
template as the HTML content that contains Html and Body tags, you cannot use that template in a ZUML page.
Defines a Template
A template document is a ZUML document that defines how to assemble the fragments. For example,
As shown, the anchor (i.e., the component that a fragment will insert as children) is defined by specify an annotation
as @insert(name). Then, when Composition [1] is applied to a ZUML document with a collections of fragments,
the matched fragment will become the child of the annotated component (such as hbox in the above example).
Define Fragments
To apply a template to a ZUML document that an user visits, you have to defined a collection of fragments that a
template might use, and then specify Composition [1] as one of the initiators of the document:
<vbox>
<hbox>
<window title="window1" width="100px"/>
<window title="window2" width="200px"/>
</hbox>
<hbox>
<grid width="300px" height="100px"/>
</hbox>
</vbox>
Multiple Templates
You could apply multiple templates to a single page too:
<?init class="org.zkoss.zk.ui.util.Composition"
arg0="/WEB-INF/layout/template0.zul"
arg1="/WEB-INF/layout/template1.zul"?>
The templates specified in arg0 and arg1 (etc.) will be loaded and rendered one-by-one.
Then, you could group fragments into one or multiple individual ZUL documents, such as
Positioning
If you want to use Composition [1] inside any of containers (like Div,Window,Tabbox) , you have to use the include
component and set its mode Defer :
Note: You have to specify Composition [1] as of the initiators of the 'fragments')
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Composition. html#
Templates
As described in the MVC: Template section, a template is a ZUML fragment that defines how to create components.
A template is enclosed with the template element as shown below.
<window>
<template name="foo">
<textbox/>
<grid model=${data}>
<columns/>
<template name="model"> <!-- nested template -->
<row>Name: <textbox value=${each.name}"/></row>
</template>
</grid>
</template>
...
The third argument of the create method is a variable resolver (VariableResolver [2]). Depending on the
requirement, you could pass any implementation you like. For example, the implementation of a listbox actually
Templates 279
utilizes it to return the data being rendered; the code is similar to the following (for easy understanding, the code has
been simplified).
For more detailed information about the variable resolver, please refer to ZUML Reference.
In addition, the template allow users to specify any number of parameters with any name, and these parameters can
be retrieved back by the getParameters method of the Template interface:
If the content of a template is located elsewhere as a separated file, to reference it, specify it in the src attribute as
follows.
Children Binding
Since 8.0 we suggest using shadow component <forEach> [2] as a replacement of children binding.
ZK Data Binding provides a powerful way called Children Binding to render a template based on the data (such as a
list of elements). Moreover, the UI will be updated automatically if the data has been changed. For more
information, please refer to the Children Binding section.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#getTemplate(java. lang. String)
[2] http:/ / books. zkoss. org/ zk-mvvm-book/ 8. 0/ shadow_elements/ iterate_collections. html
XML Output
In addition to generating HTML output to a browser, ZK could be used to generate (static) XML output to any client
that recognizes it, such as RSS [1] and Web Services [2].
Using ZK to generate XML output is straightforward:
1. Uses the XML component set (http://www.zkoss.org/2007/xml and shortcut is xml).
2. Maps the file extension to ZK Loader
3. Maps the file extension to the XML component set
The XML component set also provides some special components, such as transformer that supports XSTL. Fore
more information please refer to XML Components.
<?page contentType="image/svg+xml;charset=UTF-8"?>
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:z="zk">
<z:zscript><![CDATA[
String[] bgnds = {"purple", "blue", "yellow"};
int[] rads = {30, 25, 20};
]]></z:zscript>
<circle style="fill:${each}" z:forEach="${bgnds}"
cx="${50+rads[forEachStatus.index]}"
cy="${20+rads[forEachStatus.index]}"
r="${rads[forEachStatus.index]}"/>
</svg>
</circle>
<circle style="fill:yellow" cx="70" cy="40" r="20">
</circle>
</svg>
where
• The content type is specified with the page directive. For SVG, it is image/svg+xml. The xml processing
instruction (<?xml?>) and DOCTYPE of the output are also specified in the page directive.
• All tags in this example, such as svg and circle, are associated with a namespace
(http://www.w3.org/2000/svg) that is unknown to ZK Loader. Thus, they are assumed to belong the native
namespace. They are output directly rather than instantiating a ZK component for each of them.
• To use zscript, forEach and other ZK specific features, you have to specify the ZK namespace (zk).
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.svg</url-pattern>
</servlet-mapping>
<language-mapping>
<language-name>xml</language-name>
<extension>svg</extension>
</language-mapping>
where xml is the language name of the XML component set. Thus, when ZK Loader parses a file with the .svg
extension, it knows the default language is the XML component set[1].
[1] For more information about language identification, please refer to ZUML Reference.
Version History
Version Date Content
Event Threads 282
Event Threads
[Since ZK 7.0.0 deprecated to enable the event thread according to Java Servlet Specification that may prohibit the creation of new threads]
By default, ZK processes an event in the same Servlet thread that receives the HTTP request. It is the suggested
approach because the performance is better and it is easy to integrate with other frameworks[1].
However, it also implies the developer cannot suspend the execution. Otherwise, the end user won't see any updates
from the server. To solve it, ZK provides an alternative approach: processes the event in an independent thread
called the event processing thread. Therefore, the developer can suspend and resume the execution at any time,
without blocking the Servlet thread from sending back the responses to the browser. To turn it on[2], you have to
specify the following in WEB-INF/zk.xml (ZK Configuration Guide: disable-event-thread).
<system-config>
<disable-event-thread>false</disable-event-thread>
</system-config>
In short, it is recommended to disable the event thread. Enable the event thread only if the project does not need to
integrate other frameworks (such as Spring), depends on Messagebox [1] and modal windows a lot, and does not have
a lot of concurrent users.
Here is the advantages and limitations about using the Servlet thread to process events. In the following sections we
will talk more about the limitations and workarounds when using the Servlet thread.
Integration Less integration issues. You may have to implement EventThreadInit and/or
Many containers assume the HTTP request is EventThreadCleanup to solve the integration issue, such as copying the
handled in the Servlet thread, and many per-request information from the Servlet thread to the event processing thread.
frameworks store per-request information in the Threre are several implementations to solve the integration issue, such as
thread local storage. [3]
HibernateSessionContextListener (they can be found under the
[4]
org.zkoss.zkplus package ).
Performance No extra cost It executes a bit slower to switch from one thread to another, and it might
consume a lot more memory if there are a lot of suspended event processing
threads.
[1] Many frameworks store per-request information in the thread-local storage, so we have to copy them from Servlet thread to the Event
Processing Thread.
[2] For ZK 1.x, 2.x and 3.x, the event processing thread is enabled by default.
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ hibernate/ HibernateSessionContextListener. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ package-summary. html
Modal Windows 283
Modal Windows
Modal Windows with Servlet Thread
When the event is processed in the Servlet thread (default), the execution cannot be suspended. Thus, the modal
window behaves the same as the highlited window (Window.doHighlighted() [1]). At the client side, the visual effect
is the same: a semi-transparent mask blocks the end user from access components other than the modal window.
However, at the server side, it works just like the overlapped mode – it returns immediately without waiting for
user's closing the window.
The "next" message will be printed to the console before the end user closes the modal window.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#doHighlighted()
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#doModal()
Message Box 284
Message Box
Message Boxes with Servlet Thread
When Messagebox.show(java.lang.String) [1] is called, it returns immediately after showing the message dialog.
Furthermore, it always returns Messagebox.OK. Thus, it is meaningless to show buttons other than the OK button.
For example, the if clause in the following example is never true.
The event listener you provided is invoked when the user clicks one of the buttons. Then, you can identify which
button is clicked by examining the data (Event's getData). The data is an integer whose value is the button's
identifier, such as Messagebox.YES.
Alternatively, you can examine the event name:
Note: The event name for the OK button is onOK, not onOk. Notice: If you want to make it run under clustering
environment, you shall implement SerializableEventListener [2]. For more information, please refer to ZK
Developer's Reference: Clustering.
Message Box 285
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Messagebox. html#show(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ SerializableEventListener. html#
File Upload
File Upload with Servlet Thread
[2] [1] [2]
When the event thread is disable (default), it is recommended to use Button , Toolbarbutton or Menuitem
with upload="true" instead. For example,
<zk>
<zscript>
void upload(UploadEvent event) {
org.zkoss.util.media.Media media = event.getMedia();
if (media instanceof org.zkoss.image.Image) {
org.zkoss.zul.Image image = new
org.zkoss.zul.Image();
image.setContent( (org.zkoss.image.Image) media);
image.setParent(pics);
} else {
Messagebox.show("Not an image: "+media, "Error",
Messagebox.OK, Messagebox.ERROR);
}
}
</zscript>
<button label="Upload" upload="true" onUpload="upload(event)"/>
<toolbarbutton label="Upload" upload="true" onUpload="upload(event)"/>
<vbox id="pics" />
</zk>
[1]
If you prefer to use a dialog (Fileupload.get() ), please take a look at ZK Component Reference: Fileupload for
more inormation.
File Upload 286
<zk>
<button label="Upload">
<attribute name="onClick">{
org.zkoss.util.media.Media[] media = Fileupload.get(-1);
if (media != null) {
for (int i = 0; i < media.length; i++) {
if (media[i] instanceof org.zkoss.image.Image)
{
org.zkoss.zul.Image image = new
org.zkoss.zul.Image();
image.setContent(media[i]);
image.setParent(pics);
} else {
Messagebox.show("Not an image:
"+media[i], "Error", Messagebox.OK, Messagebox.ERROR);
break; //not to show too many errors
}
}
}
}</attribute>
</button>
<vbox id="pics" />
</zk>
As shown, Fileupload.get(int) [2] won't return until the end user uploads the files (and/or closes the dialog).
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Fileupload. html#get()
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Fileupload. html#get(int)
Theming and Styling 287
Standard themes are just some jar files that once imported into an web application's WEB-INF/lib, would be
ready to use. Web developers could also create custom themes and package them inside java archive files.
• Folder-based Themes
Starting from ZK 6.5.2, however, themes could be packaged inside a folder. A new theme can be created by
first cloning the folder containing an existing theme and then making the necessary changes to the stylesheets
and images.
The CSS styling really depends on the implementation of the component (and the mold). It is suggested to refer to
ZK Style Guide. In addition, if you have any doubt, you can use the HTML or CSS inspector shipped with the
browser, such as Firebug [1] for Firefox, and Developer Tools [2] for Internet Explorer, to investigate how CSS styles
are used.
Last Update : 2018/3/6
References
[1] http:/ / getfirebug. com/
[2] http:/ / en. wikipedia. org/ wiki/ Internet_Explorer_Developer_Toolbar
Molds
A component could have multiple different visual appearances. Each appearance is called a mold. A mold is
basically a combination of a DOM structure plus CSS. That is, it is the visual representation of a component.
Developers could dynamically change the mold by use of Component.setMold(java.lang.String) [1].
All components support at least a mold called default, which is the default value. Some components might have
support two or more molds. For example, tabbox supports both default and accordion molds.
If tabbox's mold is not set, it uses the default mold.
<tabbox>
<tabs>
<tab label="First tab" />
<tab label="Second tab" />
</tabs>
<tabpanels>
<tabpanel>The first panel.</tabpanel>
<tabpanel>The second panel.</tabpanel>
</tabpanels>
</tabbox>
And you could set tabbox's mold to "accordion" to change the look.
<tabbox mold="accordion">
<tabs>
<tab label="First tab" />
<tab label="Second tab" />
</tabs>
<tabpanels>
<tabpanel>The first panel.</tabpanel>
<tabpanel>The second panel.</tabpanel>
</tabpanels>
Molds 289
</tabbox>
For more information about component's molds, please refer to ZK Component Reference.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#setMold(java. lang. String)
style
Specifying the style is straightforward:
or, in Java:
sclass
[4]
In addition, you could specify the style class by use of HtmlBasedComponent.setSclass(java.lang.String) , such
that you could apply the same CSS style to multiple components.
<window>
<style>
.red {
color: blue;
font-style: oblique;
}
</style>
<textbox sclass="red" /> <!-- first textbox -->
<textbox sclass="red" /> <!-- another textbox -->
</window>
You could apply multiple style classes too. As shown below, just separate them with a space.
zclass
Like sclass, zclass is used to specify the CSS style class. However, zclass is the main CSS that each mold of each
component has. If it is changed, all default CSS of the given component won't be applied. In other words, you have
to provide a full set of CSS rules that a component's mold has.
Rule of thumb: specify zclass if you want to customize the look completely. Otherwise, use sclass to customize one
or a few CSS styles.
For more more information, please refer to ZK Style Guide.
Scrollable Window
A typical use of contentStyle is to make a window scrollable as follows.
<window title="Scroll Example" width="150px" height="100px" contentStyle="overflow:auto" >
This is a long line to spread over several lines, and more content to
display.
Finally, the scrollbar becomes visible.
This is another line.
</window>
CSS Classes and Styles 291
Version History
Version Date Content
References
[1] http:/ / en. wikipedia. org/ wiki/ Cascading_Style_Sheets
[2] http:/ / www. w3schools. com/ css/ default. asp
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setStyle(java. lang. String)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setSclass(java. lang. String)
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#setZclass(java. lang. String)
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#setContentStyle(java. lang. String)
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#setContentSclass(java. lang. String)
Apart from having a name, a theme could be associated with many attributes. Encapsulating theme-specific attributes
is defined in Theme [1]. Each theme should have at least a name, which helps the web application to identify it. Web
developers should extend this abstract class to define other attributes associated with concrete themes, such as file
paths included in a theme, or variables that could be used to parameterize a theme.
Standard themes have additional attributes like a more descriptive name for displaying purposes, a priority value to
help the system choose the theme to use, and a origin of the theme's resources (i.e. CSS and image files). Standard
theme information is provided by StandardTheme [2].
Example
import org.zkoss.web.theme.Theme;
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ theme/ Theme. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ theme/ StandardTheme. html#
Registration, in its simplest form, is to tell the web application about the name of the theme. It will be assumed that
the theme is for desktop only, and its resources should be retrieved from a jar file. For example,
Themes.register("custom");
Starting from ZK 6.5.2, theme resources could also be retrieved from a folder. To indicate that a theme is
folder-based, please use ThemeOrigin [2] to specify the origin of the theme resources, like below.
Themes.register("custom", ThemeOrigin.FOLDER);
Available in ZK EE 6.5.2+
In ZK EE 6.5.0+, components would appear differently when viewed on tablet devices. A custom theme could be
applicable to desktop only, tablet only, or both. To signify that a theme also serves tablet devices, please attach a
"tablet:" prefix in front of the theme name when registering. For example,
Themes.register("tablet:custom");
ThemeRegistry [3] defines the interface to create a repository of themes available to the web application.
DesktopThemeRegistry [4] (for ZK CE/PE) and ResponsiveThemeRegistry [5] (for ZK EE) are the standard
implementations that actually stores the registered themes.
Standard theme registries would accept all theme registrations. Duplicate registration would update theme
information. Registered themes are available to all users. (For ZK EE, desktop clients would only have desktop
themes available, and tablet clients would only have tablet themes available.) If you would like to modify any of
these behaviors, please provide a custom ThemeRegistry.
[3]
For example, a custom ThemeRegistry could be created by implementing ThemeRegistry interface directly, or
extending one of the standard implementations, depending on the ZK edition you are using.
package foo;
<zk>
<desktop-config>
<theme-registry-class>foo.CustomThemeRegistry</theme-registry-class>
</desktop-config>
</zk>
[6]
To access the current theme registry, please refer to ThemeFns.getThemeRegistry() and
ThemeFns.setThemeRegistry(org.zkoss.web.theme.ThemeRegistry) [7].
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ theme/ Themes. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ theme/ StandardTheme/ ThemeOrigin. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ theme/ ThemeRegistry. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ theme/ DesktopThemeRegistry. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ theme/ ResponsiveThemeRegistry. html#
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ fn/ ThemeFns. html#getThemeRegistry()
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ fn/ ThemeFns. html#setThemeRegistry(org. zkoss. web. theme. ThemeRegistry)
Switching Themes
The user can switch to a registered theme by setting a cookie or a library property. Please ensure your application
contains the corresponding theme jar. (The default theme doesn't require a separate jar file)
Standard theme resolution checks the theme settings in the following order.
1. Cookies
2. Library property
3. Theme priority
<library-property>
<name>org.zkoss.theme.preferred</name>
<value>breeze</value>
<!--<value>deepsea</value>--> <!-- no whitespace in theme names -->
</library-property>
Switching Themes 295
Themes.setTheme(Executions.getCurrent(), "custom");
Executions.sendRedirect("");
Library.setProperty("org.zkoss.theme.preferred", "custom");
Executions.sendRedirect("");
Theme Priority
If the previous two options do not yield any result, the theme with the highest priority would be applied. Theme
priority is usually assigned when registering a theme but could also be changed dynamically.
Please refer to Themes [1] for its family of register() methods.
package foo;
@Override
public void setTheme(HttpServletRequest request,
HttpServletResponse response, String themeName) {
Session sess = request.getSession();
if (sess != null) {
sess.setAttribute("mytheme", themeName);
}
}
}
Switching Themes 296
<zk>
<desktop-config>
<theme-resolver-class>foo.SessionThemeResolver</theme-resolver-class>
</desktop-config>
</zk>
[3]
To access the current theme resolver, please refer to ThemeFns.getThemeResolver() and
ThemeFns.setThemeResolver(org.zkoss.web.theme.ThemeRegistry) [4].
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ theme/ CookieThemeResolver. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ theme/ ThemeResolver. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ fn/ ThemeFns. html#getThemeResolver()
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ fn/ ThemeFns. html#setThemeResolver(org. zkoss. web. theme.
ThemeRegistry)
Available in ZK 6.5.2+
Standard implementations of ThemeProvider are available for each of ZK editions (CE, PE and EE).
Edition ThemeProvider
ZK CE [2]
StandardThemeProvider
ZK PE [3]
StandardThemeProvider
ZK EE [4]
StandardThemeProvider
We will illustrate the theme provider with two examples. One is straightforward: set the corresponding attributes
based on the cookie. The other injects a fragment to the URI such that we can allow the browser to cache the CSS
file.
Providing Theme Resources 297
Examples
A Simple Example
In the following example, we store the preferred font size and the skin (theme) in the cookie and retrieve them when
required.
Since 7.0.0, the font size attributes are deprecated because of using LESS.
package my;
public class MyThemeProvider implements ThemeProvder {
public Collection getThemeURIs(Execution exec, List uris) {
if ("silvergray".equals(getSkinCookie(exec))) {
uris.add("~./silvergray/color.css.dsp");
uris.add("~./silvergray/img.css.dsp");
}
return uris;
}
public int getWCSCacheControl(Execution exec, String uri) {
return -1;
}
public String beforeWCS(Execution exec, String uri) {
final String fsc = getFontSizeCookie(exec);
if ("lg".equals(fsc)) {
exec.setAttribute("fontSizeM", "15px");
exec.setAttribute("fontSizeMS", "13px");
exec.setAttribute("fontSizeS", "13px");
exec.setAttribute("fontSizeXS", "12px");
} else if ("sm".equals(fsc)) {
exec.setAttribute("fontSizeM", "10px");
exec.setAttribute("fontSizeMS", "9px");
exec.setAttribute("fontSizeS", "9px");
exec.setAttribute("fontSizeXS", "8px");
}
return uri;
}
public String beforeWidgetCSS(Execution exec, String uri) {
return uri;
}
/** Returns the font size specified in cooke. */
private static String getFontSizeCookie(Execution exec) {
Cookie[] cookies =
((HttpServletRequest)exec.getNativeRequest()).getCookies();
if (cookies!=null)
for (int i=0; i<cookies.length; i++)
if ("myfontsize".equals(cookies[i].getName()))
Providing Theme Resources 298
return cookies[i].getValue();
return "";
}
/** Returns the skin specified in cookie. */
private static String getSkinCookie(Execution exec) {
Cookie[] cookies =
((HttpServletRequest)exec.getNativeRequest()).getCookies();
if (cookies!=null)
for (int i=0; i<cookies.length; i++)
if ("myskin".equals(cookies[i].getName()))
return cookies[i].getValue();
return "";
}
}
<desktop-config>
<theme-provider-class>my.MyThemeProvider</theme-provider-class>
</desktop-config>
A Cacheable Example
To improve the performance, it is better to allow the browser to cache the CSS file as often as possible. With the
theme provider, it can be done by returning a positive number when java.lang.String)
ThemeProvider.getWCSCacheControl(org.zkoss.zk.ui.Execution, java.lang.String) [5] is called. However, because
the browser will use the cached version, we have to ensure the browser gets a different URL for each different
theme. Here we illustrate a technique called fragment injection.
The idea is simple: when java.util.List) ThemeProvider.getThemeURIs(org.zkoss.zk.ui.Execution, java.util.List) [6]
is called, we inject a special fragment to denote the content, such that each different theme is represented with a
different URL. The injection can be done easily with the inner class called Aide [7]. For example,
Then, we can retrieve the fragment we encoded into the URI later when java.lang.String)
ThemeProvider.beforeWCS(org.zkoss.zk.ui.Execution, java.lang.String) [8] is called. It can be done easily by use of
Providing Theme Resources 299
//slivergray
if ("silvergray".equals(getSkinCookie(exec))) {
uris.add("~./silvergray/color.css.dsp");
uris.add("~./silvergray/img.css.dsp");
}
return uris;
}
Providing Theme Resources 300
In addition to String instances, you can return instances of StyleSheet [10] in the returned collection of
ThemeProvider.getThemeURIs(org.zkoss.zk.ui.Execution,java.util.List) [11], such that you can control more about
the generated CSS link. For example, if you want to add a CSS link for the media type [12], say, print,
handheld, then you can do as follows.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ThemeProvider. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ theme/ StandardThemeProvider. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkex/ theme/ StandardThemeProvider. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ theme/ StandardThemeProvider. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ThemeProvider. html#getWCSCacheControl(org. zkoss. zk. ui.
Execution,
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ThemeProvider. html#getThemeURIs(org. zkoss. zk. ui. Execution,
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ThemeProvider/ Aide. html#
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ThemeProvider. html#beforeWCS(org. zkoss. zk. ui. Execution,
[9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ThemeProvider/ Aide. html#decodeURI(java. lang. String)
[10] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ servlet/ StyleSheet. html#
[11] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ThemeProvider. html#getThemeURIs(org. zkoss. zk. ui. Execution,java.
util. List)
[12] http:/ / www. w3. org/ TR/ CSS2/ media. html
Resolving Theme URLs 302
uri = ServletFns.resolveThemeURL(uri);
}
return uri;
}
...
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ fn/ ServletFns. html#encodeThemeURL(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ fn/ ServletFns. html#resolveThemeURL(java. lang. String)
Customizing Standard Themes 303
[class*="z-"]:not([class*="z-icon-"]) {
font-family: Arial;
}
If you want to define the default font family for the whole page body (including html native element):
body *:not([class*="z-icon-"]) {
font-family: Arial;
}
Font Size
Deprecated since 7.0.0
The default theme uses the following library properties to control the font sizes.
Customizing Standard Themes 304
org.zkoss.zul.theme.fontSizeM 12px The default font size. It is used in the most components.
org.zkoss.zul.theme.fontSizeS 11px The smaller font size used in the component that requires small fonts, such as toolbar.
To change the default value, you can specify the library properties in WEB-INF/zk.xml as follows.
<library-property>
<name>org.zkoss.zul.theme.fontSizeM</name>
<value>12px</value>
</library-property>
<library-property>
<name>org.zkoss.zul.theme.fontSizeS</name>
<value>10px</value>
</library-property>
<library-property>
<name>org.zkoss.zul.theme.fontSizeXS</name>
<value>9px</value>
</library-property>
Font Family
Deprecated since 7.0.0
Name Description
.z-a-disd {
color: #C5CACB !important;
cursor: default!important;
text-decoration: none !important;
}
.z-a-disd:visited, .z-a-disd:hover {
text-decoration: none !important;
cursor: default !important;;
Customizing Standard Themes 305
<desktop-config>
<theme-uri>/css/my.css</theme-uri>
</desktop-config>
Version History
Version Date Content
References
[1] https:/ / www. zkoss. org/ wiki/ Small_Talks/ 2016/ May/ New_Approach_for_Building_Custom_ZK_Theme
Archive-based Themes
Before creating a new ZK theme, web designers need to understand its directory structure, let's start off by
discovering where the default theme (a.k.a. breeze) is. Basically, the default theme is contained inside three java
archive files: zul.jar (ZK CE [6]), zkex.jar (ZK PE [6]) and zkmax.jar (ZK EE [6]). Note: freshly or evaluation
versions will have special suffix to indicate the zk version and the build date. (e.g. zul-6.5.1.FL.20121204.jar).
As mentioned previously, a 'theme' is a collection of stylesheets and associated images for ZK's component set.
Stylesheets are the files with extension of ".css.dsp". Think of them as normal CSS files that could utilize JSP taglib
functionality. Associated images all have file extension either of ".gif" or ".png".
• Available for ZK:
•
In ZK EE, users will also have access to tablet-enhanced theme in zkmax.jar. In addition to the stylesheets and
associated images, the tablet-enhanced theme also contains a property file (default.theme-properties) that could be
used to easily customize attributes such as font-sizes and color values.
Once those resources are extracted from the respective java archives while preserving the original directory structure,
they can become a basis for a new theme, and be ready to be packaged inside a jar file.
The top level subdirectories for this folder should look similar to the figure below.
[3]
Since 7.0.0, we provide a maven-archetype that can easily create a ZK theme maven project, refer to the blog .
package foo;
<config>
...
<listener>
<listener-class>foo.DarkstarThemeWebInit</listner-class>
</listener>
...
</config>
Now, the component style modifications shall begin. Please refer to this smalltalk [5] for a more detailed example on
doing this. Even though the smalltalk is about folder-based themes, as far as modifying theme resources is
concerned, the procedure is the same.
Here would just summarize the steps.
General steps for component style modification:
1. Locate the stylesheet for a given component
2. Modify existing images or add new images as needed
3. Customize the component style by tweaking the stylesheet located in step 1
Archive-based Themes 308
If a component style rule needs to refer to images within the theme folder, please use the zk core taglib function
encodeThemeURL for path resolution. For example, to refer to zul/img/input/combo-btn.png under the dark
theme folder, use the following syntax.
Note: The special prefix ~./ will be interpreted as the theme folder root (e.g. /theme/dark/).
After all this has been done, the components should have their views customized. Please refer to this article [6]</ref>
for how to switch themes dynamically within the ZK application.
• Available for ZK:
•
Developers could also follow the same process described above to tailor the appearance of ZK components when
viewed on tablets. When locating the stylesheets to modify, look inside the ~./zkmax/css/tablet folder instead.
For ZK EE users, custom themes could support styling for desktop-only, tablet-only, or both. Web application needs
to know about the platforms a custom theme may support. This is also accomplished through theme registration.
When a custom theme overrides the default tablet theme, its theme name must be prefixed with "tablet:" before
making registration. For example, to notify the web application that 'dark theme is tablet-capable, please use the
following code snippet.
Themes.register("tablet:dark");
In addition, the default tablet-enhanced theme has refactored many attributes such as color values, font-sizes,
border-widths, ... into a property file in ~./zkmax/default.theme-properties. Changing the attribute values inside
this property file could quickly alter the appearance of the components without touching their stylesheets.
Nevertheless, developers may also combine these two approaches to suit their needs.
After the new theme is developed, the theme project can be packaged as a jar file for distribution, say darkstar.jar.
This could be done by execute the command mvn clean package at the project root.
References
[1] ZK Themes (https:/ / github. com/ zkoss/ zkthemes)
[2] ZK Default Theme Extractor Utility. Please download at github (https:/ / gist. github. com/ raw/ 4334775/
e5d669bb873443aa03f8febffccd3fc4b2518ecb/ ztx. bat).
[3] http:/ / blog. zkoss. org/ index. php/ 2013/ 09/ 17/ zk7-create-a-new-a-theme-project/
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ WebAppInit. html#
[5] http:/ / http:/ / books. zkoss. org/ index. php?title=Small_Talks/ 2013/ January/ Packaging_Themes_Inside_Folders_in_ZK_6. 5. 2
[6] http:/ / books. zkoss. org/ wiki/ ZK_Developer%27s_Reference/ Theming_and_Styling/ Understanding_the_Theming_Subsystem/
Switching_Themes
Folder-based Themes
Before creating a new ZK theme, web designers need to understand its directory structure, let's start off by
discovering where the default theme (a.k.a. breeze) is. Basically, the default theme is contained inside three java
archive files: zul.jar (ZK CE [6]), zkex.jar (ZK PE [6]) and zkmax.jar (ZK EE [6]). Note: freshly or evaluation
versions will have special suffix to indicate the zk version and the build date. (e.g. zul-6.5.1.FL.20121204.jar).
As mentioned previously, a 'theme' is a collection of stylesheets and associated images for ZK's component set.
Stylesheets are the files with extension of ".css.dsp". Think of them as normal CSS files that could utilize JSP taglib
functionality. Associated images all have file extension either of ".gif" or ".png".
• Available for ZK:
•
In ZK EE, users will also have access to tablet-enhanced theme in zkmax.jar. In addition to the stylesheets and
associated images, the tablet-enhanced theme also contains a property file (default.theme-properties) that could be
used to easily customize attributes such as font-sizes and color values.
Once those resources are extracted from the respective java archives while preserving the original directory structure,
they can be placed inside a folder, and become a basis for a new theme.
The top level subdirectories for this folder should look similar to the figure below.
<servlet>
<servlet-name>dspLoader</servlet-name>
<servlet-class>org.zkoss.web.servlet.dsp.InterpreterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dspLoader</servlet-name>
<url-pattern>*.dsp</url-pattern>
</servlet-mapping>
Next, the new theme will need to be registered first before it could be used by the ZK application. Since the origin of
the new theme is from a folder, ZK 6.5.2 extends the theme registration API for this purpose. ThemeOrigin is an
enum defined to specify the origin of the registered theme. It has two valid values: JAR (default) and FOLDER.
Since ThemeOrigin.JAR is the default value, the extended theme registration API is only needed in the case of
folder-based themes. Theme registration could be done in the initialization code of the view model.
For example,
...
import org.zkoss.zul.theme.Themes;
import org.zkoss.web.theme.StandardTheme.ThemeOrigin;
...
public class MainViewModel {
...
@Init
public void init() {
...
Themes.register("dark", ThemeOrigin.FOLDER);
...
}
...
}
Folder-based Themes 311
Now, the component style modifications shall begin. Please refer to this smalltalk for a more detailed example on
doing this. Here would just summarize the steps.
General steps for component style modification:
1. Locate the stylesheet for a given component
2. Modify existing images or add new images as needed
3. Customize the component style by tweaking the stylesheet located in step 1
If a component style rule needs to refer to images within the theme folder, please use the zk core taglib function
encodeThemeURL for path resolution. For example, to refer to zul/img/input/combo-btn.png under the dark
theme folder, use the following syntax.
Note: The special prefix ~./ will be interpreted as the theme folder root (e.g. /theme/dark/).
After all this has been done, the components should have their views customized. Please refer to this article [6]</ref>
for how to switch themes dynamically within the ZK application.
• Available for ZK:
•
Developers could also follow the same process described above to tailor the appearance of ZK components when
viewed on tablets. When locating the stylesheets to modify, look inside the ~./zkmax/css/tablet folder instead.
For ZK EE users, custom themes could support styling for desktop-only, tablet-only, or both. Web application needs
to know about the platforms a custom theme may support. This is also accomplished through theme registration.
When a custom theme overrides the default tablet theme, its theme name must be prefixed with "tablet:" before
making registration. For example, to notify the web application that 'dark theme is tablet-capable, please use the
following code snippet.
Themes.register("tablet:dark", ThemeOrigin.FOLDER);
In addition, the default tablet-enhanced theme has refactored many attributes such as color values, font-sizes,
border-widths, ... into a property file in ~./zkmax/default.theme-properties. Changing the attribute values inside
this property file could quickly alter the appearance of the components without touching their stylesheets.
Nevertheless, developers may also combine these two approaches to suit their needs.
After the new theme is developed, the entire theme folder can be exported as a zip file for distribution, say dark.zip.
Folder-based Themes 312
<library-property>
<name>org.zkoss.theme.folder.root</name>
<value>view/themes</value>
</library-property>
Themes.register("darkstar", ThemeOrigin.FOLDER);
// For ZK EE, also make customized tablet theme available
if ("EE".equals(WebApps.getEdition()))
Themes.register("tablet:darkstar", ThemeOrigin.FOLDER);
This code fragment can be written in several places. For an example incorporating MVVM, please refer to the
section Modify the theme resource. To make the folder-based theme available at application startup, write a class
implementing the WebAppInit [3] interface and place the above code inside the init() function.
The configuration file WEB-INF/zk.xml must also include the following configuration item.
<listener>
<listener-class>DarkstarThemeWebAppInit</listener-class>
</listener>
<library-property>
<name>org.zkoss.theme.preferred</name>
<value>darkstar</value>
</library-property>
References
[1] ZK Default Theme Extractor Utility. Please download at github (https:/ / gist. github. com/ raw/ 4334775/
e5d669bb873443aa03f8febffccd3fc4b2518ecb/ ztx. bat).
[2] Please refer to ZK Installation Guide (http:/ / books. zkoss. org/ wiki/ ZK_Installation_Guide)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ WebAppInit. html
ZK Official Themes
Overview
This is an overview of the default themes and applicable add-on themes for each ZK version.
Applicable Sapphire, Sapphire, Silvertail, Sapphire, Silvertail, Sapphire, Silvertail, Atlantic, ZK Theme Pack (23
Themes Silvertail Atlantic Atlantic Themes)
ZK Official Themes 314
Breeze
ZK Official Themes 315
Sapphire
Silvertail
ZK Official Themes 316
Atlantic
ZK Theme Pack contains 23 modern themes, including lite themes, dark themes and mix-match themes. These
themes are designed for ZK 8.5 and later versions and are compatible with ZK 8.5's default Iceblue theme.
Lite themes: Poppy, Marigold, Olive, Aurora, Iceblue(default), Lavender
Dark themes: Ruby, Amber, Emerald, Aquamarine, Montana, Violet
Mix-match: Cheese and Wine, Winter Spring, Blueberry and Raspberry, Macaron, Deep Sea, Garden Salad, Zen,
Mysterious Green, Cardinal, Space Black, Office and Material
You can find below some selected screenshots from ZK Theme Pack.
Lite
ZK Official Themes 317
Dark
Mix-matched
ZK Official Themes 318
Installation
1. Download the preferred theme.jar file or obtain by maven.
• Default theme (Breeze or Iceblue) - built-in theme, no need to download and register.
• Sapphire, Silvertail, Atlantic - from github [1]
• ZK Theme Pack - download from premium downloads [2] or ZK EE maven repository [3].
2. Place theme.jar file under "projectName/WEB-INF/lib" folder.
3. Apply preferred theme by a library property in zk.xml file under "projectName/WEB-INF" folder
Switching Themes
You can include multiple themes in the same application and allow your end users to choose their preferred themes.
For more information please refer to Switching Themes [4]
Customizing a Theme
To build a custom theme based on a standard ZK theme, read Customize a standard theme [5].
Also, reference Theming and Styling [6].
Version History
Version Date Content
References
[1] http:/ / github. com/ zkoss/ zkthemes/ releases
[2] https:/ / www. zkoss. org/ download/ premium#zktheme
[3] https:/ / www. zkoss. org/ wiki/ ZK_Installation_Guide/ Setting_up_IDE/ Maven/ Resolving_ZK_Framework_Artifacts_via_Maven#3. _PE_.
2F_EE_. 28premium_users_only. 29
[4] https:/ / www. zkoss. org/ wiki/ ZK_Developer%27s_Reference/ Theming_and_Styling/ Understanding_the_Theming_Subsystem/
Switching_Themes
[5] https:/ / www. zkoss. org/ wiki/ ZK_Developer%27s_Reference/ Theming_and_Styling/ Customizing_Standard_Themes
[6] https:/ / www. zkoss. org/ wiki/ ZK_Developer%27s_Reference/ Theming_and_Styling
ZK Bootstrap Theme
[1]
ZK Bootstrap theme is a ZK addon to provide a set of molds for Bootstrap V3 , and developer can apply this
addon to combine ZK with Bootstrap styling seamlessly.
Installation
1. Download the ZK Bootstrap jar file from Github [2] or use #Maven Installation
2. Put the zk-bootstrap.jar file under WEB-INF/lib folder
3. Specify the following setting in zk.xml. (Note: if you don't want to apply for all default mold, you can skip this
step.)
<library-property>
<name>org.zkoss.zul.Button.mold</name>
<value>bs</value>
</library-property>
<library-property>
<name>org.zkoss.zul.Menupopup.mold</name>
<value>bs</value>
</library-property>
<library-property>
<name>org.zkoss.zkmax.zul.Navbar.mold</name>
<value>bs</value>
ZK Bootstrap Theme 320
</library-property>
<library-property>
<name>org.zkoss.zul.Paging.mold</name>
<value>bs</value>
</library-property>
<library-property>
<name>org.zkoss.zul.Panel.mold</name>
<value>bs</value>
</library-property>
Note: we use the word bs as bootstrap's nickname for the extra zk component molds.
Maven Installation
<dependency>
<groupId>org.zkoss.addons</groupId>
<artifactId>zk-bootstrap</artifactId>
<version>1.0.0</version>
</dependency>
Version History
Version Date Content
References
[1] http:/ / getbootstrap. com/
[2] https:/ / github. com/ jumperchen/ zk-bootstrap/ releases
[3] http:/ / books. zkoss. org/ wiki/ ZK_Component_Reference/ Essential_Components/ Button
[4] http:/ / getbootstrap. com/ components/ #btn-groups
[5] http:/ / www. zkoss. org/ zk-bootstrap/ #btn-groups
[6] http:/ / books. zkoss. org/ wiki/ ZK_Component_Reference/ Essential_Components/ Menu/ Menupopup
[7] http:/ / getbootstrap. com/ components/ #dropdowns
[8] http:/ / www. zkoss. org/ zk-bootstrap/ #btn-dropdowns
[9] http:/ / books. zkoss. org/ wiki/ ZK_Component_Reference/ Containers/ Panel
[10] http:/ / getbootstrap. com/ components/ #panels
[11] http:/ / www. zkoss. org/ zk-bootstrap/ #panels
[12] http:/ / books. zkoss. org/ wiki/ ZK_Component_Reference/ Supplementary/ Paging
[13] http:/ / getbootstrap. com/ components/ #pagination
[14] http:/ / www. zkoss. org/ zk-bootstrap/ #pagination
[15] http:/ / books. zkoss. org/ wiki/ ZK_Component_Reference/ Essential_Components/ Nav/ Navbar
[16] http:/ / getbootstrap. com/ components/ #navbar
[17] http:/ / www. zkoss. org/ zk-bootstrap/ #navbar
[18] http:/ / getbootstrap. com/ components/ #nav-tabs
[19] http:/ / www. zkoss. org/ zk-bootstrap/ #nav-tabs
[20] http:/ / getbootstrap. com/ components/ #nav-pills
[21] http:/ / www. zkoss. org/ zk-bootstrap/ #nav-pills
[22] http:/ / www. zkoss. org/ zk-bootstrap
Internationalization 322
Internationalization
This chapter describes how to make ZK applications flexible enough to run in any locale.
First of all, ZK enables developers to embed Java code and EL expressions any way you like. You could use any
Internationalization method you want, such as java.util.ResourceBundle.
However, ZK has some built-in support of internationalization that you might find them useful.
Locale
Overview
The locale used to process requests and events is, by default, determined by the browser's preferences (by use of the
getLocale method of javax.servlet.ServletRequest). For example, DE is assumed if an user is using
a DE-version browser (unless he changed the setting).
In this section, we'd like to discuss how to configure ZK to handle the locale differently. For example, you could
configure ZK to use the same Locale for all users no matter how the browser is configured. Another example is that
you could configure ZK to use the preferred locale that a user specified in his or her profile, if you maintain the user
profiles in the server.
Application-level Locale
If you want to use the same locale for all users, you can specify the locale in the library property. For example, you
could specify the following in WEB-INF/zk.xml:
<library-property>
<name>org.zkoss.web.preferred.locale</name>
<value>de</value>
</library-property>
Library.setProperty(Attributes.PREFERRED_LOCALE, "de");
Locale 323
Per-user Locale
Because ZK will check if a session attribute for the default locale, you could configure ZK to have per-user locale by
specifying the attribute in a session.
For example, you can do this when a user logins.
import org.zkoss.web.Attributes;
...
import java.util.Locale;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.zkoss.web.Attributes;
locale);
return;
}
}
}
}
}
To make it effective, you have to register it in WEB-INF/zk.xml as a listener. Once registered, the request method is
called each time ZK receives a request.
<listener>
<listener-class>MyLocaleProvider</listener-class>
</listener>
Note: An instance of the interceptor is instantiated when it is registered. It is then shared among all requests in the
same application. Thus, you have to make sure it can be accessed concurrently (i.e., thread-safe).
Note: The request method is called at very early stage, before the request parameters are parsed. Thus, it is
recommended to access them in this method, unless you configured the locale and character encoding properly for
the request.
session.setAttribute(Attributes.PREFERRED_LOCALE, locale);
Executions.sendRedirect(null); //reload the same page
[1]
Notice that Executions.sendRedirect(java.lang.String) will cause the client to reload the page, so any updates to
the current desktop will be lost.
Locale 325
session.setAttribute(Attributes.PREFERRED_LOCALE, locale);
Clients.reloadMessages(locale);
Locales.setThreadLocal(locale);
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Application. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ lang/ Library. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ lang/ Library. html#setProperty(java. lang. String,
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ Attributes. html#PREFERRED_LOCALE
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ RequestInterceptor. html#
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#reloadMessages(java. util. Locale)
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ util/ Locales. html#setThreadLocal(java. util. Locale)
Time Zone
Overview
The time zone used to process requests and events is, by default, determined by the JVM's default (by use of the
getDefault method of java.util.TimeZone)[1].
In this section, we will discuss how to configure ZK to use a time zone other than JVM's default. For example, you
might configure ZK to use the preferred time zone that a user specified in his or her profile.
[1] Unlike locale, there is no way to determine the time zone for each browser
With this sequence in mind, you could configure ZK to use the correct time zone based on the application
requirements.
<library-property>
<name>org.zkoss.web.preferred.timeZone</name>
<value>GMT-8</value>
</library-property>
where the value can be anything accepted by the getTimeZone method of java.util.TimeZone
Alternatively, if you prefer to specify it in Java, you could invoke java.lang.String)
Library.setProperty(java.lang.String, java.lang.String) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ lang/
Library. html#setProperty(java. lang. String,). Furthermore, to avoid typo, you could use
Attributes.PREFERRED_TIME_ZONE (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ Attributes.
html#PREFERRED_TIME_ZONE) as follows.
Library.setProperty(Attributes.PREFERRED_TIME_ZONE, "PST");
Version History
Version Date Content
Labels 327
Labels
Overview
For a multilingual application, it is common to display the content in the language that the end user prefers. Here we
discuss the built-in support called internationalization labels.
However, if you prefer to use other approach, please refer to the Use Other Implementation section.
Internationalization Labels
The internationalization labels of an application are loaded from properties files based on the current locale[1]. A
properties file is a simple text file encoded in UTF-8[2]. The file contains a list of key=value pairs, such as[3]
By default the property file must be placed under the WEB-INF directory and named as
zk-label_lang_CNTY.properties.[4], where lang is the language such as en and fr, and CNTY is the
country, such as US and FR.
If you want to use one file to represent a language regardless the country, you could name it
zk-label_lang.properties, such as zk-label_ja.properties. Furthermore,
zk-label.properties is the default file if the user's preferred locale doesn't match any other file.
When an user accesses a page, ZK will load the properties files for the user's locale. For example, assume the locale
is de_DE, then it will search the following files and load them if found:
1. zk-label_de_DE.properties
2. zk-label_de.properties
3. zk-label.properties
By default, one properties file is used to contain all labels of a given locale. If you prefer to split it to multiple
properties files (such as one file per module), please refer to the Loading Labels from Multiple Resources section.
Also notice that all files match the given locale will be loaded and merged, and the property specified in, say,
zk-label_de_DE.properties will override what are defined in zk-label_de.properties if
replicated. It also means if a label is the same in both de_DE and de, then you need only to specify in
zk-label_de.properties (and then it will be inherited when de_DE is used). Of course, you could specify it
in both files.
Labels 328
[1] It is the value returned by UNIQ-javadoc-0-f6421cf86b2cc1a3-QINU . For more information, please refer to the Locale section.
[2] If you prefer a different charset, please refer to the Encoding Character Set section.
[3] Please refer to here for more details about the format of a properties file, such as the use of multiple lines and EL expressions.
[4] Notice the directory and filename is configurable. For more information, please refer ZK Configuration Reference:
org.zkoss.util.label.web.location
Use labels
since 5.0.7
Since 5.0.7 and later, an implicit object called labels was introduced, such that you could access the
internationalization labels (so-called internationalization labels) directly. For example, assume you have a label
called app.title, and then you could:
<window title="${labels.app.title}">
...
</window>
The labels object is a map (java.util.Map), so you could access the label directly by the use of
labels.whatever in an EL expression. Moreover, as shown above, you could access the label even if a key is
named as aa.bb.cc (a string containing dot), such as app.title in the above example.
If the key is not a legal name, you could use labels['key'] to access, such as labels['foo-yet'].
When an internationalization label is about to be retrieved, one of zk-label_lang_CNTY.properties will be loaded. For
example, if the Locale is de_DE, then WEB-INF/zk-label_de_DE.properties will be loaded. If no such
file, ZK will try to load WEB-INF/zk-label_de.properties and WEB-INF/zk-label.properties
in turn.
Notice that ZK groups the segmented labels as map. For example, ${labels.app} was resolved as a map
containing two entries (title and description).
app.title=Foo
app.description=A super application
If you have a key named as the prefix of the other keys, you have to use $ to access it. For example, if the labels
consist of keys a, a.b, etc., ${labels.a.$} is required to resolve the label with key named a.
For example, in properties file:
app=Application
app.title=Foo
app.description=A super application
In ZUL:
Use c:l('key')
With 5.0.6 or prior, you could, to get an internationalization label, use ${c:l('key')} in EL expression. For
example,
<window title="${c:l('app.title')}">
...
</window>
Notice that the l function belongs to the TLD file called http:/ / www. zkoss. org/ dsp/ web/ core, so we have to
specify it with the taglib directive as shown above.
where we assume fullname is a string array (such as new String[] {"Jimmy", "Shiau"}).
java.lang.Object[ (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ util/ resource/ Labels.
html#getLabel(java. lang. String,)) Labels.getLabel(java.lang.String, java.lang.Object[])] assumes the content is a
valid pattern accepted by MessageFormat (http:/ / download. oracle. com/ javase/ 6/ docs/ api/ java/ text/
MessageFormat.html), such as "{1}, {0}".
Here is a more complex example. Let us assume we want to generate a full name based on the Locale, then we could
use java.lang.Object[ (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ util/ resource/ Labels.
html#getLabel(java. lang. String,)) Labels.getLabel(java.lang.String, java.lang.Object[])] to generate concatenated
messages as follows.
java.lang.Object[ (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ util/ resource/ Labels.
html#getLabel(java. lang. String,)) Labels.getLabel(java.lang.String, java.lang.Object[])] assumes the content is a
valid pattern accepted by MessageFormat (http:/ / download. oracle. com/ javase/ 6/ docs/ api/ java/ text/
Labels 330
It is typical to partition the properties file into several modules for easy maintenance. Since 5.0.7 and later, you could
specify the location for each of these properties file with the label-location element. For example,
<system-config>
<label-location>/WEB-INF/labels/order.properties</label-location>
<label-location>/WEB-INF/labels/invoice.properties</label-location>
</system-config>
<system-config>
<label-location>file:///labels/order.properties</label-location>
<label-location>file:///labels/invoice.properties</label-location>
</system-config>
Notice that the configuration with a path related to the file system is better not to be part of WEB-INF/zk.xml,
since it is easy to cause errors when deploying the application. Rather, it is better to be specified in the additional
configuration file. The additional configuration file is also specified at the run time and could be located in the file
system (rather than the WAR file). It can be done by specifying the path of the configuration file in a library property
called org.zkoss.zk.config.path.
For 5.0.6 and older, you could use the approach described in the following section to load multiple properties files.
Labels 331
[1] For more information about the URI of a file, please refer to File URI scheme (http:/ / en. wikipedia. org/ wiki/ File_URI_scheme).
Then, we could register label locators when the application starts by use of WebAppInit (http:/ / www. zkoss. org/
javadoc/latest/zk/org/zkoss/zk/ui/util/WebAppInit.html#) as follows.
where we assume moduleX and moduleY is the database table to load the properties, and
module-1.properties and module-2.properties are two modules of messages you provide. Then, you
configure it in WEB-INF/zk.xml as described in ZK Configuration Reference.
[1] For 5.0.7 and later, you could use the label-location element if the properties file is located in the file system or in the Web application as
described in the previous section.
</zk>
Version History
Version Date Content
5.0.5 October LabelLocator2 (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ util/ resource/ LabelLocator2. html#) was
2010 introduced.
The default encoding of a properties file is assumed to be UTF-8. If you want to use a different encoding, please
refer to the Use Encoding Other Than UTF-8 section.
A properties file is usually used to contain the internationalization labels of an application, but technically you could
use it in any situation you'd like[1].
[1] If it is used for internationalization labels, it will be loaded automatically. If you want to use it in other situation, you have to invoke
UNIQ-javadoc-0-f6421cf86b2cc1a3-QINU or similar to load it manually.
key={
line 1
line 2
}
Notice that the curly braces must be followed by a line break immediately, and the right brace (}) must be the only
character in the line.
The Format of Properties Files 334
order.fruit.name = Orange
order.fruit.description = A common fruit
order.fruit. {
name = Orange
description = A common fruit
}
As shown, the segmented key could be specified by specifying the prefix and a following right brace ({).
The segmented key could be accessed in two ways.
First, with an implicit object called labels:
<textbox value="${labels.order.fruit.name}"/>
Under the hood: The labels object is actually the map returned by Labels.getSegmentedLabels()
(http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ util/ resource/ Labels.
html#getSegmentedLabels()). Furthermore, if the key of a property contains dot (.), i.e., segmented, all
properties with the same prefix are grouped as another map. For example, ${labels.order} (i.e.,
Labels.getSegmentedLables().get("order")) will return a map containing an entry
(fruit) in the above example.
Second, with an EL function called [[ZUML Reference/EL Expressions/Core Methods/l|l] and/or l2:
Specify a Comment
You could put a comment line by starting with the sharp sign (#), such as
Use EL Expressions
EL expressions are allowed for a property's value. For example, you could reference a property's value in another
property, such as
In addition to referencing another property, you could reference any implicit object specified in ZUML Reference:
Implicit Objects if it is part of a HTTP request (excluding component/page).
For example, param references to a request's parameter:
Version History
Version Date Content
5.0.7 Mar 2011 labels implicit object was introduced to access properties without declaring taglib. Also allows label keys of a.b.c format.
Date and Time Formatting 336
Application-level Format
If you want to use the same format for all users, you could specify your implementation of DateFormatInfo [1] in the
library property. For example,
<library-property>
<name>org.zkoss.web.preferred.dateFormatInfo</name>
<value>foo.MyDateFormatInfo</value>
</library-property>
Per-user Format
If you'd like to configure ZK to allow each user (aka., session) has an independent format, you could store an
instance of your implementation of DateFormatInfo [1] in the session's attribute.
For example, you could do this when a user logins.
import org.zkoss.web.Attributes;
...
Per-component Format
Datebox and Timebox allow a developer to specify any format he prefer for any instance. For example,
However, it is usually better to design a page that depends on the configuration as described above, rather than
specify the format explicitly in each page. It can be done by specifying the styling rather than the real format in the
format property (Datebox.setFormat(java.lang.String) [2] and Timebox.setFormat(java.lang.String) [3]). There are
totally four different styling: short, medium, long and full (representing the styling defined in java.text.DateFormat,
SHORT, MEDIUM, LONG and FULL). For example,
<datebox format="short"/>
<datebox format="long"/>
<timebox format="medium"/>
Then, the real format will be decided by your implementation of DateFormatInfo [1], if any, or the JVM's default.
In additions, you could specify the date/time format in the syntax of
styling_for_date+styling_for_time, such as:
<datebox format="long+medium"/>
which specifies the date/time format with the long styling for date and the medium styling for time.
Date and Time Formatting 338
Per-component Locale
In additions to the current locale, you could specify the locale for individual instances of datebox and timebox. Then,
the real format will depend on the locale and the format you specified. For example,
Note: the language of the format and the datebox's calendar is the same as the locale you specified. [Since 5.0.8]
Version History
Version Date Content
5.0.7 April 2011 The per-session format of datebox/timebox was introduced. Prior to 5.0.7, the format depends only on locale.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ text/ DateFormatInfo. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Datebox. html#setFormat(java. lang. String)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Timebox. html#setFormat(java. lang. String)
Application-level first-day-of-the-week
[since 5.0.3]
If you want to use the same first-day-of-the-week for all users of the same application, you can specify it in the
library property. The allowed values include 1 (Sunday), 2 (Monday), .. and 7 (Saturday). For example, you could
specify the following in WEB-INF/zk.xml:
<library-property>
<name>org.zkoss.web.preferred.firstDayOfWeek</name>
<value>7</value><!-- Saturday -->
</library-property>
webApp.setAttribute(org.zkoss.web.Attributes.PREFERRED_FIRST_DAY_OF_WEEK,
java.util.Calendar.SATURDAY);
Per-user first-day-of-week
[since 5.0.3]
session.setAttribute(org.zkoss.web.Attributes.PREFERRED_FIRST_DAY_OF_WEEK,
java.util.Calendar.SATURDAY);
//then, the current session's first day of the week will be Saturday
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ WebApp. html#setAttribute(java. lang. String,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ Attributes. html#PREFERRED_FIRST_DAY_OF_WEEK
Locale-Dependent Resources
Overview
Many resources depend on the Locale and, sometimes, the browser. For example, you might need to use a larger font
for Chinese characters to have better readability.
[1] In the future editions, we will use different codes for browsers other than Internet Explorer, Firefox and Safari.
Example
In the following example, we assume the preferred Locale is de_DE and the browser is Internet Explorer.
/css/norm*.css 1. /norm_de_DE.css
2. /norm_de.css
3. /norm.css
/css-*/norm*.css 1. /css-ie/norm_de_DE.css
2. /css-ie/norm_de.css
3. /css-ie/norm.css
/img*/pic*/lang*.png 1. /imgie/pic*/lang_de_DE.png
2. /imgie/pic*/lang_de.png
3. /imgie/pic*/lang.png
/img*/lang.gif 1. /img/lang.gif
/img/lang*.gif* 1. /img/langie.gif
/img*/lang*.gif* 1. /imgie/lang*.gif
Version History
Version Date Content
Warning and Error Messages 342
If you want to change a particular message, you need to create WEB-INF/zk-label.properties (or
WEB-INF/zk-label_[LOCALE].properties) and add key-value pairs in it. For example, assuming you
want to customize MZk.NOT_FOUND in German translation (msgzk_de.properties), then you can add the
following to WEB-INF/zk-label_de.properties:
Notice the prefix MZk, and 3000 is the error code and you can find it at
ZK_Messages/German/msgzk_de.properties
Defined in a JS File
For messages defined in msgzk.js / msgzul.js you can create a js file and include it via
lang-addon.xml. For example for overriding msgzk.LOADING for CEZH language create a file
test_cs.js and override particular message msgzk.LOADING=CS langauge specific message"
and include this js file via lang-addon.xml as below
<language-addon>
<addon-name>test</addon-name>
<language-name>xul/html</language-name>
Note: You can use any prefix other than "test" and add language specific suffix to your js files and include all of
them by using * wild card as shown above
Finally include your lang-addon.xml in zk.xml using language-config element as shown below
<language-config>
<addon-uri>/WEB-INF/lang-addon.xml</addon-uri>
</language-config>
Version History
Version Date Content
Server Push
HTTP is a request-and-response protocol. Technically, there is no way to have the server to actively push data to the
client. However, there are a few approaches [1] to emulate push -- it is also called Ajax Push. These approaches could
be summarized in two categories, client polling and comet[2], that are both supported in ZK.
Different approaches have different pros and cons, and we will discuss them in the Configuration section.
No matter which implementation you choose, the use is the same. The Event Queue is the high-level API, and this is
a suggested approach for its simplicity. However, if you prefer to access the low-level API directly, you could refer
to the Asynchronous Tasks and Synchronous Tasks sections, depending on whether you task can be executed
asynchronously.
Event Queues
An event queue is an event-based publish-subscribe solution for the application information delivery and messaging.
It provides asynchronous communication for different modules/roles in a loosely-coupled and autonomous fashion.
By publishing, a module (publisher) sends out messages without explicitly specifying or having knowledge of
intended recipients. By subscribing, a receiving module (subscriber) receives messages that the subscriber has
registered an interest in, without explicitly specifying or knowing the publisher.
ZK generalizes the event queue to support the server push. The use is straightforward: specifying the scope of a
given event queue as EventQueues.APPLICATION [2] (or EventQueues.SESSION [1], but rare). For example,
For more information about event queues, please refer to the Event Handling: Event Queues section.
For the information about low-evel API, please refer to Asynchronous Tasks section, if the task can execute
asynchronously; or Synchronous Tasks if it must execute synchronously.
Version History
Version Date Content
5.0.6 November The event queue won't start any working threads and they are serializable, so it is safe to use them in a clustering
2010 environment.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventQueues. html#SESSION
Synchronous Tasks 345
Synchronous Tasks
Server push is a technology to actively push data to the client. For ZK, the data is usually the UI updates or its
variants. Thus, for sake of understanding, we could consider the task is about updating UI in parallel with regular
Ajax requests (poll-type request). For example, in a chat application, once a message is entered by a participant, the
server has to push it to all clients that involve in the conversation.
If the task of updating UI takes place in a working thread, it is generally more convenient to execute it synchronously
as described later. On the other hand, if the task can be encapsulated as an event listener (EventListener [1]), you
could execute it asynchronously -- please refer to the Asynchronous Tasks section for more information.
desktop.enableServerPush(true);
After the server push of a given desktop is enabled, you could use any number of working thread to update the
desktop concurrently as described in the following section[3].
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventListener. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Desktop. html#enableServerPush(boolean)
[3] For better performance, it is suggested to disable the server push if it is no longer used in the give desktop.
Executions.activate(desktop);
try {
//Step 3. Update UI
updateUI(); //impelment the logic to change UI
} finally {
//Step 4. Deactivate to return the control of UI
back
Executions.deactivate(_desktop);
}
}
} catch (InterruptedException ex) {
//Interrupted. You might want to handle it
}
}
}
Notice that the task between Executions.activate(org.zkoss.zk.ui.Desktop) (http:/ / www. zkoss. org/ javadoc/ latest/
zk/ org/ zkoss/ zk/ ui/ Executions. html#activate(org. zkoss. zk. ui. Desktop)) and
Executions.deactivate(org.zkoss.zk.ui.Desktop) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/
Executions. html#deactivate(org. zkoss. zk. ui. Desktop)) has to take less time, since it blocks others, including the
user (of the desktop), from accessing the UI. It is suggested to prepare the data before
Executions.activate(org.zkoss.zk.ui.Desktop) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/
Executions.html#activate(org.zkoss.zk.ui.Desktop)), such that it can be done in parallel with other threads.
[1] Notice that, for each desktop, there is at most one thread is allowed to access at the same time.
[2] For a real example, please refer to small talks: Simple and Intuitive Server Push with a Chat Room Example and Server Push with a Stock
Chart Example.
Version History
Version Date Content
Asynchronous Tasks 347
Asynchronous Tasks
If the task of updating UI can be represented as a method, the push can be done easily. All you need to do is
1. Implement the UI updates in an event listener (implementing EventListener [1] or SerializableEventListener [2]).
2. Then, schedule it for executed asynchronously by the use of org.zkoss.zk.ui.event.EventListener,
org.zkoss.zk.ui.event.Event) Executions.schedule(org.zkoss.zk.ui.Desktop, org.zkoss.zk.ui.event.EventListener,
org.zkoss.zk.ui.event.Event) [1].
Here is the pseudo code:
Executions.schedule(desktop,
new EventListener() {
public void onEvent(Event event) {
updateUI(); //whatever you like
}
}, event);
[2]
You could manipulate UI whatever you want in EventListener.onEvent(org.zkoss.zk.ui.Event) . It is no different
from any other event listener.
Notice that org.zkoss.zk.ui.event.EventListener, org.zkoss.zk.ui.event.Event)
Executions.schedule(org.zkoss.zk.ui.Desktop, org.zkoss.zk.ui.event.EventListener, org.zkoss.zk.ui.event.Event) [1]
can be called anywhere, including another event listener or a working thread. In other words, you don't have to fork a
working thread to use this feature.
Notice that, since there is at most one thread to access the UI of a given desktop, the event listener's performance
must be good. Otherwise, it will block other event listeners from execution. Thus, if you have a long operation to do,
you could use event queue's asynchronous event listener, or implement it as a synchronous task and handle lengthy
operation outside of the activation block.
Version History
Version Date Content
5.0.6 November 2010 This feature was introduced. With 5.0.5 or prior, you have to use Event Queues or Synchronous Tasks.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Executions. html#schedule(org. zkoss. zk. ui. Desktop,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/ EventListener. html#onEvent(org. zkoss. zk. ui. Event)
Configuration 348
Configuration
Since ZK 6, in addition to PollingServerPush [1] and CometServerPush [2] ZK provides a third Server Push
implementation - Asynchronous Processing CometServerPush [3]. As their name suggest, they implement the
Client-Polling, Comet (aka., long-polling) and Servlet 3 Comet (Servlet 3 Asynchronous Processing-based Comet
[4]
) server pushes. Client-Polling is available in all editions, Comet Server Push is available in ZK PE and EE, while
ZK EE supports Servlet 3 Comet Push.
The default implementation depends on which ZK edition you use. By default CE uses Client Polling, PE uses
Comet Server Push, EE uses Servlet 3 Comet. You can also configure ZK to use the one you prefer, or even use a
custom server push.
Note that Comet Server Push is available in ZK 5 EE only. By default, ZK 5 CE and ZK 5 PE uses
PollingServerPush [1] and ZK 5 EE uses CometServerPush [3].
Choose an Implementation
Client-polling is based on a timer that peeks the server continuously to see if any data to be pushed to the client,
while Comet establishes a permanent connection for instant push. Client-polling will introduce more traffic due to
the continuous peeks, but Comet will consume the network connections that a server allows.
Page-level Configuration
You could configure a particular ZK page to use a particular implementation by the use of
DesktopCtrl.enableServerPush(org.zkoss.zk.ui.sys.ServerPush) [2]. For example,
((DesktopCtrl)desktop).enableServerPush(
new org.zkoss.zk.ui.impl.PollingServerPush(2000,5000,-1));
Application-level Configuration
If you would like to change the default server push for the whole application, you could use the the server-push-class
element as follows.
<device-config>
<device-type>ajax</device-type>
<server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class>
</device-config>
where you could specify any implementation that implements ServerPush [5].
Configuration 349
Customized Client-Polling
PollingServerPush [1] uses a timer to peek if the server has any data to push back. The period between two peeks is
determined by a few factors.
• PollingServerPush.delay.min
The minimal delay to send the second polling request (unit: milliseconds). Default: 1000.
• PollingServerPush.delay.max
The maximal delay to send the second polling request (unit: milliseconds). Default: 15000.
• PollingServerPush.delay.factor
The delay factor. The real delay is the processing time that multiplies the delay factor. For example, if the last
request took 1 second to process, then the client polling will be delayed for 1 x factor seconds. Default:
5.
The larger the factor is, the longer delay it tends to be.
It could be configured in WEB-INF/zk.xml by use of the preference element as follows.
<preference>
<name>PollingServerPush.delay.min</name>
<value>3000</value>
</preference>
<preference>
<name>PollingServerPush.delay.max</name>
<value>10000</value>
</preference>
<preference>
<name>PollingServerPush.delay.factor</name>
<value>5</value>
</preference>
<!-- JavaScript code to start the server push; rarely required
<preference>
<name>PollingServerPush.start</name>
<value></value>
</preference>
<preference>
<name>PollingServerPush.stop</name>
<value></value>
</preference>
-->
In additions, you could specify them in the constructor: int, int) PollingServerPush.PollingServerPush(int, int, int) [6].
For example,
((DesktopCtrl)desktop).enableServerPush(
new org.zkoss.zk.ui.impl.PollingServerPush(2000,10000,3));
Configuration 350
CometServerPush [2] comes with its own configuration parameters such as retry delay, retry count, and ajax timeout.
• CometServerPush.retry.delay
The minimum time delay to send the next comet request retry (unit: milliseconds). Default: 5000.
• CometServerPush.retry.count
The maximum retry count if a comet request failed, -1 means keep retry forever (unit: count). Default: 10.
• CometServerPush.ajax.timeout
The amount of time a comet request will wait for a server response until it aborts. (unit: milliseconds). Default:
varies, depending on browser
Configure these parameters by using the preference element in your WEB-INF/zk.xml
<preference>
<name>CometServerPush.retry.delay</name>
<value>3000</value><!-- 3 seconds delay between each retry-->
</preference>
<preference>
<name>CometServerPush.retry.count</name>
<value>3</value><!-- 3 tries for each request -->
</preference>
<preference>
<name>CometServerPush.ajax.timeout</name>
<value>180000</value><!-- 3 minutes -->
</preference>
Error Handling
The configuration of the errors is handled by the client-reload element, specified in WEB-INF/zk.xml. The
markup below demonstrates an example of catching an error of the server push:
<error-reload>
<device-type>ajax</device-type>
<connection-type>server-push</connection-type>
<error-code>410</error-code>
<reload-uri>/login.zul</reload-uri>
</error-reload>
where the connection-type element specifies through which channel the requests are sent. By default it is the AU
channel in which Ajax requests are sent by widgets running at the client. If you would like to specify the error page
for server-push then connection-type must be set to server-push.
Configuration 351
"dummy" Requests
When using ServerPush the client engine will send "dummy"-requests (without extra payload) to the /zkau servlet to
pull queued server side updates.
Those requests look similar to this:
dtid=z_m06&cmd_0=dummy&opt_0=i
In the case of PollingServerPush there will be one of these requests per configured interval, sometimes causing a
firewall to give a false alert.
In the case of CometServerPush, after a long polling comet request returned, there will be one "dummy" request sent
to retrieve server update.
WebSocketServerPush
[since 8.5.0]
Since ZK 8.5.0, the WebSocket based Server Push WebSocketServerPush [7] is provided. When WebSocket
connection is enabled, this will be used as default, other configuration specifying which Server Push to use will be
ignored.
Version History
Version Date Content
6.0.0 Feb 2012 The CometServerPush is available in ZK PE, while ZK EE supports Servlet 3 Asynchronous Processing-based Comet.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ impl/ PollingServerPush. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkex/ ui/ comet/ CometServerPush. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ ui/ comet/ CometServerPush. html#
[4] http:/ / books. zkoss. org/ wiki/ Small_Talks/ 2012/ February/
New_Features_of_ZK_6#ZK_Comet_supports_Servlet_3_Asynchronous_Processing
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ ServerPush. html#
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ impl/ PollingServerPush. html#PollingServerPush(int,
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ au/ websocket/ WebSocketServerPush. html#
Clustering 352
Clustering
ZK components, pages and desktops are all serializable, so using ZK in a clustering environment is straightforward.
However, it is a challenge to develop a sophisticated application that is ready for clustering. If you are not familiar
with it, you might refer to J2EE clustering [1] and other resources.
Here we discuss how to configure ZK for a clustering environment, how to configure some servers, and the
important notes when developing a clustering-ready application with ZK.
References
[1] http:/ / www. javaworld. com/ jw-02-2001/ jw-0223-extremescale. html
ZK Configuration
Turn on Serializable UI Factory
[Required]
To use ZK in a clustering environment, you have to use the serializable UI factory. It could be done by specifying
the following statement in WEB-INF/zk.xml:
<zk>
<system-config>
<ui-factory-class>org.zkoss.zk.ui.http.SerializableUiFactory</ui-factory-class>
</system-config>
</zk>
SerializableUiFactory [1] is the UI factory that will instantiate serializable sessions such that the sessions,
components, pages and desktops will be serialized when a session is about to deactivate.
Turn on Log
[Optional]
If an attribute or a listener is not serializable, ZK will skip it, i.e., not to serialize it (similar to how a Servlet
container serializes the attributes of sessions). It is sometimes hard to know what are ignored, since it is common for
a developer to forget to declare a value or a listener as serializable.
To detect this problem, you could turn on the logger for org.zkoss.io.serializable to the DEBUG
level[2]. The logger is the standard logger [3]. You could consult the configuration of the Web server you use. Or, you
could run the following statement when your application starts[4].
org.zkoss.util.logging.Log.lookup("org.zkoss.io.serializable").setLevel("DEBUG");
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ http/ SerializableUiFactory. html#
[2] Available in 5.0.7
[3] http:/ / download. oracle. com/ javase/ 6/ docs/ api/ java/ util/ logging/ Logger. html
[4] It can be done by use of the UNIQ-javadoc-0-f6421cf86b2cc1a3-QINU listener. For more information, please refer to the Customization
section.
The interpreter (BeanShell) does not work well under the clustering environment. Since the serialization is not
stable, zscript cannot be used in a clustering environment. To avoid accidental or unintended use it is recommended
to disable zscript in your ZK application with the following configuration (in zk.xml):
<system-config>
<disable-zscript>true</disable-zscript>
</system-config>
[1] Rather, the default is SerializableUiFactory (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ http/ SerializableUiFactory.
html#).
Version History
Version Date Content
Server Configuration
The configuration of a Web server really depends on the server itself. There is no standard approach.
Apache + Tomcat
For configuring Apache + Tomcat, please refer to
• How to Run ZK on Apache + Tomcat clustering, Part I
• How to Run ZK on Apache + Tomcat clustering, Part II
More detail settings
• Tomcat Cluster
Apache + JBoss
For configuring JBoss, please refer to
• JBoss Cluster
Glassfish
For configuring Glassfish, please refer to
• Glassfish Cluster
Server Configuration 355
WebLogic
For configuring WebLogic, please refer to
• WebLogic Cluster
Version History
Version Date Content
Programming Tips
Objects Referenced by UI Must be Serializable
Objects that are referenced by an UI object, such as components and pages, have to be serializable. Otherwise, they
might have no value after de-serialized, or causes an exception (depending how it is used).
Attributes of UI Objects
If the value of an attribute is not serializable, it will be ignored. Thus, it will become null after de-serialized. So, it is
better to make them all serializable (such as implementing java.io.Serializable), or handle the serialization manually
(refer to the Clustering Listeners section below) .
zscript
It is OK, though not recommended, to use zscript in a clustering environment, but there are some limitations.
• BeanShell's function is not serializable. For example, the following won't work:
void foo() {
}
Event Listeners
Event listeners have to be serializable. Otherwise, it will be ignored after serialization.
[2]
A simplest way to make an event listener serializable is to implement SerializableEventListener (available since
5.0.6), instead of EventListener [1].
For example,
button.addEventListener(Events.ON_CLICK,
new SerializableEventListener() {
public void onEvent(Event event) {
....
}
Programming Tips 356
});
Data Models
The data models, such as ListModel [2] and ChartModel [1], have to be serializable. Otherwise, the UI object (such as
grid) won't behave correctly. The implementations provided by ZK are serializable. However, the items to be stored
in the data models have to be serializable too.
Composers
If you extend from GenericForwardComposer [4] or GenericAutowireComposer [1], you have to make sure all of its
members are serializable (or transient), since the implementation will keep a reference in the applied component.
When implementing from Composer [2] directly, the composer could be non-serializable if you don't keep a reference
in any UI object. In other words, the composer will be dropped after
[3]
Composer.doAfterCompose(org.zkoss.zk.ui.Component)
ViewModels
If you are using ZK MVVM then your ViewModel classes must be serializable.
Clustering Listeners
If there are non-serializable objects, you could implement one of the clustering listeners to handle them manually as
described below. Basically, there are two kinds of clustering listeners for different purpose:
• Serialization Listeners: they are called when an object is about to be serialized, and after it has been de-serialized.
Example: ComponentSerializationListener [4] and PageSerializationListener [5]
• Activation Listeners: they are called when a session is about to be passivated, and after it has been activated.
Examples: ComponentActivationListener [6] and PageActivationListener [7].
To register a listener is straightforward: just implements the corresponding listener interface. For example, you could
implement ComponentActivationListener [6] if an object is stored in a component and wants to called on activation
and passivation.
Passivation Flow
When a session is about to be passivated (such as moving to anther machine), the activation listeners will be called
first to notify the passivation, and then the serialization listeners will be called before the object is serialized.
Sequence Description
1 [8] [2]
Invokes SessionActivationListener.willPassivate(org.zkoss.zk.ui.Session) for each object referenced by the Session that will be
passivated
2 [9] [9]
Invokes DesktopActivationListener.willPassivate(org.zkoss.zk.ui.Desktop) for each object referenced by each Desktop that will
be passivated
3 [10] [8]
Invokes PageActivationListener.willPassivate(org.zkoss.zk.ui.Page) for each object referenced by each Page that will be
passivated
4 [11]
Invokes ComponentActivationListener.willPassivate(org.zkoss.zk.ui.Component) for each object referenced by each Component
[1]
that will be passivated
Programming Tips 357
5 [12] [2]
Invokes SessionSerializationListener.willSerialize(org.zkoss.zk.ui.Session) for each object referenced by the Session that will
be passivated
7 [13] [9]
Invokes DesktopSerializationListener.willSerialize(org.zkoss.zk.ui.Desktop) for each object referenced by each Desktop that
will be passivated
9 [14] [8]
Invokes PageSerializationListener.willSerialize(org.zkoss.zk.ui.Page) for each object referenced by each Page that will be
passivated
11 [15]
Invokes ComponentSerializationListener.willSerialize(org.zkoss.zk.ui.Component) for each object referenced by each Component
[1]
that will be passivated
Activation Flow
When a session is about to be activated (such as moving from another machine), the serialization listener is called
after the object has been deserialized. After all objects are deserialized, the activation listener will be called to notify
a session has been activated.
Sequence Description
5 [16]
Invokes ComponentSerializationListener.didDeserialize(org.zkoss.zk.ui.Component) for each object referenced by each
[1]
Component that will be passivated
6 [17] [8]
Invokes PageSerializationListener.didDeserialize(org.zkoss.zk.ui.Page) for each object referenced by each Page that will be
passivated
7 [18] [9]
Invokes DesktopSerializationListener.didDeserialize(org.zkoss.zk.ui.Desktop) for each object referenced by each Desktop that
will be passivated
8 [19] [2]
Invokes SessionSerializationListener.didDeserialize(org.zkoss.zk.ui.Session) for each object referenced by the Session that will
be passivated
9 [20] [2]
Invokes SessionActivationListener.didActivate(org.zkoss.zk.ui.Session) for each object referenced by the Session that will be
passivated
10 [21] [9]
Invokes DesktopActivationListener.didActivate(org.zkoss.zk.ui.Desktop) for each object referenced by each Desktop that will
be passivated
11 [22] [8]
Invokes PageActivationListener.didActivate(org.zkoss.zk.ui.Page) for each object referenced by each Page that will be
passivated
12 [23] [1]
Invokes ComponentActivationListener.didActivate(org.zkoss.zk.ui.Component) for each object referenced by each Component
that will be passivated
Programming Tips 358
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ GenericAutowireComposer. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Composer. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Composer. html#doAfterCompose(org. zkoss. zk. ui. Component)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ComponentSerializationListener. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PageSerializationListener. html#
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ComponentActivationListener. html#
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PageActivationListener. html#
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ SessionActivationListener. html#willPassivate(org. zkoss. zk. ui. Session)
[9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopActivationListener. html#willPassivate(org. zkoss. zk. ui.
Desktop)
[10] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PageActivationListener. html#willPassivate(org. zkoss. zk. ui. Page)
[11] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ComponentActivationListener. html#willPassivate(org. zkoss. zk. ui.
Component)
[12] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ SessionSerializationListener. html#willSerialize(org. zkoss. zk. ui.
Session)
[13] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopSerializationListener. html#willSerialize(org. zkoss. zk. ui.
Desktop)
[14] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PageSerializationListener. html#willSerialize(org. zkoss. zk. ui. Page)
[15] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ComponentSerializationListener. html#willSerialize(org. zkoss. zk. ui.
Component)
[16] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ComponentSerializationListener. html#didDeserialize(org. zkoss. zk. ui.
Component)
[17] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PageSerializationListener. html#didDeserialize(org. zkoss. zk. ui. Page)
[18] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopSerializationListener. html#didDeserialize(org. zkoss. zk. ui.
Desktop)
[19] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ SessionSerializationListener. html#didDeserialize(org. zkoss. zk. ui.
Session)
[20] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ SessionActivationListener. html#didActivate(org. zkoss. zk. ui. Session)
[21] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopActivationListener. html#didActivate(org. zkoss. zk. ui.
Desktop)
[22] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PageActivationListener. html#didActivate(org. zkoss. zk. ui. Page)
[23] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ComponentActivationListener. html#didActivate(org. zkoss. zk. ui.
Component)
Integration 359
Integration
This chapter describes how to integrate ZK with other frameworks, including how to write a JSP/JSF tag with ZK
components, how to access ZK components in foreign Ajax channel, how to work with form-based framework, and
so on.
Presentation Layer
In following section, we will describe how to integrate the below frameworks in presentation layer which are
responsible for navigating users between pages and presenting data to users, like JSP and Struts.
JSP
Employment/Purpose
Basically there are two approaches to use ZK in JSP pages.
1. Use <jsp:include> to include a ZUL page.
2. Use ZK JSP Tags [4] in a JSP page directly.
Here we discuss the general concepts applicable to both approaches. For information of ZK JSP Tags, please refer to
ZK JSP Tags Essentials. It is also worth to take a look at the HTML Tags section.
Prerequisite
DOCTYPE
To use ZK components correctly, the JSP page must specify DOCTYPE as follows.
<html>
...
BODY Style
By default, ZK will set the CSS style of the BODY tag to width:100%;height:100% If you prefer to have the
browser to decide the height (i.e., the browser's default) for you, you could specify height:auto to the BODY
tag (optional).
<body style="height:auto">
...
JSP 360
Browser Cache
Though optional, it is suggested to disable the browser to cache the result page. It can be done as follows.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
In addition, you could invoke the following statement in JSP to tell ZK to drop desktops once the user navigates to
other URL. It is optional but it saves memory since the browser page is not cached and safe to remove if the user
navigates away.
<%
request.setAttribute(org.zkoss.zk.ui.sys.Attributes.NO_CACHE,
Boolean.TRUE);
%>
Notice that it has to be invoked before ZK JSP's zkhead tag, if ZK JSP is used, or before the first jsp:include
that includes a ZUL page.
HTML Form
ZK input components (datebox, slider, listbox and so on) work seamlessly with HTML form. In addition to Ajax,
you could process input in batch with legacy Servlets.
<html>
<body>
<z:page>
<form action="/foo/legacy">
<table>
<tr>
<td>When</td><td><z:datebox name="when"/></td>
</tr>
<tr>
<td>Which></td>
<td>
<z:listbox name="which">
<z:listitem label="choice 1"/>
<z:listitem label="choice 2"/>
</z:listbox>
</td>
</tr>
<tr>
<td><z:button type="submit" label="Submit"/></td>
<td><z:button type="reset" label="Reset"/></td>
JSP 361
</tr>
</form>
</z:page>
</body>
</html>
</rows>
</grid>
</n:form>
</window>
Once users press the submit button, a request is posted to the /fooLegacy servlet with the query string as follows.
?when=Nov+10%2C+2010&name=Mark+Gates&department=Manufactory&type=new
Thus, as long as you maintain the proper associations between name and value, your servlet could work as usual
without any modification.
If both John and Henry are selected, then the query string will contain:
who=john&who=henry
Notice that, to use the list boxes and tree controls with the name property, you have to specify the value property
for listitem and treeitem, respectively. They are the values being posted to the servlets.
Version History
Version Date Content
Struts
The use of Struts [1] with ZK is straightforward: just replace JSP pages with ZUL pages. You don't need to modify
action handlers, data models and others. All you need to do is to map the result view to a ZUL page instead of JSP.
In addition, EL expressions will work the same way even in the ZUL page.
<h:h2 xmlns:h="xhtml">
${messageStore.message}
</h:h2>
As shown, you could use the same EL expression to access the data provided by Struts and your action handler.
Then, you map the hello action to HelloWorld.zul by modifying WEB-INF/classes/struts.xml as
follows.
<action name="hello" class="org.apache.struts.helloworld.action.HelloWorldAction" method="execute">
<result name="success">/HelloWorld.zul</result>
</action>
Then, you could visit http://localhost:8080/Hello_World_Struts2_Ant/hello.action as you are used to and have the
same result.
Of course, it is a ZUL document. You could have any Ajax behavior you'd like.
Struts 364
package foo;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.ui.*;
import org.zkoss.zul.*;
import org.apache.struts.helloworld.model.MessageStore;
Submit Form
By replacing JSP with ZUML, you could enable a static page with ZK's power. And, you could do what any ZUML
documents can do. In other words, Struts is used only for Model and Controller, while ZK for View. However,
sometimes you have to redirect back to submit-based URL (maybe another action with parameters). It can be done
easily by enclosing the input components with HTML FORM. For example,
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
There is no Action mapped for namespace / and action name zkau. - [unknown location]
To avoid this problem, you can add the line below in struts.xml to exclude ZK au requests for struts filter
<struts>
<constant name="struts.action.excludePattern" value="/zkau"/>
<!-- other configurations -->
</struts>
Version History
Version Date Content
References
[1] http:/ / struts. apache. org/
[2] http:/ / struts. apache. org/ 2. x/ hello-world-using-struts-2. html
Portal 366
Portal
Configuration
Here we describe the standard configuration. Depending on the portal server, you might have more than one
configuration to set. For more information, please refer to ZK Installation Guide.
WEB-INF/portlet.xml
To use it, you first have to add the following portlet definition for DHtmlLayoutPortlet [1] into
WEB-INF/portlet.xml. Notice that expiration-cache must be set to zero to prevent portals from
caching the result.
<portlet>
<description>ZK loader for ZUML pages</description>
<portlet-name>zkPortletLoader</portlet-name>
<display-name>ZK Portlet Loader</display-name>
<portlet-class>org.zkoss.zk.ui.http.DHtmlLayoutPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>ZK</title>
<short-title>ZK</short-title>
<keywords>ZK,ZUML</keywords>
</portlet-info>
</portlet>
Portal 367
WEB-INF/web.xml
ZK portlet loader actually delegates the loading of ZUML documents to ZK Loader (DHtmlLayoutServlet [2]). Thus,
you have to configure WEB-INF/web.xml as specified in ZK Installation Guide, even if you want to use only
portlets.
Use ZK Portlet
Examples
How to pass a request parameter or attribute to a portlet depends on the portal. You have to consult the user's guide
of your favorite portal for details, or refer to ZK Installation Guide.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ http/ DHtmlLayoutPortlet. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ http/ DHtmlLayoutServlet. html#
ZK Filter 368
ZK Filter
If you prefer to Ajax-ize a dynamically generated HTML page (e.g., the output of a Velocity Servlet), you could use
ZK Filter to process the generated page. The content of the generated page will be interpreted by ZK Filter as a
ZUML document. Thus, please make sure the output is a valid ZUML document, such as it must be a valid XML. If
the output is HTML, it must be a valid XHTML document.
To enable ZK Filter, you have to configure WEB-INF/web.xml, as shown below.
<filter>
<filter-name>zkFilter</filter-name>
<filter-class>org.zkoss.zk.ui.http.DHtmlLayoutFilter</filter-class>
<init-param>
<param-name>extension</param-name>
<param-value>html</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>zkFilter</filter-name>
<url-pattern>/my/dyna.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>zkFilter</filter-name>
<url-pattern>/my/dyna/*</url-pattern>
</filter-mapping>
where url-pattern is the servlets that you would like ZK Filter to process.
The extension parameter (init-param) defines the language of the dynamical output. By default, it is html,
since most of legacy servlet generates a HTML document. If the output is a ZUL document, you could specify zul
as the extension.
Notice that, if you want to filter the output from include and/or forward, remember to specify the dispatcher element
with REQUEST and/or INCLUDE. Consult the Java Servlet Specification for details. For example,
<filter-mapping>
<filter-name>zkFilter</filter-name>
<url-pattern>/my/dyna/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
ZK Filter 369
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ UiFactory. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ impl/ AbstractUiFactory. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ impl/ AbstractUiFactory. html#getPageDefinition(org. zkoss. zk. ui. sys.
RequestInfo,
Foreign Templating Framework 370
[1] Apache Tiles (http:/ / tiles. apache. org/ ) is a typical templating framework and allows developers to assemble UI at both server and
client.
[2] For more information, please refer to ZK Component Reference.
Prerequisite
DOCTYPE
To use ZK components correctly, the templating page must specify DOCTYPE as follows.
<html>
...
Browser Cache
Though optional, it is suggested to disable the browser to cache the result page. It can be done as follows.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
<html>
<body>
<jsp:include page="frag.zul"/>
...
In other words, if the result page is assembled when the request is received, you don't need to do anything
specially[1]. However, if the assembling is done at the client side by using Ajax to request fragments after loaded,
you have to read the following section.
[1] You might take a look at Use ZK in JSP for more information.
<html>
<head>
<script src="http://code.jquery.com/jquery-1.4.2.min.js">
</script>
</head>
<body>
<div id="anchor"></div>
</body>
</html>
The zk.redrawCtrl parameter is used to control how a ZUL page is specified. In this case, since page is
specified, the generation of HTML, HEAD and BODY tags are disabled.
Then, you don't need to specify the zk.redrawCtl parameter when loading it at the client (e.g.,
$('#anchor').load('foo.zul')).
Foreign Templating Framework 372
Of course, if the fragment itself is a JSP page and then use inclusion to include a ZUL page (or use ZK JSP Tags),
then the generated HTML structure is already a correct HTML fragment (and you don't need to anything described
above).
<html>
<head>
<script src="http://code.jquery.com/jquery-1.4.2.min.js">
</script>
</head>
<body>
<div id="anchor"></div>
</body>
</html>
Note: since 5.0.8, assigning page to the zk.redrawCtrl parameter implies no-cache, i.e.,
zk.redrawCtrl=page implies org.zkoss.zk.desktop.nocache="true".
ID Generator
Each ZUL page we request by Ajax as described above will be an independent desktop. It means the browser
window will have several desktops, if we assemble UI this way. Thus, the component's UUID must be unique across
different desktops (of the same session[1]). The default ID generator can handle it well.
However, if you use a customized IdGenerator (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/
IdGenerator. html#), you have to generate component's UUID ( org.zkoss.zk.ui.Component)
IdGenerator.nextComponentUuid(org.zkoss.zk.ui.Desktop, org.zkoss.zk.ui.Component) (http:/ / www. zkoss. org/
javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ IdGenerator. html#nextComponentUuid(org. zkoss. zk. ui. Desktop,))
correctly. A typical trick is to encode desktop's ID as part of component's UUID.
Foreign Templating Framework 373
[1] In short, component's UUID must be unquie in the same session. It is OK to be duplicated in different session.
});
</script>
</body>
</html>
Since they are in different desktops, you have to use the group-scoped event queue[1] if you want to send events from
one desktop (such as leftside.zul) to another (such as rightside.zul). For more information, please refer to Event
Queues.
[1] The group-scoped event queue is available only in ZK EE. For ZK CE, you have to use the session-scoped event queue.
Version History
Version Date Content
Middleware Layer
Middleware usually means a special software between applications and a operating system. Here, "middleware
layer" denotes those frameworks that glue presentation and persistence layer together, including dependency
injection frameworks. In the following sections, we will discuss how to integrate them with ZK.
Spring
Overview
Spring Framework is a popular application development framework for enterprise Java. One key element is its
infrastructural support: a light-weighted container that manages POJOs as Spring beans and maintain beans'
dependency injection relationship. We will talk about several integration ways including wiring and accessing beans
in various conditions. We assume that readers have knowledge in Spring's basic configuration and concept such as
bean scope, we will therefore not cover these topics here. Please refer to Spring documentation [1]. We write these
source code examples based on Spring 3.0.5.RELEASE.
Configuration
The minimal Maven dependency you need is :
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
If you use scoped-proxy with CGLIB-based class proxy, you will need cglib.
<dependency>
<groupId>cglib</groupId>
Spring 375
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
Note: If you don't use Maven, please refer to Spring Framework Reference Documentation to know which JAR
file you need.
To integrate ZK application and Spring , the minimal configuration you have to setup is following:
Spring related configuration in web.xml
...
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
</beans>
• Line 12: Apply @Component on those classes that you plan to register them as Spring beans and specify base
package of those classes.
Spring 376
@Component
@Scope("session")
public class UserPreference {
...
}
• User preference should be distinct for each user but shared among multiple requests. It is suitable to be a session
scoped bean.
@Component
public class SystemConfiguration {
...
}
• As system configuration should be shared within the whole application, this should be a singleton bean.
Access Spring beans with EL
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window title="Access Bean with different scopes" border="normal" width="700px"
apply="org.zkoss.reference.developer.composer.ResolverComposer">
<vlayout>
<hlayout>
User Preference :
<label id="sessionValue">${userPreference.value}</label>
</hlayout>
<hlayout>
System Configuration :
<label id="singletonValue">${systemConfiguration.value}</label>
</hlayout>
</vlayout>
</window>
• The delegating variable-resolver will look-up the bean named userPreference automatically for you.
Spring 377
Using Scoped-Proxy
We recommend you to apply scoped-proxy on every bean that is used in a composer( or a ViewModel). There are at
least two reasons. First, in "Spring Framework Reference" mentions that when you inject a shorter-lived scoped bean
into a longer-lived scoped bean, you should use scoped-proxy bean. The proxy can fetch the real shorter-lived
scoped bean for longer-lived scoped bean and delegate all method calls onto the real bean. Because the scope of
composer ( or a ViewModel) doesn't match any of Spring's bean scopes, using scoped-proxy ensures you call a
method on the right bean. Second, a reference to a bean might be invalid after session replication in clustering
environment. Hence, even you use a singleton bean, it's better to use the scoped-proxy.
To apply scoped-proxy to a bean, just apply @Scope with proxyMode specified on a bean.
@Service("orderService")
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
public class OrderServiceImpl implements OrderService {
...
}
• Line 2: If you use ScopedProxyMode.TARGET_CLASS, you need CGLIB library in the classpath of your
application.
@WireVariable
private OrderService orderService;
@Wire("#number")
private Label label;
@Override
public void doAfterCompose(Window comp) throws Exception {
super.doAfterCompose(comp);
Spring 378
label.setValue(Integer.toString(orderService.list().size()));
}
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window title="Access Bean with different scopes" border="normal" width="700px"
apply="org.zkoss.reference.developer.spring.composer.ResolverComposer">
...
</window>
@WireVariable
OrderService orderService;
...
</window>
</zk>
Spring 379
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class SpringComposer extends SelectorComposer<Window> {
@WireVariable
private OrderService orderService;
...
}
@Wire("#number")
private Label label;
@Override
public void doAfterCompose(Window comp) throws Exception {
super.doAfterCompose(comp);
OrderService orderService =
(OrderService)SpringUtil.getBean("orderService");
label.setValue(Integer.toString(orderService.list().size()));
}
}
Version History
Version Date Content
References
[1] http:/ / www. springsource. org/ spring-framework#documentation
[2] https:/ / github. com/ zkoss/ zkbooks/ tree/ master/ developersreference/ integration. spring
CDI
Overview
Contexts and Dependency Injection (CDI) is one of Java EE 6 features and is composed of a set of services designed
for using with stateful objects. It also allows developers to integrate various kinds of objects in a loosely coupled and
type safe way. We will talk about several ways of integration including injecting and accessing CDI beans under
different conditions. We assume that readers have knowledge in basic CDI configuration and concept such as bean
scope, we will therefore not cover those topics here. Please refer to Oracle's CDI tutorial [1].
@SessionScoped @Named
public class UserPreference implements Serializable{
...
}
• User preference should be distinct for each user but shared among multiple requests. It is suitable to be a session
scoped bean.
Application scoped bean
CDI 381
@ApplicationScoped @Named
public class SystemConfiguration implements Serializable{
...
}
• As system configuration should be shared within the whole application, it should be an application scoped bean.
Access bean using EL in a ZUL
<?variable-resolver class="org.zkoss.zkplus.cdi.DelegatingVariableResolver"?>
...
<hlayout>
User Preference :
<label id="sessionValue">${userPreference.value}</label>
</hlayout>
<hlayout>
System Configuration :
<label id="applicationValue">${systemConfiguration.value}</label>
</hlayout>
...
@WireVariable("normalOrderService")
NormalOrderService orderService;
@Wire("#number")
private Label label;
@Override
public void doAfterCompose(Window comp) throws Exception {
super.doAfterCompose(comp);
CDI 382
label.setValue(Integer.toString(orderService.findAll().size()));
}
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window title="Access Bean with different scopes" border="normal" width="700px"
apply="org.zkoss.reference.developer.spring.composer.ResolverComposer">
...
</window>
@WireVariable
private UserPreference userPreference;
@WireVariable
private ProductService productService;
@Init
public void doAfterCompose(Window comp) throws Exception {
productList = productService.findAll();
}
<?variable-resolver class="org.zkoss.zkplus.cdi.DelegatingVariableResolver"?>
<window border="normal" width="500px"
apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm')@init('org.zkoss.reference.developer.composer.MyViewModel')">
...
</window>
CDI 383
@VariableResolver(org.zkoss.zkplus.cdi.DelegatingVariableResolver.class)
public class MyComposer extends SelectorComposer<Window> {
@WireVariable
private UserPreference userPreference;
...
}
Version History
Version Date Content
References
[1] http:/ / docs. oracle. com/ javaee/ 6/ tutorial/ doc/ gjbnr. html
[2] https:/ / github. com/ zkoss/ zkbooks/ tree/ master/ developersreference/ integration. cdi
EJB 384
EJB
Enterprise JavaBeans (EJB) technology is the server-side component architecture for Java EE. Here we describe how
to use it in a ZUML document.
Here we use JBoss [1] as the example. The configuration of the server might vary from one server to another, but the
ZUML document is the same.
Notice that if you would like to access EJB in Java (such as in a composer or in a richlet), you could skip this section
(since you could use the approach described in any EJB guide).
Depending your configuration, you might have to pass extra information about JNDI to it such as:
<?variable-resolver class="org.zkoss.zkplus.jndi.JndiVariableResolver"
arg0="ZkEJB3Demo"
arg1="mail=java:comp/env/mailing,sec=java:comp/security/module" ?>
<!--
arg0: prepend - the prepended part of JDNDI name
arg1: mapping - the key-value pairs for JNDI names and the
corresponding variable names
-->
<window>
...
</window>
<ejb-local-ref>
<ejb-ref-name>personLocalBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>demo.PersonBeanLocal</local>
<local-jndi-name>ZkEJB3Demo/PersonBean/local</local-jndi-name>
</ejb-local-ref>
web.xml:
<ejb-local-ref>
<ejb-ref-name>personLocalBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local-home>demo.PersonBeanLocal</local-home>
<local>demo.PersonBeanLocal</local>
</ejb-local-ref>
The variables provided by a variable resolver is also available to the Java code in zscript. For example,
<zscript>
personLocalBean.createDemoData();
</zscript>
<properties>
</properties>
</persistence-unit>
EJB 386
Then, we could retrieve the entity manager factory by use of JndiVariableResolver [2].
Source Code
You can get all source code mentioned in this section at github [3]
Version History
Version Date Content
References
[1] http:/ / jboss. org
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ jndi/ JndiVariableResolver. html#
[3] https:/ / github. com/ zkoss/ zkbooks/ tree/ master/ developersreference/ integration. ejb
Persistence Layer
In the following sections, we are going to describe considerations and issues when you use persistence frameworks
with ZK.
JDBC
ZK aims to be as thin as the presentation tier. In addition, as the code executes at the server, so connecting database
is no different from any desktop applications. In other words, ZK doesn't change the way you access the database, no
matter you use JDBC or other persistence framework, such as Hibernate [1].
Use JDBC
The simplest way to use JDBC, like any JDBC tutorial might suggest, is to use java.sql.DriverManager.
Here is an example to store the name and email into a MySQL [2] database.
@Listen("onClick = button")
public void submit() {
PreparedStatement stmt = null;
Connection conn = null;
JDBC 387
try {
//load driver and get a database connection
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost/test?user=root&password=R3f@ct0r");
stmt = conn.prepareStatement("INSERT INTO user
values(?, ?)");
<vbox>
<button label="submit"/>
</vbox>
</window>
Though this way is simple, but it has obvious drawback. After all, ZK applications are web-based applications,
where loading is unpredictable and treasurable resources such as database connections have to be managed more
JDBC 388
effectively.
Luckily, all J2EE frameworks and Web servers support a utility called connection pooling. It is straightforward to
use, while managing the database connections well. We will discuss more in the next section.
Tip: Unlike other Web applications, it is possible to use DriverManager with ZK, though not
recommended.
First, you could cache the connection in the desktop, reuse it for each event, and close it when the desktop becomes
invalid. It works just like traditional Client/Server applications. Like Client/Server applications, it works efficiently
only if there are at most tens concurrent users.
To know when a desktop becomes invalid, you have to implement a listener by use of DesktopCleanup [3].
@Wire
private Textbox name;
@Wire
private Textbox email;
@Listen("onClick = button")
public void submit() {
stmt.setString(1, name.getValue());
stmt.setString(2, email.getValue());
stmt.executeUpdate();
stmt.close();
stmt = null;
} catch (SQLException e) {
try{
conn.rollback();
}catch(SQLException ex){
//log
}
//(optional log and) ignore
} catch (Exception e) {
//log
} finally { //cleanup
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ex) {
//(optional log and) ignore
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
//(optional log and) ignore
}
}
}
}
}
<vbox>
<button label="submit"/>
</vbox>
</window>
Notes:
• It is important to close the statement and connection after use.
• You could access multiple databases at the same time with multiple connections. Depending on the configuration
and J2EE/Web servers, these connections could even form a distributed transaction.
JDBC 390
<!-- The name you used above, must match _exactly_ here!
The connection pool will be bound into JNDI with the name
"java:/comp/env/jdbc/MyDB"
-->
<Resource name="jdbc/MyDB" username="someuser" password="somepass"
url="jdbc:mysql://localhost:3306/test"
auth="Container" defaultAutoCommit="false"
driverClassName="com.mysql.jdbc.Driver" maxActive="20"
timeBetweenEvictionRunsMillis="60000"
type="javax.sql.DataSource" />
Then, in web.xml, you have to add the following content under the <web-app> element as follows.
<resource-ref>
<res-ref-name>jdbc/MyDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Notes
[1] http:/ / www. hibernate. org/
[2] http:/ / www. mysql. com/
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopCleanup. html#
[4] Thanks Thomas Muller ( (http:/ / asconet. org:8000/ antville/ oberinspector)) for correction.
See also (http:/ / tomcat. apache. org/ tomcat-5. 5-doc/ jndi-resources-howto. html) and (http:/ / en. wikibooks. org/
wiki/ZK/How-Tos/HowToHandleHibernateSessions#Working_with_the_Hibernate_session) for more details.
JBoss + MySQL
The following instructions is based on section 23.3.4.3 of the reference manual of MySQL 5.0.
To configure connection pool for JBoss, you have to add a new file to the directory called deploy
($JBOSS_DIR/server/default/deploy). The file name must end with "*-ds.xml" (* means the
database, please refer to $JBOSS_DIR/docs/examples/jca/), which tells JBoss to deploy this file as JDBC
Datasource. The file must have the following contents. The information that depends on your installation and usually
need to be changed is marked in the blue color.
mysql-ds.xml:
<datasources>
<local-tx-datasource>
<!-- This connection pool will be bound into JNDI with the name
"java:/MyDB" -->
JDBC 391
<jndi-name>MyDB</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/test</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>someuser</user-name>
<password>somepass</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>20</max-pool-size>
<idle-timeout-minutes>5</idle-timeout-minutes>
<exception-sorter-class-name>com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter</exception-sorter-class-name>
<valid-connection-checker-class-name>com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker</valid-connection-checker-class-name>
</local-tx-datasource>
</datasources>
To specify the JNDI name at which the datasource is available , you have to add a jboss-web.xml file under the
WEB-INF folder.
jboss-web.xml
<jboss-web>
<resource-ref>
<res-ref-name>jdbc/MyDB</res-ref-name>
<jndi-name> java:/MyDB </jndi-name>
</resource-ref>
</jboss-web>
In web.xml, you have to add the following content under the <web-app> element as follows.
<resource-ref>
<res-ref-name>jdbc/MyDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
JDBC 392
</resource-ref>
JBoss + PostgreSQL
<datasources>
<local-tx-datasource>
<!-- This connection pool will be bound into JNDI with the name
"java:/MyDB" -->
<jndi-name>MyDB</jndi-name>
<driver-class>org.postgresql.Driver</driver-class>
<user-name>someuser</user-name>
<password>somepass</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>20</max-pool-size>
<track-statements>false</track-statements>
</local-tx-datasource>
</datasources>
download
• Please download the source(Tomcat 5.5 (and above) + MySQL) (https://sourceforge.net/projects/zkforge/files/
Small Talks/JDBC(JNDI sample)/Mysql_tomcat.war/download)
• Please download the source(JBoss + MySQL) (https://sourceforge.net/projects/zkforge/files/Small Talks/
JDBC(JNDI sample)/jboss+mysql.zip/download)
Version History
Version Date Content
Hibernate 393
Hibernate
Overview
Due to object/relational paradigm mismatch [1], developers tend to use ORM (object/relational mapping) framework
to convert object-oriented model to relational model and vice versa. Hibernate is the most popular ORM framework
in Java world. We will talk about some integration topics in this chapter such as lazy initialization with Spring. If
you haven't read about basic concepts and installation of Hibernate, please refer to Hibernate Dcumentation [2]. The
example we give in this chapter is based on Hibernate 4.0.0.final and Spring 3.1.2.RELEASE.
Homemade DAO
Here we introduce how to implement an DAO without other frameworks (e.g. Spring).
Configuration
The minimal Maven dependency you need is:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.0.0.Final</version>
<scope>compile</scope>
</dependency>
Note: If you don't use Maven, please refer to Hibernate Reference Documentation to know which JAR file you
need.
Hibernate 394
Utility Class
A simple way to implement a DAO is to control Hibernate sessions and transactions manually, hence we need a
utility class to get SessionFactory. ZK's HibernateUtil [5] has been deprecated since 6.0.2, you can write your
own one according to the code example in Hibernate Reference Manual.[6] Here we provide a simple example.
Utility class to get SessionFactory
package org.zkoss.reference.developer.hibernate.dao;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
Hibernate 395
}
}
HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
} else {
final Throwable ex = (Throwable) errs.get(0);
rollback(exec, ex);
}
}
}
HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();
}
} catch (Throwable rbEx) {
log.error("Could not rollback transaction after
exception! Original Exception:\n"+ex, rbEx);
}
}
}
<listener>
<listener-class>org.zkoss.reference.developer.hibernate.web.OpenSessionInViewListener</listener-class>
</listener>
</zk>
Hibernate 396
DAO Implementation
The listener begins and commits transactions keeping DAO's implementation simple. Just use the utility class to get
current Hibernate session to perform the operation.
Simple DAO implementation
/**
* rollback is handled in filter.
* @param newOrder
* @return
* @throws HibernateException
*/
public Order save(Order newOrder) throws HibernateException{
Session session =
HibernateUtil.getSessionFactory().getCurrentSession();
session.save(newOrder);
session.flush();
return newOrder;
}
}
Finally, we can use the DAO class in a ViewModel to manipulate domain objects.
Use DAO in a ViewModel
@Init
public void init(){
orders = orderDao.findAll();
if (!orders.isEmpty()){
setSelectedItem(orders.get(0));
}
}
Hibernate 397
Spring-based DAO
With Spring provided dependency injection and ORM support, here our efforts are simplified quite substantially.
We'll demonstrate one typical usage in non-managed environment. To apply session-per-request pattern, we can use
Spring provided OpenSessionInViewFilter instead of writing our own one. According to the suggestion in
Spring Reference Documentation 3.1, using plain Hibernate API to implement a DAO is the current recommended
usage pattern. In the DAO, we can also easily retrieve SessionFactory by dependency inject without any utility
classes (HibernateUtil). Besides, declarative transaction management and rollback rule also reduces our work.
The following are the the related code snippets.
Configuration
The minimal dependencies you need are Hibernate-Core, Spring-Web, Spring-ORM, and Cglib:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
Hibernate 398
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
</bean>
<!--
hibernate.current_session_context_class=thread
-->
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.HSQLDialect
hibernate.hbm2ddl.auto=crate
hibernate.show_sql=true
hibernate.connection.pool_size=5
hibernate.connection.autocommit=false
</value>
</property>
<property name="annotatedClasses">
<list>
<value>org.zkoss.reference.developer.hibernate.domain.Order</value>
<value>org.zkoss.reference.developer.hibernate.domain.OrderItem</value>
</list>
</property>
</bean>
</bean>
<tx:annotation-driven />
</beans>
Hibernate 399
• Line 40: For Hibernate 3.x, some package names should be changed to
org.springframework.orm.hibernate3.*, e.g.
org.springframework.orm.hibernate3.HibernateTransactionManager.
OpenSessionInViewFilter
Spring already provides a OpenSessionInViewFilter to solve lazy loading in web views problems. This filter makes
Hibernate Sessions available via the current thread, which will be auto-detected by Spring's transaction managers.
For detailed description and usage, please refer to Spring's Javadoc.
Configure OpenSessionInViewFilter in web.xml
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
DAO Implementation
For a Spring-powered DAO, we can use injected SessionFactory and @Transactional to perform
persistence operation.
DAO empowered by Spring
@Repository
public class SpringOrderDao {
@Autowired
private SessionFactory sessionFactory;
@Transactional(readOnly=true)
public List<Order> queryAll() {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("select o from Order as
o");
List<Order> result = query.list();
return result;
}
@Transactional
public Order save(Order newOrder){
Session session = sessionFactory.getCurrentSession();
session.save(newOrder);
session.flush();
return newOrder;
}
Hibernate 400
To use this Spring-based DAO in a composer (or a ViewModel), ZK provides several ways like variable resolvers.
Please refer to ZK Developer's Reference/Integration/Middleware Layer/Spring.
@Entity
@Table(name="orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String status = PROCESSING;
private String description;
@OneToMany(mappedBy="orderId", fetch=FetchType.LAZY)
private List<OrderItem> items = new ArrayList<OrderItem>();
//other codes...
}
By default, we set the Listbox selection on the first row. When ZUL accesses the selected order's lazy-loaded items
collection, Hibernate can load it successfully with the help of open-session-in-view filter because session is still
open. However, if we click the second row which also accesses a detached Order object's items collection, we
should re-load the object with Hibernate session or we'll get LazyInitializationException.
Hibernate 401
@Init
public void init(){
orders = orderDao.findAll();
setSelectedItem(orders.get(0));
}
• Line 11: Initialize the Listbox selection with the Order object at index 0 of orders.
• Line 16: Re-query the selectedItem.
We use Session.load() to re-query the Order object with its id, this newly-retrieved object still has an open
Hibernate session. Then when we access the lazy-loaded collection (items) in the ZUL, Hibernate can retrieve the
collection for us. After doing so, we can eliminate LazyInitializationException.
Hibernate 402
Re-attach to a session
public class OrderDao {
//...
/**
* Initialize lazy-loaded collection.
* @param order
* @return
*/
public Order reload(Order order){
return
(Order)HibernateUtil.getSessionFactory().getCurrentSession().load(Order.class,order.getId());
}
}
If we just pass a Java List object to be the model of the Listbox, when a user scrolls down to view other rows, ZK
will send AU request to retrieve data for those un-rendered rows. Listbox will try to access lazy-loaded collection but
objects in the list are already detached, and we will get LazyInitailizationException. During this
rendering process, developers will not be notified and cannot interfere this process to reload detached objects. One
solution is to implement a custom ListModel [2] for the component.
We demonstrate 2 implementations here for your reference. The first one is simpler but less efficient; it re-queries
each detached object when a component requests it.
Reloaded ListModel
@Override
public Order getElementAt(int index) {
//throw a runtime exception if orderDao does not find
target object
Order renewOrder = orderDao.reload(orderList.get(index));
return renewOrder;
}
@Override
public int getSize() {
return orderList.size();
}
}
• Line 1: We extends AbstractListModel [7] to build our list model for it handles selection for us, but we have to
implement Order's equals() and hashCode().
• Line 14: Re-query the detached object by its id and return a persistent one.
The second one is more complicated but more efficient; it re-queries a one page size data each time and stores as a
cache in an execution. If the cache has the object that the component requests, it returns the one in cache without
re-querying again.
Lived ListModel
/**
* query one page size of entity for one execution a time.
*/
@Override
public Order getElementAt(int index) {
Map<Integer, Order> cache = getCache();
}
return cache;
}
@Override
public int getSize() {
if (totalSize == null){
totalSize = orderDao.findAllSize().intValue();
}
return totalSize;
}
}
• Line 16: If the cache doesn't contain target Order, we query a one page size of Order starting from the index
as passed index doesn't always increase sequentially.
• Line 42: The getElementAt(int) will be invoked multiple times during an execution. In order to avoid
using a cache of detached objects, we make the cache as an attribute of an execution which is dropped after being
handled.
Reference
[1] Hibernate in Action, Christian Bauer, Gavin King, Manning
[2] http:/ / www. hibernate. org/ docs
[3] Unit of Work in Hibernate Core Reference Manual (http:/ / docs. jboss. org/ hibernate/ core/ 3. 6/ reference/ en-US/ html_single/
#transactions-basics-uow)
[4] Open Session in View\Problem (https:/ / community. jboss. org/ wiki/ OpenSessionInView#The_problem)
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ hibernate/ HibernateUtil. html#
[6] Hibernate Reference Documentation\ Tutorial (http:/ / docs. jboss. org/ hibernate/ core/ 3. 6/ reference/ en-US/ html_single/
#tutorial-firstapp-helpers)
[7] Open Session in View\Using an intercepto (https:/ / community. jboss. org/ wiki/ OpenSessionInView#Using_an_interceptor)
[8] Hibernate Reference Documentation/contextual session (http:/ / docs. jboss. org/ hibernate/ orm/ 4. 1/ manual/ en-US/ html_single/
#architecture-current-session)
[9] The reason is explained in a Hibernate article "Open Session in View" (https:/ / community. jboss. org/ wiki/
OpenSessionInView#Why_cant_Hibernate_just_load_objects_on_demand)
[10] https:/ / code. google. com/ p/ zkbooks/ source/ browse/ trunk/ developersreference/ integration. hibernate/ src/ main/ webapp/ homemade/
order. zul
[11] https:/ / github. com/ zkoss/ zkbooks/ tree/ master/ developersreference/ integration. hibernate
Hibernate 406
Version History
Version Date Content
JPA
Overview
Java Persistence API (JPA) is a POJO-based persistence specification. It offers object-relational mapping solution to
enterprise Java applications. We will demonstrate examples on how to integrate a popular and commonly used
combination: JPA & Spring. In our example project, we use a popular JPA 2.0 implementation, Hibernate
4.0.0.Final. We will also talk about solutions against well-known LazyInitializationException.
Configuration
The minimal Maven dependencies you need are:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
JPA 407
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.0.0.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
<scope>compile</scope>
</dependency>
Note: If you don't use Maven, please refer to JPA vendor's documentation to know which JAR file you need.
Our example project's Spring configuration is for non-managed environment.
Spring configuration
<!-- omit headers -->
</bean>
</bean>
</bean>
<tx:annotation-driven />
OpenEntityManagerInViewFilter
To apply entitymanager-per-request pattern, we can use Spring provided
OpenEntityManagerInViewFilter instead of writing our own one. Make sure filter mapping's url-pattern
covers all pages that access lazy-loaded entity. If you don't want this filter intercepting all pages, be sure to include
ZK AU request path (/zkau/*) in url-pattern as your event handling methods (or command methods) might also
access lazy-loaded objects.
Configure OpenEntityManagerInViewFilter in web.xml
<filter>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
DAO Implemnetation
In the DAO, we can easily retrieve EntityManager by Spring's dependency inject without writing any utility
class. Spring's declarative transaction management and rollback rule also reduces our work.
DAO empowered by Spring
@Repository
public class SpringOrderDao {
@PersistenceContext
private EntityManager em;
@Transactional(readOnly=true)
public List<Order> queryAll() {
Query query = em.createQuery("from Order as o");
List<Order> result = query.getResultList();
return result;
}
@Transactional
public Order save(Order newOrder){
em.persist(newOrder);
em.flush();
return newOrder;
}
//...
}
@Entity
@Table(name="orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String status = PROCESSING;
private String description;
@OneToMany(mappedBy="orderId", fetch=FetchType.LAZY)
private List<OrderItem> items = new ArrayList<OrderItem>();
//other codes...
}
We set Listbox to select the first row of the orders as default. When the ZUL accesses the selected order's
lazy-loaded items collection, JPA can load it successfully with the help of
OpenEntityManagerInViewFilter because EntityManager is still open. However, if we click the
second row which accesses a detached Order object's items collection, we should re-load the Order object with
JPA EntityManager or we'll get LazyInitializationException.
JPA 410
@WireVariable
private SpringOrderDao springOrderDao;
@Init
public void init(){
orders = springOrderDao.queryAll();
if (!orders.isEmpty()){
setSelectedItem(orders.get(0));
}
}
• Line 12: Initialize the Listbox selection with the Order object at index 0 of orders.
• Line 17: Re-query the selectedItem.
We reload the detached Order objects from the database, this will make the detached object attach to an
EntityManager. Then, when we access the lazy-loaded collection (items), JPA can retrieve the collection for
us. After doing so, we can eliminate LazyInitializationException.
Reload detached object
@Repository
public class SpringOrderDao {
@PersistenceContext
private EntityManager em;
@Transactional(readOnly=true)
public Order reload(Order order){
return em.find(Order.class, order.getId());
}
If we just pass a Java List object to be the model of the Listbox, when a user scrolls down to view other rows, ZK
will send AU request to retrieve data for those un-rendered rows. Listbox will try to access lazy-loaded collection but
objects in the list are already detached, and we will get LazyInitailizationException. During this
rendering process, developers will not be notified and cannot interfere this process to reload detached objects. One
solution is to implement a custom ListModel [2] for the component.
We demonstrate 2 implementations here for your reference. The first one is simpler but less efficient; it re-queries
each detached object when a component requests it.
Reloaded ListModel
@Override
public Order getElementAt(int index) {
//throw a runtime exception if orderDao does not find
target object
Order renewOrder = orderDao.reload(orderList.get(index));
return renewOrder;
}
@Override
public int getSize() {
return orderList.size();
}
}
• Line 1: We extend AbstractListModel [7] to build our list model for it to handle selection for us, but we have to
override Order's equals() and hashCode().
• Line 14: Re-query the detached object by its id and return a persistent one.
The second one is more complicated but more efficient; it re-queries a one page size data each time and stores as a
cache in an execution. If the cache has the object that the component requests, it returns the one in cache without
re-querying it again.
Lived ListModel
JPA 413
/**
* query one page size of entity for one execution a time.
*/
@Override
public Order getElementAt(int index) {
Map<Integer, Order> cache = getCache();
@Override
public int getSize() {
if (totalSize == null){
totalSize = orderDao.queryAllSize().intValue();
}
return totalSize;
}
}
• Line 16: If the cache doesn't contain target Order, we query one page size of Order starting from the index
because passed index doesn't always increase sequentially.
• Line 42: The getElementAt(int) will be invoked multiple times during an execution. In order to avoid
using a cache of detached objects, we make the cache as an attribute of an execution which is dropped after being
handled.
Reference
[1] Unit of Work in Hibernate EntityManager User Guide (http:/ / docs. jboss. org/ hibernate/ entitymanager/ 3. 6/ reference/ en/ html_single/
#transactions-basics-uow)
[2] Open Session in View\Problem (https:/ / community. jboss. org/ wiki/ OpenSessionInView#The_problem)
[3] The reason is explained in a Hibernate article "Open Session in View" (https:/ / community. jboss. org/ wiki/
OpenSessionInView#Why_cant_Hibernate_just_load_objects_on_demand)
[4] https:/ / code. google. com/ p/ zkbooks/ source/ browse/ trunk/ developersreference/ integration. jpa/ src/ main/ webapp/ order. zul
[5] https:/ / github. com/ zkoss/ zkbooks/ tree/ master/ developersreference/ integration. jpa
JPA 415
Version History
Version Date Content
Security
In the following sections, we are going to describe how to integrate security frameworks with ZK.
Spring Security
Overview
Spring Security is an application framework that provides security services for J2EE-based enterprise software
application. It is a popular and widely adopted framework, in this article we will demonstrate how to integrate it to
secure a ZK application including securing pages, handling authentication process, securing components, and
securing events. Our example is a simple forum-like application. Users can read, create, edit, and delete an article
according to his authorities.
Configuration
Maven
We need to add dependencies for Spring Security and Maven's transitive dependency management can include all
necessary dependencies of Spring for us.
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
• Line 4: Becuase we use the security namespace in the application context, we need
spring-security-config.
• Line 16: Spring-core depends on commons-logging.
• Line 21: The cglib is optional. We add it because we use CGLIB-based class proxy.
Note: If you don't use Maven, please refer to Spring Security Reference Documentation to check which JAR files
are needed.
Spring
Our example application also integrates Spring framework, the required configuration in web.xml is as follows:
web.xml
<!-- Loads the Spring application context configuration -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<context:component-scan base-package="org.zkoss.reference.developer.spring.security.model"/>
<import resource="applicationContext-security.xml"/>
</beans>
• Line 9: We can register beans by class-path scanning to reduce XML configuration effort.
• Line 11: We can import another configuration file for Spring Security.
Spring Security 417
This filter is a hook into Spring Security's web infrastructure. It intercepts all requests and hands over them to be
processed by Spring Security internal filters.
Namespace configuration has been supported by Spring framework since version 2.0 and it is an alternative
configuration syntax which is closer to problem domain. It also can reduce configuration's complexity because one
element may contain multiple beans and processing steps. To use the security namespace, you should have
spring-security-config in your classpath and add the schema declaration to your application context file:
applicationContext-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<form-login login-page="/login.zul"
authentication-failure-url="/login.zul?login_error=1"
Spring Security 418
login-processing-url="/j_spring_security_check"/>
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
</beans:beans>
Here we introduce some main elements and will leave the details in the subsequent sections.
• LIne 12: The <http> element is the parent for all web-related namespace functions and we use
auto-config to save configuration efforts. We also create a HTTPS configuration sample in
applicationContext-security.xml. Please see source code for details.
• Line 32: Each Spring Security application which uses the namespace configuration must include
<authentication-manager> . It is responsible for registering the AuthenticationManager which
provides authentication services to the application.
• Line 33:We implement our MyUserDetailsService bean to provide authentication service and configure it
in <authentication-provider> element.
Secure Pages
In Spring Security, pages are protected by <intercept-url> element under <http>. We can specify in
pattern to match against the URLs of incoming requests using an ant path style syntax in <intercept-url>
element. The access attribute defines the access permission for requests which match the given pattern. Here
we use simple role-based access control.
In most cases, we usually secure all pages with :
<intercept-url pattern="/**" access="ROLE_USER" />
The "ROLE_USER" is an authority string we define and give for each authenticated user in our custom user service,
MyUserDetailsService.
Then we can selectively allow some pages for anonymous access like:
<intercept-url pattern="/login.zul" access="IS_AUTHENTICATED_ANONYMOUSLY" />
IS_AUTHENTICATED_ANONYMOUSLY is a built-in permission value used to grant access to anonymous users
applicationContext-security.xml
<!-- HTTP configuration sample -->
<http auto-config="true">
<!-- ZK AU requests -->
<intercept-url pattern="/zkau/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<!-- the login page -->
<intercept-url pattern="/login.zul" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<!-- pages for anonymous access in an application -->
Spring Security 419
...
</http>
• Line 4: ZK AU requests must be available for anonymous access or ZK can't work normally.
• Line 6: Remember to set login page URL available to anonymous users, otherwise users wont be able to access
the log-in page (this is a common configuration error).
• Line 14: Restrict all page requests with permission ROLE_USER.
Authentication
Setting auto-config enables form-based login process automatically but it uses Spring Security's built-in login
page. We usually build our own login page so we can specify our custom login page URL to override the default
configuration.
applicationContext-security.xml
<form-login login-page="/login.zul"
authentication-failure-url="/login.zul?login_error=1"
/>
• Line 5: Specify the URL used to render the login page at login-page.
• Line 5: Specify the URL to redirect the browser on login failure,
• Line 8: Specify the destination URL in which the user will be redirected to after logging out.
In order to let Spring Security handle authentication, we should use HTML's form in a zul.
login.zul
...
<html:form action="j_spring_security_check" method="POST"
xmlns:html="native">
<grid>
<rows>
<row>User: <textbox id="u" name="j_username"/></row>
<row>Password: <textbox id="p" type="password" name="j_password"/></row>
<row spans="2">
<hbox>
<button type="reset" label="Reset" />
<button type="submit" label="Submit" />
</hbox>
Spring Security 420
</row>
</rows>
</grid>
</html:form>
• Line 2: The default action URL monitored by Spring Security filter is j_spring_security_check.
• Line 6,7: The login form should contain j_username and j_password input fields.
In most cases, each application will have its own way to authenticate a user and Spring Security provides various
authentication provider to achieve it. We can create a simple MyUserDetailsService which implements
Spring Security's UserDetailsService interface to perform our own authentication.
MyUserDetailsService
@Service
public class MyUserDetailsService implements UserDetailsService {
add(new MyUser("rod","81dc9bdb52d04dc20036dbd8313ed055",
//password:1234
new String[]{"ROLE_USER", "ROLE_EDITOR"} ));
add(new MyUser("dianne","81dc9bdb52d04dc20036dbd8313ed055",
add(new MyUser("scott","81dc9bdb52d04dc20036dbd8313ed055",
new String[]{"ROLE_USER"} ));
add(new MyUser("peter","81dc9bdb52d04dc20036dbd8313ed055",
new String[]{"ROLE_USER"} ));
}
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService">
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
Secure Components
Every authenticated user has his/her own authorities. A common scenario is where we want to control UI's status
according to current user's authorities. In our example, an anonymous user can only view an article, and a user with
"ROLE_USER" can see a disabled "Delete" button. But a user with "ROLE_EDITOR" can see the "Delete" button
and be able to click it.
//omit import
(currentUser.getAuthorities().isEmpty())) {
return Collections.emptyList();
}
Collection<? extends GrantedAuthority> granted = currentUser.getAuthorities();
return granted;
}
• Line 7: Return true if the authenticated principal is granted ALL of the roles specified in authorities. The input
parameter is a comma separated list of roles which the user have been granted.
Then we still have to write a description file to describe the functions that we can use in a zul. In our example, it is
/WEB-INF/security.tld. You can read the file in the source code.
security.tld
<taglib>
<uri>http://www.zkoss.org/demo/integration/security</uri>
<description>
</description>
<function>
<name>isAllGranted</name>
<function-class>org.zkoss.reference.developer.spring.security.SecurityUtil</function-class>
</function-signature>
<description>
</description>
</function>
...
</taglib>
Before using a tag library in a zul, we should load its tld file with a directive.
articleContent.zul
The function isAllGranted() will return true if the authenticated principal is granted all of the roles specified
in authorities. An anonymous user doesn't have authority "ROLE_USER", so the "Delete" button will not be created.
If a user have authority "ROLE_EDITOR", he can see an enabled "Delete" button (the disable will be false).
You can use our SecurityUtil and security.tld as reference and write your own one to apply in your
application.
Secure Events
If you want to restrict available actions according to a business rule or a dynamic status, this cannot be achieved by
tag library. To do this, Spring Security provides a "method security" which can add security to your service layer
methods. To use this feature, you should declare as follows:
applicationContext-security.xml
Then add @Secure to those methods you want to secure with permissions.
ArticleService.java
@Secured({"ROLE_USER", "IS_AUTHENTICATED_ANONYMOUSLY"})
public List<Article> findAll();
@Secured({"ROLE_USER", "IS_AUTHENTICATED_ANONYMOUSLY"})
public Article find(long id);
@Secured({"ROLE_USER"})
public void create(Article a);
@Secured({"ROLE_EDITOR","ROLE_USER"})
public void update(Article a);
@Secured({"ROLE_EDITOR"})
public void delete(long id);
}
If a user uses the service and has no permission, Spring Security will throw its AccessDeniedException.
If the security checking is more dynamic and cannot be determined in compile time, we can check a user's
permission in an event listener of a controller. In our example, a user with "ROLE_EDITOR" can edit any article but
a user with "ROLE_USER" can only edit those articles written by himself/herself. We can check this when a user
clicks the "Edit" button:
permission checking in an event listener
@VariableResolver(DelegatingVariableResolver.class)
public class ArticleContentViewCtrl extends SelectorComposer<Component> {
@Listen("onClick=#openEditorBtn")
Spring Security 425
• Line 9~11: If the current login user is neither the owner of the article nor has the authority "ROLE_EDITOR", we
will not allow the editing of the article and throw a Spring Security's AccessDeniedException.
If we throw a runtime exception for an access with insufficient permission, ZK will show the error message on the
page by default. But for an unauthenticated user (not log in yet), we can even do more: redirect the anonymous user
to the login page. We will show how to achieve this in ZK:
First, we have to catch the exception thrown in an event listener by ZK error handling mechanism, configure
<error-page> in zk.xml.
zk.xml
<error-page>
<exception-type>org.springframework.security.access.AccessDeniedException</exception-type>
<location>/WEB-INF/errors/handleAccessDenied.zul</location>
</error-page>
Then, create the error handling page. To avoid users visiting the page directly, we put it under /WEB-INF. This
error handling page displays nothing but a page initiator to redirect an unauthenticated user to the login page.
handleAccessDenied.zul
<?init class="org.zkoss.reference.developer.spring.security.ui.error.AjaxAccessDeniedHandler"?>
<zk>
<!-- Forward a unauthenticated user to login page. -->
</zk>
The page initiator redirects the browser to login page if current user principle doesn't exist, otherwise, it displays
exception's detail in a custom page.
AjaxAccessDeniedHandler.java
Executions.createComponents("/WEB-INF/errors/displayAccessDeniedException.zul",
null, args);
}
}
}
Version History
Version Date Content
References
[1] https:/ / github. com/ zkoss/ zkbooks/ tree/ master/ developersreference/ integration. spring. security
Miscellenous 427
Miscellenous
In following sections, we will talk about other integration issues which are not covered and does not classify in
previous sections.
Google Analytics
To track the Ajax traffic with Google Analytics [1] or other statistic services, you have to override a client-side API:
zk.Event, zk.Desktop) zAu.beforeSend(_global_.String, zk.Event, zk.Desktop) [2]. This method will be called each
time ZK Client is about to send an Ajax request to the server. You could override it to record the requests on any
statistic service you prefer.
Here we use Google Analytics as an example to illustrate how to override it.
try {
var pageTracker = _gat._getTracker("UA-xxxx"); //whatever code your
website is assigned
pageTracker._setDomainName("zkoss.org");
pageTracker._initData();
pageTracker._trackPageview();
Of course, you could only record the information you are interested by examining Event.name [3].
Google Analytics 428
Version History
Version Date Content
References
[1] http:/ / www. google. com/ analytics/
[2] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ _global_/ zAu. html#beforeSend(_global_. String,
[3] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Event. html#name
Bridge [1]
Starting an execution in a foreign Ajax channel is straightforward: invoke javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse, org.zkoss.zk.ui.Desktop) Bridge.start(javax.servlet.ServletContext,
javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.zkoss.zk.ui.Desktop) [2]. Then,
you are allowed to access the components, post events and do anything you like. At the end, you invoke
Bridge.getResult() [3] to retrieve the JavaScript code snippet and send it back to the client to execute. Finally, you
invoke Bridge.close() [4] to close the execution.
Example
...
<h:commandButton id="save" value="Save"
onclick="jsf.ajax.request(this, event, {execute:'@all'}); return
false;" actionListener="${myBean.saveDetails}">
</h:commandButton>
Start Execution in Foreign Ajax Channel 429
...
@ManagedBean
@SessionScoped
public class MyBean {
ExternalContext ec =
FacesContext.getCurrentInstance().getExternalContext();
ServletContext svlctx = (ServletContext) ec.getContext();
HttpServletRequest request = (HttpServletRequest)
ec.getRequest();
HttpServletResponse response = (HttpServletResponse)
ec.getResponse();
Component comp = getComponent();
Bridge bridge = Bridge.start(svlctx, request,
response,comp.getDesktop());
try {
// update ZK component(s) state here
//comp.appendChild(new SomethingElse()); ...
FacesContext.getCurrentInstance().getPartialViewContext().getPartialResponseWriter();
responseWriter.startDocument();
responseWriter.startEval();
responseWriter.write(bridge.getResult());
responseWriter.endEval();
responseWriter.endDocument();
responseWriter.flush();
responseWriter.close();
} finally {
bridge.close();
}
}
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ embed/ Bridge. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ embed/ Bridge. html#start(javax. servlet. ServletContext,
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ embed/ Bridge. html#getResult()
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ embed/ Bridge. html#close()
[5] For more information on jsf.ajax.request (https:/ / javaserverfaces. dev. java. net/ nonav/ docs/ 2. 0/ jsdocs/ symbols/ jsf. ajax. html#.
request) read official JSF Javascript docs for jsf.ajax (https:/ / javaserverfaces. dev. java. net/ nonav/ docs/ 2. 0/ jsdocs/ symbols/ jsf. ajax.
html).
Version History
Version Date Content
5.0.5 September Bridge (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ embed/ Bridge. html#) was introduced to
2010 simplify the starting of an execution in foreign Ajax channel
Websocket Channel
Employment/Purpose
[Since ZK 8]
ZK has supported a way to share the application data between a ZK application and a websocket application within
the same session. Here we demonstrate how to use the Storage [1] in a desktop scope to share the application data
through the websocket channel.
Example
Websocket Server
@ServerEndpoint(value ="/echo/",
configurator = ZKWebSocket.class)
public class EchoServer {
@OnOpen
public void onOpen(Session session) {
}
@OnMessage
public void onMessage(String message, Session session){
Storage<Integer> storage = ZKWebSocket.getDesktopStorage(session);
if ("receive".equals(message)) {
Integer count = storage.getItem("count");
try {
session.getBasicRemote().sendText("Received..."
+ count);
} catch (Exception e) {
e.printStackTrace();
}
Websocket Channel 431
} else {
try {
storage.setItem("count",
Integer.parseInt(message));
session.getBasicRemote().sendText("Sent..." +
message);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
@OnClose
public void onClose(Session session){
}
As you can see above, in line 2, we have to register a ZKWebSocket [2] class into the configurator of the
ServerEndpoint annotation. And in line 10 we can use the method of
[3]
ZKWebSocket.getDesktopStorage(javax.websocket.Session) to receive the data storage from a websocket session
(the storage is a thread-safe implementation). Note that the websocket session must have a dtid value which is sent
from client as follows.
ZK Application
MVVM Example
<window id="win" apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm')
@init('org.zkoss.foo.ZKWebSocketViewModel')">
<groupbox title="ZK">
<hlayout>count: <label value="@load(vm.count)"/></hlayout>
<button label="add" onClick="@command('cmd')"/>
</groupbox>
</window>
@ToServerCommand("update")
public class ZKWebSocketViewModel {
@Init
public void init(@ContextParam(ContextType.DESKTOP) Desktop
desktop) {
Websocket Channel 432
count = 100;
syncToStorage(desktop);
}
@Command
@NotifyChange("count")
public void cmd(@ContextParam(ContextType.DESKTOP) Desktop
desktop) {
count++;
syncToStorage(desktop);
}
@Command("update")
@NotifyChange("count")
public void doUpdate(@ContextParam(ContextType.DESKTOP) Desktop
desktop) {
count = desktop.<Integer>getStorage().getItem("count");
}
As you can see above, in line 22 and 26, we can receive the data storage from the desktop object to share or update
the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa.
MVC Example
<window id="win" apply="org.zkoss.foo.ZKWebSocketComposer">
<groupbox title="ZK">
<hlayout>count: <label id="label" /></hlayout>
<button id="btn" label="add"/>
</groupbox>
</window>
label.setValue("100");
syncToStorage();
}
@Listen("onClick = #btn")
public void doClick() {
count++;
label.setValue(String.valueOf(count));
syncToStorage();
}
As you can see above, in line 21 and 26, we can receive the data storage from the desktop object to share or update
the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa.
Note: in line 24 Command [4] annotation has been added since the release of ZK 8.0.0, and it is used to receive a
notification from client to server. For more details, please take a look at the #Communication section.
Communication
MVVM Example
Here is the MVVM way to send a command from client to server.
MVC Example
Here is the MVC way to send a command from client to server.
MVVM Example
When a user triggers a command with some data from client to server, the data should be in a Map (or says Object)
type. For example,
As you can see above, the data will automatically be converted into a specific object type according to the method
declaration.
Note: developer can implement a custom Converter [5] and specify it into the ZK library properties [6].
Websocket Channel 435
MVC Example
When a user triggers a command with some data from client to server, the data should be in an array type in order.
For example,
...
public static class MyFoo {
private String foo;
public void setFoo(String foo) { this.foo = foo;}
public String getFoo() { return this.foo;}
}
@Command
public void update(MyFoo foo, MyBar bar) {
}
}
As you can see above, the data will automatically be converted into a specific object type according to the method
declaration.
Note: developer can implement a custom Converter [7] and specify it into the ZK library properties [8].
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ Storage. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ http/ ZKWebSocket. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ http/ ZKWebSocket. html#getDesktopStorage(javax. websocket. Session)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ annotation/ Command. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ bind/ Converter. html#
[6] http:/ / books. zkoss. org/ wiki/ ZK_Configuration_Reference/ zk. xml/ The_Library_Properties/ org. zkoss. bind.
jsonBindingParamConverter. class
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ util/ Converter. html#
[8] http:/ / books. zkoss. org/ wiki/ ZK_Configuration_Reference/ zk. xml/ The_Library_Properties/ org. zkoss. zk. ui.
jsonServiceParamConverter. class
Embed ZK Component in Foreign Framework 436
Note: if it is OK for your developers to work on ZUL directly. it is more convenient and powerful to use
the inclusion (such as <jsp:include>) or ZK JSP Tags [4], and you don't have to wrap them into a native
element.
Prerequisite
DOCTYPE
To use ZK components correctly, the pages generated by the foreign framework (JSP, JSF...) must generate the doc
type as follows.
Browser Cache
Though optional, it is suggested to disable the browser to cache the result page. It can be done as follows.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
(HttpServletResponse)pgctx.getResponse();
//create components
Listbox listbox = new Listbox();
listbox.appendChild(new Listitem("Item 1"));
listbox.appendChild(new Listitem("Item 2"));
Example
@FacesComponent(value = "window")
public class WindowTag extends UIComponentBase {
Embed ZK Component in Foreign Framework 438
try {
Renders.render(svlctx, request,response,
new GenericRichlet() {
public void service(Page page)
throws Exception {
window = new Window();
window.setPage(page);
applyProperties();
doAfterCompose();
}
}, null, responseWriter);
} catch (ServletException e) {
throw new IOException(e.getMessage());
}
}
}
}
/** apply composer by calling doAfterCompose after ZK component
is composed */
private void doAfterCompose() throws Exception {
Object o = getAttributes().get("apply");
if(o instanceof String) {
o = Classes.newInstanceByThread(o.toString());
}
if(o instanceof Composer) {
((Composer)o).doAfterCompose(window);
}
}
....
}
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ embed/ Renders. html#render(javax. servlet. ServletContext,
[2] http:/ / weblogs. java. net/ blog/ driscoll/ archive/ 2009/ 10/ 09/ jsf-2-custom-java-components-and-ajax-behaviors
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkplus/ embed/ Renders. html#
Performance Tips 440
Performance Tips
This chapter describes the tips to make your ZK application running faster. For information about identifying the
bottleneck, please refer to the Performance Monitoring section.
For MVVM performance tips, please refer to MVVM Reference [1]
References
[1] http:/ / books. zkoss. org/ zk-mvvm-book/ 8. 0/ advanced/ performance-tips. html
<window apply="foo.MyComposer">
//omitted
You can handle all events and components in foo.MyComposer. By the use of auto-wiring, it is straightforward to
handle events and components.
where doSomething() is interpreted as zscript. Thus, for better performance, they should be replaced too.
If you decide not to use zscript at all, you could turn on the disable-script configuration as follows, such that an
exception will be thrown if zscript is used.
<system-config>
<disable-zscript>true</disable-zscript>
</system-config>
Use Compiled Java Codes 441
<zscript deferred="true">
</zscript>
By specifying the deferred attribute, the zscript codes it contains will not be evaluated when ZK renders a page.
It means that the interpreter won't be loaded when ZK renders a page. This saves memory and speeds up page
rendering.
In the following example, the interpreter is loaded only when the button is clicked:
<window id="w">
<zscript deferred="true">
void addMore() {
new Label("More").setParent(w);
}
</zscript>
<button label="Add" onClick="addMore()"/>
</window>
<window onCreate="init()">
<window use="my.MyWindow">
package my;
public class MyWindow extends Window {
public void onCreate() { //to process the onCreate event
...
If you prefer to do the initialization right after the component (and all its children) is created, you can implement the
AfterCompose [2] interface as shown below. Note: the afterCompose method of the AfterCompose interface
is evaluated at the Component Creation phase, while the onCreate event is evaluated in the Event Processing
Phase.
package my;
public class MyWindow extends Window implements
org.zkoss.zk.ui.ext.AfterCompose {
public void afterCompose() { //to initialize the window
...
Use Compiled Java Codes 442
As suggested in the previous sections, the performance can be improved by not using zscript at all. Thus, you can
rewrite the above code snippet either with EventListener or by specifying the forward attribute as follows.
<window apply="foo.MyComposer">
<button label="OK" forward="onOK"/>
</window>
Version History
Version Date Content
References
[1] http:/ / books. zkoss. org/ zk-mvvm-book/ 8. 0/ index. html
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ ext/ AfterCompose. html#
Use Native Namespace instead of XHTML Namespace 443
<h:table xmlns:h="xhtml">
<h:tr>
<h:td>Name</h:td>
<h:td>
<textbox/>
</h:td>
</h:tr>
</h:table>
On the other hand, the following code snippet won't create components for any elements specified with the native
space (with prefix n:)[6].
<n:table xmlns:n="native">
<n:tr>
<n:td>Name</n:td>
<n:td>
<textbox/>
</n:td>
</n:tr>
</n:table>
Notice that table, tr and td are generated directly to the client, so they don't have no counterpart at the client
either. You cannot change their states dynamically. For example, the following code snippet is incorrect.
If you have to change them dynamically, you still have to use the XHTML component set, or you could use Html [7]
alternatively, if the HTML tags won't contain any ZUL component.
Notice that you could create the native components in Java too. For more information, please refer to the native
namespace section.
HtmlNativeComponent n =
new HtmlNativeComponent("table", "<tr><td>When:</td><td>", "</td></tr>");
n.setDynamicProperty("border", "1");
n.setDynamicProperty("width", "100%");
n.appendChild(new Datebox());
parent.appendChild(n);
Use Native Namespace instead of XHTML Namespace 444
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zhtml/ AbstractTag. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zhtml/ Table. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zhtml/ Tr. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Textbox. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zhtml/ Td. html#
[6] In fact, it will still create some components for the rerender purpose, such as AbstractTag (http:/ / www. zkoss. org/ javadoc/ latest/ zk/
org/ zkoss/ zhtml/ AbstractTag. html#). However, since they shall not be accessed, you could image they are not created at all.
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Html. html#
<div>
<custom-attributes org.zkoss.zk.ui.stub.native="false"/>
<n:table xmlns:n="native"> <!-- won't be stub-ized -->
...
[1] Non-native components could be stub-ized too by use of Table (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zhtml/ Table.
html#). For more information, please refer here.
Version History
Version Date Content
5.0.6 January, 2011 The attribute called org.zkoss.zk.ui.stub.native was introduced to disable the stub-ization.
Use ZK JSP Tags instead of ZK Filter 445
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<jsp:include page="foo.zul"/>
...
</body>
</head>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<z:zkhead />
</head>
<body>
<%-- any JSP content --%>
<z:page>
<table>
<tr>
<td>Name</td>
<td><z:textbox/></td>
</tr>
Use ZK JSP Tags instead of ZK Filter 446
</table>
</z:page>
</body>
</head>
where z:page declares a ZK page and then ZK tags can be used inside it.
The above example is equivalent to the following code snippet, if a ZUL page is used,
<!-- another.zul -->
<?page complete="true"?>
<n:html xmlns="http://www.w3.org/1999/xhtml" xmlns:n="http://www.zkoss.org/2005/zk/native">
<n:head>
</n:head>
<n:body>
<!-- any HTML content -->
<n:table>
<n:tr>
<n:td>Name</n:td>
<n:td><textbox/></n:td>
</n:tr>
</n:table>
where <?page complete="true"?> declares that this page is a complete page, i.e., it will provide HTML's
html, head and body tags as shown above.
Version History
Version Date Content
Defer the Creation of Child Components 447
<tabbox>
<tabs>
<tab label="Preload" selected="true"/>
<tab id="tab2" label="OnDemand"/>
</tabs>
<tabpanels>
<tabpanel>
This panel is pre-loaded since no fulfill specified
</tabpanel>
<tabpanel fulfill="self.linkedTab.onSelect">
This panel is loaded only tab2 receives the onSelect
event
</tabpanel>
</tabpanels>
</tabbox>
Version History
Version Date Content
Defer the Rendering of Client Widgets 448
In addition to Defer the Creation of Child Components, you can defer the rendering of the widgets at the client by
the use of the renderdefer attribute. It is a technique to make a sophisticated page to appear earlier.
For example, we can defer the rendering of the inner window for 100 milliseconds as shown below
Unlike the fulfill attribute, the components on the server and the widgets at the client are created no matter
renderdefer is specified. It only defers the rendering of the widgets into DOM elements.
Here is another example to use it with pure Java.
The render-defer technique is useful to improve the response time of showing a sophisticated page in a slow client.
The total time required to render is not reduced (since all widgets have to render later), but it allow the page to show
up sooner and it makes the user feel more responsive.
<style>
.always-scroll .z-grid-body {
</style>
<columns>
<column label="renderdefer (scrollbar col / odd row style missing) hflex col" hflex="1"></column>
</columns>
<rows>
</rows>
</grid>
When you put "renderdefer" into row or rows without pre-defining to forcibly show the vertical-scrollbar, ZK treats
it as the "zeroth" rows at first while hflex uses the total width of the grid without subtracting the width of the
scrollbar. After "renderdefer" completes, we will find that the horizontal scroll bar has also appeared. However, this
shouldn't be the case, only the y-scrollbar should have appeared. The solution to solve this is to force the
vertical-scrollbar to show through CSS to prevent this from happening.
We recommend that you should defer the component by putting the "renderdefer" property on the component tag
instead of putting it on children components (e.g. grid, columns, rows).
References
[1] http:/ / tracker. zkoss. org/ browse/ ZK-2336
With Enterprise Edition, widgets[1] will delay the rendering of DOM elements until really required. For example, the
DOM elements of comboitem won't be created until the drop down is shown up. It improved the performance a
lot for a sophisticated user interface.
This feature is transparent to the application developers. All widgets are still instantiated (though DOM elements
might not), so they can be accessed without knowing if this feature is turned on.
[1] A widget is the (JavaScript) object running at the client to represent a component
<treechildren>
<treeitem forEach="${each.fine}" open="false">
<treerow>
<treecell label="${each.name}"/>
<treecell label="${each.description}"/>
</treerow>
</treeitem>
</treechildren>
</treeitem>
</treechildren>
</treeitem>
<library-property>
<name>org.zkoss.zul.client.rod</name>
<value>false</value>
</library-property>
Or, if you prefer to disable it for a particular page, then specify false to a page's attribute called
org.zkoss.zul.client.rod rod, such as
Or, if you prefer to disable it for all descendants of a particular component, then specify false to a component's
attribute. And, you can enable it for a subset of the descendants. For example,
<window>
<div>
..
</div>
</window>
Version History
Version Date Content
For more information of using and implementing a model, please refer to the Model section and ZK Component
Reference: Listbox.
Version History
Version Date Content
Turn on Render on Demand 452
With ZK EE, you can enable Render on Demand for Grid and Listbox to boost performance to show huge amount
of data. Grid and Listbox will load only the necessary data chunk from associated ListModel, render required
Row(s)/Listitem(s) on the server, then create only the required corresponding widgets and render the DOM elements
really needed in browser. It improves the performance and saves memory significantly on both the server and
browser sides.
Note: ROD actually brings performance boost on both the client side and server side. However, if you use
forEach to populate Rows or Listitems, the components will be all in memory, which does not give you any
performance benefits on server side. (The client side still enjoys a boost.) If you want to fully leverage the power of
ROD, the use of model is recommended.
ROD: Grid
If you want to enable Grid ROD for the whole application, you can specify a library property called grid (http:/ /
www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/grid.html#) rod with true. For example, specify the following
in zk.xml:
<library-property>
<name>org.zkoss.zul.grid.rod</name>
<value>true</value>
</library-property>
Or, if you prefer to enable it for a particular page, then specify true to a page's attribute called grid (http:/ / www.
zkoss.org/javadoc/latest/zk/org/zkoss/zul/grid.html#) rod, such as
Or, if you prefer to enable it for all descendant grids of a particular component, then specify true to the
component's attribute. You can enable it for a subset of the descendant grids. For example,
<window>
<grid ...>
..
</grid>
<div>
<grid ...>
..
</grid>
..
</div>
</window>
Note that Grid ROD is will not work unless the Grid is configured with a limited view port; i.e. you have to set
height or vflex attribute of the Grid or set the Grid to paging mold so the user can see only a portion of the
Turn on Render on Demand 453
Specifies the minimum number of rows rendered on the client. It is only considered if Grid is using live data (
Grid.setModel(ListModel) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Grid.
html#setModel(ListModel))) and not using paging mold ( Grid.getPagingChild() (http:/ / www. zkoss. org/ javadoc/
latest/zk/org/zkoss/zul/Grid.html#getPagingChild())).
<custom-attributes org.zkoss.zul.grid.initRodSize="30"/>
ROD: Listbox
If you want to enable Listbox ROD for the whole application, you can specify a library property called listbox (http:/
/ www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ listbox. html#) rod with true. For example, specify the
following in zk.xml:
<library-property>
<name>org.zkoss.zul.listbox.rod</name>
<value>true</value>
</library-property>
Or, if you prefer to enable it for a particular page, then specify true to a page's attribute called listbox (http:/ /
www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/listbox.html#) rod, such as
Or, if you prefer to enable it for all descendant listboxs of a particular component, then specify true to the
component's attribute. And, you can enable it for a subset of the descendant listboxs. For example,
<window>
<listbox ...>
..
</listbox>
<div>
<listbox ...>
..
</listbox>
..
</div>
</window>
Note that Listbox ROD is not working unless the Listbox is configured with a limited view port; i.e. you have to set
height, vflex, or rows attribute of the Listbox or set the Listbox to paging mold so the user sees only a
portion of the content of the Listbox.
Turn on Render on Demand 454
Specifies the number of items rendered when the Listbox first render. It is used only if live data (
Listbox.setModel(ListModel) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Listbox.
html#setModel(ListModel))) and not paging ( Listbox.getPagingChild() (http:/ / www. zkoss. org/ javadoc/ latest/
zk/org/zkoss/zul/Listbox.html#getPagingChild())).
<custom-attributes org.zkoss.zul.listbox.initRodSize="30"/>
Version History
Version Date Content
LIMIT 100
//if _ascending, ORDER BY _orderBy ASC
//if _descending, ORDER BY _orderBy DSC
}
return _cache[index - _beginOffset];
}
@Override
public void sort(Comparator cmpr, boolean ascending) {
_cache = null; //purge cache
_size = -1; //so size will be reloaded
_descending = !(_ascending = ascending);
_orderBy = ((FieldComparator)cmpr).getRawOrderBy();
_sorting = cmpr;
//Here we assume sort="auto(fieldName)" is specified in
ZUML, so cmpr is FieldComparator
//On other hand, if you specifies your own comparator,
such as sortAscending="${mycmpr}",
//then, cmpr will be the comparator you assigned
fireEvent(ListDataEvent.CONTENTS_CHANGED, -1, -1);
}
@Override
public String getSortDirection(Comparator cmpr) {
if (Objects.equals(_sorting, cmpr))
return _ascending ? "ascending" : "descending";
return "natural";
}
}
The implementation of boolean) Sortable.sort(java.util.Comparator, boolean) [3] generally has to purge the cache,
store the sorting direction and field, and then fire ListDataEvent.CONTENTS_CHANGED [2] to reload the content.
The field to sort against has to be retrieved from the given comparator. If you specify "auto(fieldName)" to
Listheader.setSort(java.lang.String) [3], then the comparator is an instance of FieldComparator [17], and you could
retrieve the field's name from FieldComparator.getRawOrderBy() [21].
If you'd like to use your own comparator, you have to carry the information in it and then retrieve it back when
boolean) Sortable.sort(java.util.Comparator, boolean) [3] is called.
Also notice that we cache the size to improve the performance, since ListModel.getSize() [4] might be called multiple
times.
Here is some pseudo for a custom TreeModel which renders an Apache Commons VFS FileObject to be able to
browse a filesystem:
@Override
public FileObject getChild(FileObject parent, int index) {
Implement ListModel and TreeModel 456
@Override
public int getChildCount(FileObject node) {
int childCount = 0;
try {
FileType type = node.getType();
if( type == FileType.FOLDER ){
childCount = node.getChildren().length;
}
} catch (FileSystemException e) {
throw new IllegalArgumentException(e);
}
return childCount;
}
@Override
public boolean isLeaf(FileObject node) {
boolean isLeaf = false;
try {
FileType type = node.getType();
isLeaf = (type == FileType.FILE );
} catch (FileSystemException e) {
throw new IllegalArgumentException(e);
}
return isLeaf;
}
/*
* Return the sibling index of each node in walk down from the
root.
*/
@Override
public int[] getPath(FileObject node) {
List<Integer> paths = new ArrayList<Integer>();
try {
// walk upwards to root getting sibling index of each
child in each parent
FileObject parent = node.getParent();
while (parent != null &&
Implement ListModel and TreeModel 457
parent.getType().equals(FileType.FOLDER)) {
FileObject[] children = parent.getChildren();
for( int index = 0; index < children.length; index++){
FileObject c = children[index];
if( node.equals(c)){
paths.add(index);
break;
}
}
node = parent;
parent = node.getParent();
}
} catch (FileSystemException e) {
throw new IllegalArgumentException(e);
}
int[] p = new int[paths.size()];
for( int index = 0; index < paths.size(); index++){
p[index] = paths.get(p.length - 1 - index); //
reverse
}
return p;
}
}
When many treerows are open and further rows are expanded the re-rendering of the tree may visit many of the open
rows. It is therefore recommended that you cache the results of any expensive calls where possible with a suitable
eviction strategy.
For a real example, please refer to Small Talk: Handling sortable huge data using ZK and/or Small Talk: Handling
huge data using ZK.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ ListModelList. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ event/ ListDataEvent. html#CONTENTS_CHANGED
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Listheader. html#setSort(java. lang. String)
Minimize Number of JavaScript Files to Load 458
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ 947199ea/ js/ zk. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ 947199ea/ js/ zul. lang. wpd
* http:/ / www. zkoss. org/ zksandbox/ macros/ category. js
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zkmax. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. wgt. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. utl. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. layout. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. wnd. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. tab. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. inp. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. box. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. sel. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zk. fmt. wpd
* http:/ / www. zkoss. org/ zksandbox/ zkau/ web/ _zv2010062914/ js/ zul. mesh. wpd
This means that the browser will trigger 15 requests to load the 15 JavaScript files. Even if each file is not too big, it
still takes more time to connect to the server and download it. However, we can specify a DSP file to include several
JavaScript into one and declare it at the top of the index.zul.
For example, /macros/zksandbox.js.dsp
<c:include page="~./js/zul.layout.wpd"/>
<c:include page="~./js/zul.wnd.wpd"/>
<c:include page="~./js/zul.tab.wpd"/>
<c:include page="~./js/zul.inp.wpd"/>
<c:include page="~./js/zul.box.wpd"/>
<c:include page="~./js/zul.sel.wpd"/>
<c:include page="/macros/category.js"/>
Note:
1. The included JavaScript files have their own sequence, so you cannot place them in randomly.
2. The zk.wpd is a ZK core JavaScript file hence you don't need to include it
3. The zul.lang.wpd is an I18N message, so you don't need to include it.
4. In ZK 5.0.4 we introduced a new feature(#System-wide_Minimizing_the_Number_of_JavaScript_Files).
However, since the release of this new feature the packages zul, zul.wgt, and zkmax will be merged
automatically into the ZK package, so you don't specify them in the the zksandbox.js.dsp file.
5. int) DspFns.setCacheControl(java.lang.String, int) [3] is used to set the Cache-Control and Expires headers to 24
hours, so the JavaScript file will be cached for a day.
index.zul
If a package is used by all your pages, you could configure it system wide by specifying the packages in the
language add-on. Please refer to ZK Configuration Reference/zk.xml/The language-config Element for how to
specify a language add-on.
For example, if the zul.wnd package (Window [4]) is required for all pages, then you could add the following to the
language add-on.
Notice that you have to specify the merge attribute which indicates that the JavaScript code of the package will be
loaded with the zk package. In other words, the ~./js/zk.wpd will contain all the packages specified with the
merge attribute.
Also notice that if you use several DSP/JSP file to load multiple packages in a file as described in the previous
section, you generally don't specify them here. Otherwise, you will load the same package twice (though it is safe, it
wastes time).
Note: if you merge several JavaScript file into your own lang-addon.xml, but some JavaScript files need to be
counted on zul.lang.wpd, such as the package zul.inp, thus, you can't include this package into your
lang-addon.xml.(it will be fixed in ZK 5.0.5+)
Minimize Number of JavaScript Files to Load 460
Notice that all packages are default to load-on-demand, you rarely need to specify the ondemand attribute, unless
you want to undo the package that has been specified with the merge attribute.
Version History
Version Date Content
References
[1] http:/ / zk1. svn. sourceforge. net/ viewvc/ zk1/ releases/ 5. 0. 7/ zksandbox/ src/ archive/ index. zul?view=log
[2] http:/ / www. zkoss. org/ zksandbox
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ fn/ DspFns. html#setCacheControl(java. lang. String,
[4] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zul/ wnd/ Window. html#
*Notice : the ZK static resource server is a simple server which deploy official ZK library, not your whole
application.
Load JavaScript and CSS from Server Nearby 461
How to
1. Implement the URLEncoder [1]
2. Add library-property configuration to the zk.xml
Document : ZK Configuration Reference/zk.xml/The Library
Properties/org.zkoss.web.servlet.http.URLEncoder.
3. Host ZK static resouce server
Following is a sample :
Configuration
<library-property>
<name>org.zkoss.web.servlet.http.URLEncoder</name>
<value>org.zkoss.test.TestEncoder</value> <!-- Where the Implementation Class is -->
</library-property>
Implementation
package org.zkoss.test;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.zkoss.web.servlet.http.Encodes.URLEncoder;
public class TestEncoder implements URLEncoder {
@Override
public String encodeURL(ServletContext ctx, ServletRequest
request, ServletResponse response,
String uri, URLEncoder defaultEncoder) throws Exception {
if (isStaticResource(uri)) {
return getResourceHost() + uri.replace("~./", "");
} else {
return defaultEncoder.encodeURL(ctx, request,
response, uri, defaultEncoder);
}
}
/**
* file .wcs : CSS File
* file .wpd : Javscript File
*/
private boolean isStaticResource(String url) {
// zul.lang.wpd should not be a static resource
return url.startsWith("~./") &&
!url.endsWith("zul.lang.wpd") && (url.endsWith(".wpd") ||
url.endsWith(".wcs"));
}
/**
* Detect where the ip is/ who is login / what kind of resouce
Load JavaScript and CSS from Server Nearby 462
server will
*
* @return the host name include protocol prefix. (Client will
retrieve resource from it)
*/
private String getResourceHost() {
return "http://SomeWhereNearbyMe/DefaultContext/zkau/web/";
}
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ servlet/ http/ Encodes/ URLEncoder. html#
Specify Stubonly for Client-only Components 463
It is common that the states of some components are not required to maintain on the server. A typical example is that
an application might use some components, such as hbox, for layout and won't access it again after rendered. To
minimize the memory footprint, ZK supports a special property called stubonly
[1]
(Component.setStubonly(java.lang.String) ). Once specified with true, its states won't be maintained on the
server (and all states are maintained at the client). For example,
<hbox stubonly="true">
</hbox>
• Notice this feature is available since ZK 5.0.4 EE, and available in CE since ZK 6.0.0.
<hbox stubonly="true">
a stub-only label
<textbox stubonly="false"/>
<splitter/>
<listbox>
<listitem label="also stubonly"/>
</listbox>
</hbox>
Invalidation
While a stub component cannot be invalidated directly, it is safe to invalidate its parent. ZK will rerender all
non-stub components and retain the states of stub components at the client. For example, in the following snippet, it
is safe to click the invalidate button. From an end user's point of view, there is no difference whether
stubonly is specified or not.
<window>
<button label="self.parent.invalidate()"/>
<vbox stubonly="true">
Specify Stubonly for Client-only Components 464
stubonly <textbox/>
</vbox>
</window>
It is a special case that paging can not apply stubonly at the same time. For example,
Although paging will invalidate listbox and its children, stubonly needs the referred widget in client side
which is detached during paging and throws mounting error.
In the above example[3], win is reused but it also has a stub component (a label). When the window is closed, all
the information at the client are removed (since Window's onClose() method detaches the window). Thus, if the
event was executed again, the client can restore the detailed information back.
Specify Stubonly for Client-only Components 465
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#setStubonly(java. lang. String)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ StubComponent. html#
[3] Please refer to ZK-1094 (http:/ / tracker. zkoss. org/ browse/ ZK-1094) for a real case.
Event Handling
ZK will preserve all registered event listeners and handlers, when converting a stub-only component to a stub
component. In other words, the listener will be called if the corresponding event is fired. However, since the original
component no longer exists, the event is fired in the most generic format: an instance of Event (http:/ / www. zkoss.
org/javadoc/latest/zk/org/zkoss/zk/ui/event/Event.html#), rather than a derived class.
For example, in the following snippet, "org.zkoss.zk.ui.event.StubEvent:onStub" will be generated to
System.out.
<textbox stubonly="true" onChange='System.out.println(event.getClass().getName()+":"+event.getName())'/>
In addition, the target ( Event.getTarget() (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ event/
Event.html#getTarget())) is the stub component rather than the original one (text).
Client-side Programming
The client-side widget of a component is the same no matter if it is stub only. Thus, the application can have the full
control by registering the client side event listener, such as
In other words, the stub-only components behave the same at the client.
Refer to Client Side Programming and ZK Client-side Reference: General Control for more information.
Version History
Version Date Content
6.0.2 June, Bug: stubonly doesn't work (http:/ / tracker. zkoss. org/ browse/ ZK-1182), and change the event handle from origin event to
2012 StubEvent.
Reuse Desktops 466
Reuse Desktops
[Since 5.0.0]
By default, a desktop is purged when the user browses to another URI or refreshes the page. Thus, the user can have
the most updated information. However, if a page takes too long to generate, you can provide a plugin so-called
desktop recycle.
First, you implement the DesktopRecycle [1] interface to cache and reuse the desktops which are supposedly being
removed. Second, specify the class in WEB-INF/zk.xml. For example, let us assume the class you implement is
called foo.MyRecycle, then add the following to zk.xml
<listener>
<listener-class>foo.MyRecycle</listener-class>
</listener>
org.zkoss.zkmax.ui.util.DesktopRecycle
[Enterprise Edition]
[Since 5.0.0]
ZK provides a default implementation, the DesktopRecycle [2] class, to simplify the use. You can use it directly or
extends from it. By default, it caches all desktops for all URI. You can extend it to limit to certain paths by
overriding the shallRecycle method, or not to use desktops older than particular time by overriding the
shallReuse method.
For example, we can limit the URL to cache to "/long-op/*", and re-generate the page if it has been served for
more than 5 minutes.
It is straightforward to implement the DesktopRecycle [1] interface from scratch, if you prefer. The basic idea is to
cache the desktop when the beforeRemove method is invoked, and to reuse the cached desktop when the
beforeService method is called.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopRecycle. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ ui/ util/ DesktopRecycle. html#
Miscellaneous
Button: use the os mold if there are a lot of buttons
The trendy mold of a button provides a better and consistent look for, especially, Internet Explorer 6.
Unfortunately, the browser (particularly, Internet Explorer) will be slowed down if there are a lot of button (with
trendy) in the same page.
Notice that the default mold is os in ZK 5, while trendy in ZK 3.6. Since ZK 7.0.0, there is no difference between os and trendy.
<library-property>
<name>org.zkoss.zul.Button.mold</name>
<value>trendy</value>
</library-property>
<desktop-config>
<file-check-period>600</file-check-period><!-- unit: seconds -->
</desktop-config>
Miscellaneous 468
Version History
Version Date Content
Security Tips
This chapter describes how to make ZK applications secure. ZK is designed to provide enterprise-grade security.
However, there are still several discussions worth to take a look. You can also refer to a detail report here (PDF).
Cross-site scripting
Overview
Cross-site scripting [1] (XSS) is a type of computer security vulnerability typically found in web applications that
enables malicious attackers to inject client-side script into web pages viewed by other users. Because HTML
documents have a flat, serial structure that mixes control statements, formatting, and the actual content, any
non-validated user-supplied data included in the resulting page without proper HTML encoding may lead to markup
injection.
To prevent from XSS attack, ZK component encodes any value that might be input by an user, such as the value of
label and textbox, by escaping & and other unsafe characters. For example, the following statement is totally safe no
matter what the value of any_value might be:
<textbox value="${any_value}"/>
However, there are still some notes worth to pay attention to.
<html>${any_content}</html>
Client-side Actions
The client-side action is not encoded and the options is interpreted as a JSON object. Thus, you could encode it by
yourself, if you allow the end user to specify it (which is generally not suggested at all).
Version History
Version Date Content
References
[1] http:/ / en. wikipedia. org/ wiki/ Cross-site_scripting
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Comboitem. html#setContent(java. lang. String)
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Clients. html#evalJavaScript(java. lang. String)
Block Request for Inaccessible Widgets 470
<button unless="${accessible}"/>
<button visible="${accessible}"/>
If you want to block a request for inaccessible widgets for the whole application or for a particular desktop, you can
implement the org.zkoss.zk.au.AuService interface to filter out unwanted requests. ZK Enterprise Edition
has provided a simple implementation called InaccessibleWidgetBlockService [1]. To apply it to the whole
application, just specify the following in WEB-INF/zk.xml as follows.
<listener>
<listener-class>org.zkoss.zkmax.au.InaccessibleWidgetBlockService$DesktopInit</listener-class>
</listener>
<library-property>
<name>org.zkoss.zkmax.au.IWBS.events</name>
<value>onClick,onChange,onSelect</value>
</library-property>
Supported Components
All invisible components would be blocked. Some components would be blocked when they are disabled/readonly,
as follow.
Component
Button
Listbox
Menuitem
Navitem
Textbox
Tree
Intbox
Spinner
Doublebox
Decimalbox
Longbox
Doublespinner
Timepicker
Timebox
Checkbox
Datebox
Combobox
Chosenbox
Selectbox
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zkmax/ au/ InaccessibleWidgetBlockService. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ IWBS/ events. html#
Denial Of Service 472
Denial Of Service
Overview
From OWASP [1]
The Denial of Service (DoS) attack is focused on making unavailable a resource (site, application,
server) for the purpose it was designed. There are many ways to make a service unavailable for
legitimate users by manipulating network packets, programming, logical, or resources handling
vulnerabilities, among others. If a service receives a very large number of requests, it may stop
providing service to legitimate users. Denial-of-service attacks significantly degrade service quality
experienced by legitimate users. It introduces large response delays, excessive losses, and service
interruptions, resulting in direct impact on availability.
While OWSAP recommends[2] a few techniques developers can employ against DoS at application level, ZK as a
framework provides two features to protect against DoS as described below
Notes
[1] https:/ / www. owasp. org/ index. php/ Denial_of_Service
[2] https:/ / www. owasp. org/ index. php/ Denial_of_Service
<session-config>
<max-desktops-per-session>a_number</max-desktops-per-session>
</session-config>
<session-config>
<max-requests-per-session>a_number</max-requests -per-session>
</session-config>
Note : A negative number means no limitation at all, but it is not recommended due to the possibility of the
denial-of-service (DoS) attacks
Denial Of Service 473
Version History
Version Date Content
By design ZK is an Ajax solution. Because of this design generally no form submit nor specific URL request can
cause side effects.
3. The attacker must determine the right values for all the form's or URL's inputs; if any of them are
required to be secret authentication values or IDs that the attacker can't guess, the attack will fail.
ZK generates unique ids for html elements that represent ZK components on client side and these unique ids are
checked on server side when data containing them is passed via ZK's Ajax mechanism. For successful CSRF attack,
the attacker will have to guess all unique ids for those html elements while submitting the malicious request. If the
html element ids are not the same as they were when page rendered then the data is considered invalid by ZK and
request is rejected at server side automatically.
Also note that ZK will regenerate these ids if the components are re-rendered via page refresh or component are
re-created again.
4. The attacker must lure the victim to a Web page with malicious code while the victim is logged into
the target site.
This is more of a humane issue and depends on the end user. Application developers should raise the awareness
about CSRF by documenting this in their application documentation which end users can refer to.
Version History
Version Date Content
References
[1] https:/ / www. owasp. org/ index. php/ Cross-Site_Request_Forgery_(CSRF)
[2] https:/ / en. wikipedia. org/ wiki/ Cross-site_request_forgery#Limitations
[3] http:/ / books. zkoss. org/ wiki/ ZK_Developer's_Guide/ Fundamental_ZK/ Basic_Concepts/ Page_and_Desktop
References
[1] The Open Web Application Security Project official website (https:/ / www. owasp. org/ index. php/ Main_Page)
[2] The OSWAP Top 10 (https:/ / www. owasp. org/ index. php/ Top_10-2017_Top_10)
[3] https:/ / www. owasp. org/ index. php/ Top_10-2017_A1-Injection
[4] https:/ / www. owasp. org/ index. php/ Top_10-2017_A2-Broken_Authentication
[5] https:/ / www. owasp. org/ index. php/ Top_10-2017_A3-Sensitive_Data_Exposure
[6] https:/ / www. owasp. org/ index. php/ Top_10-2017_A4-XML_External_Entities_(XXE)
[7] https:/ / www. owasp. org/ index. php/ Top_10-2017_A5-Broken_Access_Control
[8] https:/ / www. owasp. org/ index. php/ Top_10-2017_A6-Security_Misconfiguration
[9] https:/ / www. owasp. org/ index. php/ Top_10-2017_A7-Cross-Site_Scripting_(XSS)
[10] http:/ / books. zkoss. org/ wiki/ ZK_Developer%27s_Reference/ Security_Tips/ Cross-site_scripting
[11] https:/ / www. owasp. org/ index. php/ Top_10-2017_A8-Insecure_Deserialization
[12] https:/ / www. owasp. org/ index. php/ Top_10-2017_A9-Using_Components_with_Known_Vulnerabilities
[13] https:/ / www. owasp. org/ index. php/ Top_10-2017_A10-Insufficient_Logging%26Monitoring
Performance Monitoring
To improve the performance of an Ajax application, it is better to monitor the performance for identifying the
bottleneck. Depending on the information you'd like to know, there are a few approaches.
• PerformanceMeter [1]: Monitoring the performance from the network speed, server-processing time and the
client-rendering time.
• EventInterceptor [2]: Monitoring the performance of each event listener.
• Monitor [3]: Monitoring the number of desktops, sessions and other system load.
• There are a lot of performance monitor tools, such as VisualVM [4] and JProfiler [5]. They could provide more
insightful view of your application.
For sample implementations, you might take a look at the following articles:
• Performance Monitoring of ZK Applicaiton
• A ZK Performance Monitor
• Real-time Performance Monitoring of Ajax Event Handlers
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PerformanceMeter. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ EventInterceptor. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Monitor. html#
[4] http:/ / visualvm. dev. java. net/
[5] http:/ / www. ej-technologies. com/ products/ jprofiler/ overview. html
Performance Meters 478
Performance Meters
PerformanceMeter [1] is a collection of callbacks that the implementation could know when a request is sent, arrives
or is is processed.
<zk>
<listener>
<listener-class>foo.MyMeter</listener-class>
</listener>
</zk>
Performance Meters 479
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PerformanceMeter. html#requestStartAtClient
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PerformanceMeter. html#requestStartAtServer(java. lang. String,
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PerformanceMeter. html#requestCompleteAtServer(java. lang. String,
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PerformanceMeter. html#requestReceiveAtClient(java. lang. String,
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ PerformanceMeter. html#requestCompleteAtClient(java. lang. String,
Event Interceptors
Though EventInterceptor [2] is designed to allow developer to intercept how an event is processed, you could use it
as callback to know how long it takes to process an event. The event processing time can be calculated by
subtracting the time between EventInterceptor.beforeProcessEvent(org.zkoss.zk.ui.event.Event) [1] and
EventInterceptor.afterProcessEvent(org.zkoss.zk.ui.event.Event) [2]
Once implemented, you could register it by specifying the following in WEB-INF/zk.xml (assume the class is
called foo.MyEventMeter):
<zk>
<listener>
<listener-class>foo.MyEventMeter</listener-class>
</listener>
</zk>
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ EventInterceptor. html#beforeProcessEvent(org. zkoss. zk. ui. event.
Event)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ EventInterceptor. html#afterProcessEvent(org. zkoss. zk. ui. event. Event)
Loading Monitors 480
Loading Monitors
[3]
To know the loading of an application, you could implement Monitor to count the number of desktops, sessions
and requests.
Once implemented, you could register it by specifying the following in WEB-INF/zk.xml (assume the class is
called foo.MyStatistic):
<zk>
<listener>
<listener-class>foo.MyStatistic</listener-class>
</listener>
</zk>
Version History
Version Date Content
Step by Step Trouble Shooting 481
This brilliant visualization of the JS execution flow and stack depth can be used / interpreted in many ways to extract
the information you require.
Step by Step Trouble Shooting 485
Performance Debugging/Logging/Tracing
Feeling Lucky
Sometimes real profiling might be overkill or you just want to make a few a quick probing tests. Then a simple way
to find a long running method is to launch your server in debug mode without any break point. Then trigger the slow
operation. Right in the middle of the operation, "Suspend" the execution (yes you can suspend manually without
defining a break point), in your IDE. You'll have several suspended threads. Just examine their call stacks, and
usually the longest stack is the one you are interested in. Then go down in the stack to search for the bottleneck
(Chances are high, that you'll suspend the execution during the actual call causing the bottleneck - either it takes a
long time to execute, or it is called very very often).
In eclipse a very obvious case looks might look like this (just imaginarily replace Thread.sleep() with db.query(),
url.openConnection(), webService.get() ...):
No so lucky
In other cases the bottleneck will not be as outstanding as in the above image, but still the call stack can be a good
source to start from. e.g. outputting some counters and tracing information to the log.
Putting extra tracing logs will not always be possible and also takes implementation, building, deployment time.
So if this is not quickly possible it is better to do some performance sampling or profiling.
• Sampling: analyzes the call stacks of all threads at given intervals and summarizes statistics about which methods
are active most of the time
→ sufficient in most cases, will find the biggest bottlenecks at a high chance
• Profiling: gives exact more detailed timing, but this is at a cost, you should already know what you are looking for
before starting the profiler
→ profiling everything might just "kill" the application
There are several Profiling tools in various price ranges and there is JVisualVM (included in the JDK) so it should be
available everywhere.
This is a nice tutorial about how to get started with JVisualVM : Profiling with VisualVM - Part 1 [8] and Part 2 [9]
Sampling example using JVisualVM
here a small example showing 2 different kinds of time performance issues (a busy loop, and a suspended thread
(sleeping)):
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
lineChart.setType("line");
lineChart.setModel(chartModel());
builder.toString().getBytes(Charset.forName("utf-8"));
}
lineChart.setEngine(new LineChartEngine());
}
return model;
Step by Step Trouble Shooting 489
When executing this slow page, it will delay the page rendering by ~8 seconds, where 3 seconds will be spent in a
busy loop (causing a peak on the CPU) doing some useless string building, and 5 seconds you wouldn't notice on
your CPU, as the thread is just sleeping.
Starting the sampler will show the actual "Hot Spots" like this.
The 2 slow methods appear, then just take a snapshot, and view the details about the actual call strack in the
combined view, and filter by the method name.
In the call tree we actually see what is happening inside in more detail:
• the "chartModel()"-method is sleeping for 5 seconds
Step by Step Trouble Shooting 490
• and the "doAfterCompose()"-method is performing time consuming String operations for roughly 3 seconds
→ Performance issue located!
Memory Issue
Memory issues come in many different flavors:
The most common ones are explained here http://plumbr.eu/blog/understanding-java-lang-outofmemoryerror.
Ensure your physical memory, and the memory assigned to the JVM are sufficient.
Memory Leak
When running out of heap space, you'll either have a good explanation for it i.e. know that you allocate big lumps of
data (and how often) or you might have a memory leak, indicated by your constantly increasing memory, and not
being released again, after a Garbage Collection.
Here a very simple case using JVisualVM. Take this Composer code snippet which allocates a lot of memory.
If we didn't know where to look in the code we can use a Heap Dump to locate it.
I clicked the "find" button to find the 20 biggest objects on the heap. The results tell us the following.
• the 100MB are a big byte-array
Step by Step Trouble Shooting 491
Monitoring ZK
To check if you have a desktop/session leak, you can add the Statistic-listener to your application, and read its status
in a simple monitoring page (keep in mind, that this page will consume an additional desktop).
Add a listener to zk.xml
<listener>
<listener-class>org.zkoss.zk.ui.util.Statistic</listener-class>
</listener>
<zk>
<label multiline="true">
Active Desktops:
${desktop.webApp.configuration.monitor.activeDesktopCount}
Active Sessions:
${desktop.webApp.configuration.monitor.activeSessionCount}
Total Desktops:
${desktop.webApp.configuration.monitor.totalDesktopCount}
Total Sessions:
${desktop.webApp.configuration.monitor.totalSessionCount}
</label>
</zk>
related documentation
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_listener_Element/The_org.zkoss.
zk.ui.util.Monitor_interface
• http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/util/Statistic.html
If you see more Sessions than you expect, your session/desktop timeout might be too long, or too many desktops
give you a hint that the desktop cleanup process is not functioning properly, also [reusing desktops [14]] can help.
ZK Server Configuration
• check debug mode → zk config should not be enabled (disabled by default)
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_client-config_Element/
The_debug-js_Element
• check caching config → should not be disabled (enabled by default)
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.
web.classWebResource.cache
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.
zk.WPD.cache
Step by Step Trouble Shooting 493
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.
zk.WCS.cache
• check compression settings → should not be disabled (enabled by default)
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/web.xml/
ZK_AU_Engine#The_Initial_Parameters
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/web.xml/ZK_Loader#The_Initial_Parameters
• consider/check render on demand settings
• http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/Performance_Tips/
Client_Render_on_Demand
• http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/Performance_Tips/
Listbox,_Grid_and_Tree_for_Huge_Data/Turn_on_Render_on_Demand
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.
zul.client.rod
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.
zul.grid.initRodSize
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.
zul.listbox.initRodSize
• http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.
zul.tree.initRodSize (ZK 7)
• check http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/Performance_Tips
Network Issue
If something in your network infrastructure (routers, proxies, web servers...) is causing the performance issues, there
is little you can do as a web application developer.
Here some ideas to identify possible bottlenecks trying to reduce network complexity by:
• using ip addresses directly to circumvent DNS look-ups → speedup might indicate a problem with your DNS
server
• avoiding proxies, routers, firewalls (e.g. access the application server from a browser on a remote desktop
"closer" to the actual server)
• accessing the application server directly, instead of going through a webserver or load balancer
• disabling SSL and check difference
→ "Kindly" inform your network administrator about your observations and ask for help identifying, excluding,
fixing these infrastructure problems.
Step by Step Trouble Shooting 494
Version History
Version Date Content
References
[1] https:/ / developer. chrome. com/ devtools/ docs/ network#resource-network-timing
[2] http:/ / books. zkoss. org/ wiki/ ZK_Developer%27s_Reference/ Performance_Tips/ Client_Render_on_Demand
[3] http:/ / books. zkoss. org/ wiki/ ZK_Developer%27s_Reference/ Performance_Tips/ Listbox,_Grid_and_Tree_for_Huge_Data/
Use_Live_Data_and_Paging
[4] https:/ / developers. google. com/ web/ tools/ chrome-devtools/ evaluate-performance/
[5] http:/ / books. zkoss. org/ wiki/ ZK_Configuration_Reference/ zk. xml/ The_client-config_Element/ The_debug-js_Element
[6] http:/ / technet. microsoft. com/ en-us/ sysinternals/ bb896653
[7] http:/ / www. computerperformance. co. uk/ HealthCheck/ Disk_Health. htm#Disk%20Bottleneck%202
[8] https:/ / blogs. oracle. com/ nbprofiler/ entry/ profiling_with_visualvm_part_1
[9] https:/ / blogs. oracle. com/ nbprofiler/ entry/ profiling_with_visualvm_part_2
[10] http:/ / books. zkoss. org/ wiki/ ZK_Configuration_Reference/ web. xml/ ZK_Session_Cleaner
[11] http:/ / books. zkoss. org/ wiki/ ZK_Configuration_Reference/ zk. xml/ The_session-config_Element
[12] http:/ / books. zkoss. org/ wiki/ ZK_Configuration_Reference/ zk. xml/ The_desktop-config_Element
[13] http:/ / eclipsesource. com/ blogs/ 2013/ 01/ 21/ 10-tips-for-using-the-eclipse-memory-analyzer/
[14] http:/ / books. zkoss. org/ wiki/ ZK_Developer's_Reference/ Performance_Tips/ Reuse_Desktops
Testing
ZK is a Java framework. Technically you could use any Java test tools you prefer. Here we describe the testing tips
and ZTL (the official test tool based on Selenium).
For information of particular test tools, please refer to small talks:
• Sahi: Making ZK Functional Tests With Sahi
• Selenium: How to Test ZK Application with Selenium and ZK Unit Testing
• zunit: ZK Unit Testing Project - zunit
Testing Tips 495
Testing Tips
ID and UUID
By default, the desktop's ID and component's UUID are randomized for preventing Cross-Site Request Forgery
(CSRF) and allowing multiple desktops coexists in the same web page (such as Portlet). However, it also means the
DOM element's IDs will change from one test run to another, since component's UUID will become DOM element's
ID at the browser, .
If your test code runs at the server (such ZATS and JUnit), it is not an issue at all (since DOM elements are available
at the client only). However, if your test tool runs at the browser, you have to resolve it with one of the following
solutions:
1. Not to depend on DOM element's ID. Rather, use component's ID and/or component's parent-child-sibling
relationship.
2. Implement IdGenerator [1] to generate UUID in a predictable and repeatable way
With this approach, you still can verify the DOM structure if you want, since it can be retrieved from widget's
Widget.$n() [8].
ZTL [9] is a typical example that takes this approach. For more information, please refer to the ZTL section.
Testing Tips 496
<system-config>
<id-generator-class>my.IdGenerator</id-generator-class>
</system-config>
since 7.0.0
[10]
Since ZK 7.0.0, we provide a static ID generator implementation for testing, to use StaticIdGenerator , simply
add it to zk.xml.
<system-config>
<id-generator-class>org.zkoss.zk.ui.impl.StaticIdGenerator</id-generator-class>
</system-config>
<zk>
<system-config>
<id-generator-class>my.IdGenerator</id-generator-class>
</system-config>
</zk>
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ IdGenerator. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ _global_/ jq. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#$(zk. Object,
Testing Tips 497
[5] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#id
[6] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#firstChild
[7] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#nextSibling
[8] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#$n()
[9] http:/ / code. google. com/ p/ zk-ztl/
[10] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ impl/ StaticIdGenerator. html#
ZATS
Overview
ZK Application Test Suite (ZATS) is a collection of tools which can help users test their ZK-based application.
This suite has following modules:
ZATS Mimic, a unit-test library that can be used with any well-known unit test framework (e.g. JUnit and TestNG)
to test your ZUL without an application server or a browser.
ZATS Mimic enables developers to test their composer without an application server and of course without a
browser either. Through this library, testers can mimic user interactions to applications such as clicking or typing to
verify composer's (controller layer) data and logic. All they have to do is to write a regular unit test case and use
Mimic's utility class to interact components on ZUL and then, run the test case.
No deploying to server, no rendering on browser, the unit test case can be executed in a very short period of time -
this is very helpful for frequent unit testing during a agile development process.
The concept is as follows:
Testers write test cases to simulate user action such as clicking or typing with operation agents. Operation agent
communicates with server emulator and triggers the composer's event handlers to change the component's status.
Testers are able to check component's properties from the component agent to verify the result of user action. It
might be a label changing its value or a listbox increasing by one item. All behaviors that reflect on the
ZATS 498
<label />
</window>
</zk>
@Wire("label")
Label label;
@Listen("onClick = button")
public void hello(){
label.setValue("Hello Mimic");
}
}
We write the test case with JUnit 4 annotation, please refer to JUnit 4 in 60 seconds [1].
The following test case will mimic a user clicking the button and verify whether or not the the label's value is "Hello
Mimic".
HelloTest.java"
@AfterClass
ZATS 499
@After
public void after() {
Zats.cleanup();
}
@Test
public void test() {
DesktopAgent desktop =
Zats.newClient().connect("/hello.zul");
//button.as(ClickAgent.class).click();
button.click();
assertEquals("Hello Mimic",
label.as(Label.class).getValue());
}
}
• Before starting a test, we have to call Zats.init() and pass root directory where ZUL pages are stored as a
parameter. Most of the times, it is located in your web application's content root folder. In our example, we use
maven default project structure. This method initializes testing environment and starts the server emulator. (line
5)
• Of course, we start the server emulator at @BeforeClass , we should stop it by Zats.end() at
@AfterClass . (line 10)
• We should call Zats.cleanup() to clear desktop before opening another ZUL. (line 15)
• The first statement of a test case is to create a client and connect it to a ZUL page, like a browser visiting a ZUL.
The connect() returns a DesktopAgent and we usually retrieve ComponentAgent from it to perform
user operation. (line 20)
• Before we can mimic a user action to a component, we should retrieve a ComponentAgent. Empowered by
selector syntax, DesktopAgent .query() is a powerful tool to retrieve it. As the ZUL contains only one
button, we can query it by component name: query("button") (line 22)
• As we do not have a browser screen to view, we cannot interact with a component by mouse's pointer. To mimic a
user action, we have to convert ComponentAgent to one of the operation agents. The conversion method
as() will check for available operation for the target ComponentAgent . For example, you cannot type
something in a Label, If you try to convert it to an unsupported operation agent, you will get an exception. (line
25)
• For convenience, ComponentAgent provides shortcut methods for commonly used operations like click()
; it automatically gets operation agent and calls it for you. (line 26)
• To verify test result, we can also use ComponentAgent.as() to convert it to a ZK component then get its
property by getter methods. (line 27)
ZATS 500
Version History
Version Date Content
6.0.0 overview
References
[1] http:/ / www. cavdar. net/ 2008/ 07/ 21/ junit-4-in-60-seconds
Customization
Here describes how to customize ZK, such as initializing components with different properties, loading ZUML
document from database and so on.
Packing Code
There are two ways to pack the customization code: part of the Web application, or an independent JAR file. Packing
as part of the Web application is straightforward. All you have to do is to specify the customization in
WEB-INF/zk.xml as described in ZK Configuration Reference.
In many cases, it is better to pack the customization code as an independent JAR file, such that it can be managed
separately and reused in multiple Web applications.
<config>
<config-name>zkex</config-name><!-- used to resolve dependency -->
<depends>zk</depends>
<version>
<version-class>org.zkoss.zkex.Version</version-class>
<version-uid>5.0.6</version-uid>
<zk-version>5.0.0</zk-version><!-- or later -->
</version>
<listener>
<listener-class>org.zkoss.zkex.init.WebAppInit</listener-class>
</listener>
<library-property>
Packing Code 501
<name>org.zkoss.zul.chart.engine.class</name>
<value>org.zkoss.zkex.zul.impl.JFreeChartEngine</value>
</library-property>
<library-property>
<name>org.zkoss.zul.captcha.engine.class</name>
<value>org.zkoss.zkex.zul.impl.JHLabsCaptchaEngine</value>
</library-property>
</config>
[1] For more information, please refer to ZK Client-side Reference: Language Definition.
[2] For more information, please refer to ZK Configuration Reference: JAR File's config.xml.
Version History
Version Date Content
Component Properties 502
Component Properties
With component definitions, we could specify the initial values for the properties, attributes and annotations of a
component.
Properties
Depending on the requirement, you could change the initial value of a property for a particular ZUML document or
for the whole application.
Notice that the initial values are applicable only to the component instantiated by ZK Loaders. It has no effect if you
instantiate it in pure Java (unless you invoke Component.applyProperties() [4] after instantiating a component).
Page-wide Initialization
Suppose we want to assign normal to the border property (Window.setBorder(java.lang.String) [1]) of all windows
in a ZUML document, then we could use the component directive as follows.
Application-wide Initialization
If you prefer to have the same initial value for all ZUML documents, you could specify it in a language addon. For
example, we could prepare a file called WEB-INF/lang-addon.xml with the following content:
<language-addon>
<addon-name>myapp</addon-name>
<language-name>xul/html</language-name>
<component>
<component-name>window</component-name>
<extends>window</extends>
<property>
<property-name>border</property-name>
<property-value>normal</property-value>
</property>
</component>
</language-addon>
Then, we could specify this file by adding the following content to WEB-INF/zk.xml:
<language-config>
<addon-uri>/WEB-INF/lang-addon.xml</addon-uri>
</language-config>
Molds
A mold is yet another property (Component.setMold(java.lang.String) [1]), so you could change the initial value as
described in the previous section. However, since it is common to change the value, we allow developers to specify
the mold for a given component in a library property. As shown, the library is named as ClassName.mold. For
example, if you would like to specify trendy as the initial mold of a button, then you could add the following to
WEB-INF/zk.xml:
<library-property>
<name>org.zkoss.zul.Button.mold</name>
<value>trendy</value>
</library-property>
Attributes
Like properties, you could assign an attribute's initial value for a given component in the whole application (like
calling Component.setAttribute() [2]).
Notice that the initial values are applicable only to the component instantiated by ZK Loaders. It has no effect if you
instantiate it in pure Java (unless you invoke Component.applyProperties() [4] after instantiating a component).
Page-wide Initialization
Unlike the initial value of a property, there is no way to specify the initial value of a custom attribute in a ZUML
document.
Application-wide Initialization
Similar to customizing the initial value of a property, you could specify the following in a language addon to assign
an initial value of a attribute to a component.
<language-addon>
<addon-name>myapp</addon-name>
<language-name>xul/html</language-name>
<component>
<component-name>panel</component-name>
<extends>panel</extends>
<custom-attribute>
<attribute-name>any.attribute.name</attribute-name>
<attribute-value>any.value</attribute-value>
</custom-attribute>
</component>
</language-addon>
Component Properties 504
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zul/ Window. html#setBorder(java. lang. String)
[2] https:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ ext/ Scope. html#setAttribute(java. lang. String,%20java. lang.
Object,%20boolean)
UI Factory
UiFactory [1] is used to instantiate all UI objects, such as session, desktop, and components, and to load ZUML
documents. You could customize it to provide the functionality you want.
For example, SerializableUiFactory [1] is the factory used to instantiate sessions that are serializable[1], while
SimpleUiFactory [2], the default factory, instantiates non-serializable sessions.
Here are a list of customization you could do with UI Factory:
• Load a ZUML document from, say, a database
• It can be done by overriding java.lang.String) UiFactory.getPageDefinition(org.zkoss.zk.ui.sys.RequestInfo,
java.lang.String) [3]
• Instantiate a component by using a different implementation
• It can be done by overriding org.zkoss.zk.ui.Component, org.zkoss.zk.ui.metainfo.ComponentInfo)
UiFactory.newComponent(org.zkoss.zk.ui.Page, org.zkoss.zk.ui.Component,
org.zkoss.zk.ui.metainfo.ComponentInfo) [4] and org.zkoss.zk.ui.Component,
org.zkoss.zk.ui.metainfo.ComponentInfo, java.lang.String) UiFactory.newComponent(org.zkoss.zk.ui.Page,
org.zkoss.zk.ui.Component, org.zkoss.zk.ui.metainfo.ComponentInfo, java.lang.String) [4].
• Instantiate a desktop by using a different implementation
• It can be done by overriding java.lang.String, java.lang.String)
UiFactory.newDesktop(org.zkoss.zk.ui.sys.RequestInfo, java.lang.String, java.lang.String) [5]
• Instantiate a page by using a different implementation
• It can be done by overriding org.zkoss.zk.ui.metainfo.PageDefinition, java.lang.String)
UiFactory.newPage(org.zkoss.zk.ui.sys.RequestInfo, org.zkoss.zk.ui.metainfo.PageDefinition,
java.lang.String) [6] and/or org.zkoss.zk.ui.Richlet, java.lang.String)
UiFactory.newPage(org.zkoss.zk.ui.sys.RequestInfo, org.zkoss.zk.ui.Richlet, java.lang.String) [6]
[1] [2]
Notice that it is suggested to extend from either SerializableUiFactory or SimpleUiFactory , rather than to
implement UiFactory [1] from scratch.
UI Factory 505
[1] Then, the application is able to run in a clustering environment. Fore more information, please refer to the Clustering section
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ http/ SimpleUiFactory. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ UiFactory. html#getPageDefinition(org. zkoss. zk. ui. sys.
RequestInfo,
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ UiFactory. html#newComponent(org. zkoss. zk. ui. Page,
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ UiFactory. html#newDesktop(org. zkoss. zk. ui. sys. RequestInfo,
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ UiFactory. html#newPage(org. zkoss. zk. ui. sys. RequestInfo,
where we assume you implemented loadFromDatabase to load the ZUML document from a database. In
addition, you have to implement getFromCache and setCache to cache the result in order to improve the
performance of retrieving the document from the database.
On the other hand, the parsing of the ZUML document can be done easily by calling java.lang.String,
java.lang.String) AbstractUiFactory.getPageDefinitionDirectly(org.zkoss.zk.ui.sys.RequestInfo, java.lang.String,
java.lang.String) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ impl/ AbstractUiFactory.
html#getPageDefinitionDirectly(org.zkoss.zk.ui.sys.RequestInfo,).
UI Factory 506
Version History
Version Date Content
Interfaces
Task Interface
Notice that ZK will instantiate an object from the class you registered for each callback. For example, an object is
instantiated to invoke java.lnag.Object) DesktopInit.init(org.zkoss.zk.ui.Desktop, java.lnag.Object) [6], and another
object instantiated to invoke DesktopCleanup.cleanup(org.zkoss.zk.ui.Desktop) [7], even if you register a class that
implements both DesktopInit [4] and DesktopCleanup [3].
If you have something that is initialized in the init callback and have to clean it up in the cleanup callback, you
cannot store it as a data member. Rather, you have to maintain it by yourself, such as storing it in the desktop's
attributes (java.lang.Object) Desktop.setAttribute(java.lang.String, java.lang.Object) [8]), session's attributes or
application's attributes.
Init and Cleanup 507
Registration
The registration in WEB-INF/zk.xml is the same, no matter what interface you implement:
<listener>
<listener-class>my.MyImplementation</listener-class>
</listener>
webapp.getConfiguration().addListener(my.MyImplementation.class);
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ WebAppCleanup. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ SessionInit. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ SessionCleanup. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopInit. html#
[5] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ ExecutionInit. html#
[6] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopInit. html#init(org. zkoss. zk. ui. Desktop,
[7] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ DesktopCleanup. html#cleanup(org. zkoss. zk. ui. Desktop)
[8] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Desktop. html#setAttribute(java. lang. String,
[9] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/ Configuration. html#addListener(java. lang. Class)
AU Services 508
AU Services
An AU service (AuService [1]) is a plugin used to intercept the AU requests (AuRequest [2]) sent from the client.
By plugging in an AU service, you could
• Ignore some AU requests (such as hostile requests)
• Change the default way of handling an AU request
• Handle application-specific AU requests
To plug an AU service to a desktop, you could invoke Desktop.addListener(java.lang.Object) [3]. You could plug as
many AU services as you want. Once plugged, all AU requests will go through the AU services (unless it was
ignored by other AU service).
If you want to plug a particular component, you could invoke Component.setAuService(org.zkoss.zk.au.AuService)
[4]
. Unlike desktops, a component can have at most one AU service.
If you want to plug an AU service, you could implement DesktopInit [4] and register it in zk.xml as described in the
Init and Cleanup section.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ AuService. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ AuRequest. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ sys/ Desktop. html#addListener(java. lang. Object)
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Component. html#setAuService(org. zkoss. zk. au. AuService)
AU Extensions 509
AU Extensions
An AU extension (AuExtension [1]) is a small program that can be plugged into ZK Update Engine
(DHtmlUpdateServlet [2]) and extend its functionality. Actually our file upload and multimedia viewing are
implemented as an AU extension that you can replace with your implementation.
An AU extension is associated with a name starting with slash, such as "/upload". Then each time a request targeting
/zkau/upload will be forwarded to this extension for service.
To register an AU extension, you could specify the name and the class name as the initial parameter of the
declaration of ZK Update Engine in WEB-INF/web.xml. For more information, please refer to ZK Configuration
Reference.
If you want to register it in Java, you could use org.zkoss.zk.au.http.AuExtension)
DHtmlUpdateServlet.addAuExtension(java.lang.String, org.zkoss.zk.au.http.AuExtension) [3] instead.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ http/ AuExtension. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ http/ DHtmlUpdateServlet. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ http/ DHtmlUpdateServlet. html#addAuExtension(java. lang. String,
5.0.* (current) [1] The 5.0 branch. It is the working repository for the most up-to-date
source codes for current ZK 5
6.0.* [2] The 6.0 branch. It is the working repository for the most up-to-date
(upcoming) source codes for upcoming ZK 6
3.6.* [3] The 3.6 branch. It is the working repository for the most up-to-date
(maintaining) source codes for maintaining ZK 3.6. Though it is named trunk, it is
used only for the 3.6 branch now.
3.0.* [4] The 3.0 branch. It is the working repository for the most up-to-date
(maintaining) source codes for maintaining ZK 3.0
2.4.* [5] The 2.4 branch. It is the working repository for the most up-to-date
(maintaining) source codes for maintaining ZK 2.4
Releases https://zk1.svn.sourceforge.net/svnroot/zk1/releases/x.y.z The releases. We won't change the code in this repository. The URL
(frozen) depends on the version you want to check out. For a complete list,
please visit [6]
How to Build ZK Source Code 510
Maven Build
Since ZK 5.0.5 release, we put an Eclipse project setting into each submodule folder, such as zk, zul, zkdemo, and so
on. After that time, you can be able to check out the source code from the SVN path above as a Eclipse Maven
project to develop/build it. Or you can use Maven command line to build the ZK Jar files.
For example,
$ svn checkout
https://zk1.svn.sourceforge.net/svnroot/zk1/releases/5.0.5/zul zul
$ cd zul
$ mvn clean package
See Also
• ZK Installation Guide: Setting up IDE/Maven
Version History
Version Date Content
References
[1] https:/ / zk1. svn. sourceforge. net/ svnroot/ zk1/ branches/ 5. 0/
[2] https:/ / zk1. svn. sourceforge. net/ svnroot/ zk1/ branches/ 6. 0/
[3] https:/ / zk1. svn. sourceforge. net/ svnroot/ zk1/ trunk/
[4] https:/ / zk1. svn. sourceforge. net/ svnroot/ zk1/ branches/ 3. 0/
[5] https:/ / zk1. svn. sourceforge. net/ svnroot/ zk1/ branches/ 2. 4/
[6] http:/ / zk1. svn. sourceforge. net/ viewvc/ zk1/ releases/ .
Handle AU Request Resend 511
if (ajaxReqTries == null)
//
http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_client-config_Element/The_auto-resend-timeout_Element
if (!zAu.confirmRetry("FAILED_TO_RESPONSE", status+(statusText?":
"+statusText:"")))
return 0; // no retry;
return ajaxReqTries;
For more detail on the arguments, please take a look at int, _global_.String, int)
zAu.ajaxErrorHandler(java.lang.Object, int, _global_.String, int) [1]
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ _global_/ zAu. html#ajaxErrorHandler(java. lang. Object,
[2] http:/ / tracker. zkoss. org/ browse/ ZK-1616
Supporting Utilities 512
Supporting Utilities
In this section we will discuss the utilities that ZK are built on. You don't need them to develop ZK applications, but
you might find them useful if they are applicable. Here we provide the basic information of them. Interested readers
might refer to Javadoc [1] for detailed API.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/
Logger
In this section we describe how to configure the logging of ZK internal functions. You general can ignore it, unless
you'd like to know how ZK operates internally.
Notice that, if you are using Google App Engine, you can not configure the logging as described in this chapter. For
more information, please refer to Setting up Google App Engine.
<dependency>
<groupId>org.zkoss.common</groupId>
<artifactId>zcommon</artifactId>
<version>${zk.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</exclusion>
</exclusions>
</dependency>
ZK uses the standard logger [4] to log messages. You could control what to log by configuring the logging of the
Web server you are using. The configuration usually varies from one server to another. However, you could use the
configuration mechanism provided by ZK as described in this section. It shall work with most Web servers.
There are basically two steps to configure the standard logger with ZK's configuration mechanism:
1. Prepare a logging configuration file
2. Specify the configuration file in a library property
Logger 513
A logging configuration file is a standard properties file. Each line is a key-value pair in the following format:
''a.package.or.a.class'' = ''level''
org.zkoss.zk.ui.impl.UiEngineImpl=FINER
#Make the log level of the specified class to FINER
org.zkoss.zk.ui.http=DEBUG
#Make the log level of the specified package to DEBUG
org.zkoss.zk.ui=OFF
#Turn off the log for the specified package
org.zkoss=WARNING
#Make all log levels of ZK classes to WARNING except those
specified here
Allowed Levels
[Deprecated since ZK 7.0.0]
Level Description
INFO Indicates providing informational messages. It also implies ERROR and WARNING.
FINE Indicates providing tracing information for debugging purpose. It also implies ERROR, WARNING and INFO.
FINER Indicates providing fairly detailed tracing information for debugging purpose. It also implies ERROR, WARNING, INFO and
DEBUG
Specify the handler for Jetty and servers that don't turn on the standard logger
[Deprecated since ZK 7.0.0]
Some Web servers, such as Jetty, don't turn on the standard logger by default. Thus, in the logging configuration file,
you have to configure the handler too. For example, you can turn on the
java.util.logging.ConsoleHandler to write the logs to the console by adding the following lines to the
logging configuration file:
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter =
java.util.logging.SimpleFormatter
Logger 514
Here is another example that configures the console and a file to be the target of the logs:
handlers = java.util.logging.FileHandler,
java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter =
java.util.logging.SimpleFormatter
java.util.logging.FileHandler.formatter =
java.util.logging.SimpleFormatter
java.util.logging.FileHandler.pattern = /var/log/jetty6/solr-%u.log
java.util.logging.FileHandler.level = FINER
org.zkoss.zk.ui.impl.UiEngineImpl=FINER
org.zkoss.bind=FINE
To let ZK load the logging configuration file, you have to specify in a library property called
org.zkoss.util.logging.config.file. For example,
<library-property>
<name>org.zkoss.util.logging.config.file</name>
<value>conf/zk-log.properties</value>
</library-property>
If a relative path is specified, it will look for the class path first. If not found, it will assume it is related to the current
directory, i.e., the directory specified in the system property called user.dir.
You could specify an absolute path, such as /usr/jetty/conf/zk-log.properties, if you are not sure
what the current directory is.
[1] http:/ / www. slf4j. org/
[2] http:/ / www. slf4j. org/ manual. html
[3] https:/ / docs. oracle. com/ cd/ E19717-01/ 819-7753/ gcbkm/ index. html
[4] http:/ / docs. oracle. com/ javase/ 1. 4. 2/ docs/ guide/ util/ logging/ overview. html
<servlet>
<servlet-name>zkLoader</servlet-name>
<servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
<init-param>
<param-name>log-level</param-name>
<param-value>OFF</param-value>
Logger 515
</init-param>
</servlet>
How to Log
Class: Log (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/util/logging/Log.html#)
The logger used by ZK is based on the standard logger, java.util.logging.Logger. However, we wrap it as
Log (http://www.zkoss.org/javadoc/latest/zk/org/zkoss/util/logging/Log.html#) to make it more efficient.
To log the message to the client rather than the console at the server, you could use
Clients.log(java.lang.String) (http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ util/
Clients.html#log(java.lang.String))
The typical use is as follows.
import org.zkoss.util.logging.Log;
class MyClass {
private static final Log log = Log.lookup(MyClass.class);
public void f(Object v) {
if (log.debugable()) log.debug("Value is "+v);
}
}
Version History
Version Date Content
DSP
Package: org.zkoss.web.servlet.dsp [1]
A JSP-like template technology. It takes the same syntax as that of JSP. Unlike JSP, DSP is interpreted at the run
time, so it is easy to deploy DSP pages. No Java compiler is required in your run-time environment. In addition, you
could distribute DSP pages in jar files.
However, you cannot embed Java codes in DSP pages. Actions of DSP, though extensible through TLD files, are
different from JSP tags.
If you want to use DSP in your Web applications, you have to set up WEB-INF/web.xml to add the following
lines.
<servlet>
<description><![CDATA[
The servlet loads the DSP pages.
]]></description>
<servlet-name>dspLoader</servlet-name>
<servlet-class>org.zkoss.web.servlet.dsp.InterpreterServlet</servlet-class>
<!-- Specify class-resource, if you want to access TLD defined in jar files -->
<init-param>
<param-name>class-resource</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dspLoader</servlet-name>
<url-pattern>*.dsp</url-pattern>
</servlet-mapping>
The mapping of the DSP loader is optional. Specify it only if you want to write Web pages in DSP syntax.
Though standard components of ZK use DSP as a template technology, they are handled directly by ZK loader.
A Sample of DSP
<%@ page contentType="text/css;charset=UTF-8" %>
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<!--Test-->
<c:if test="${c:isSafari() || c:browser('chrome')}">
.search-input-outer input {
padding: 0 2px;
}
DSP 517
</c:if>
</style>
For more details, please check the javadoc of org.zkoss.web.servlet.dep.action [2] package.
Version History
Version Date Content
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ servlet/ dsp/ package-summary. html
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ web/ servlet/ dsp/ action/ package-summary. html
iDOM
Package: org.zkoss.idom [1]
An implementation of W3C DOM. It is inspired by JDOM[2] to have concrete classes for all XML objects, such as
Element and Attribute. However, iDOM implements the W3C API, such as org.w3c.dom.Element. Thus, you could
use iDOM seamlessly with XML utilities that only accept the W3C DOM.
A typical example is XSLT and XPath. You could use any of favorite XSL processor and XPath utilities with iDOM.
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ idom/ package-summary. html
[2] http:/ / www. jdom. org
Version History
Version Date Content
Article Sources and Contributors 518
Architecture Overview Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/Overture/Architecture_Overview Contributors: Alicelin, Char, Flyworld, Neillee, Tomyeh
ID Space Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/UI_Composing/ID_Space Contributors: Alicelin, Hawk, Henrichen, M17, Tomyeh, Vincent
Richlet Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/UI_Composing/Richlet Contributors: Alicelin, Char, Hawk, Jumperchen, M17, Robertwenzel, Sphota,
Tomyeh
Event Queues Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/Event_Handling/Event_Queues Contributors: Alicelin, Ashishd, Hawk, Henrichen, Jeanher,
Jumperchen, Tmillsclare, Tomyeh, Vincent
Composer Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/MVC/Controller/Composer Contributors: Alicelin, Dennischen, Hawk, Henrichen, Iantsai, M17,
Raymondchao, SimonPai, Tomyeh, Wenninghsu
Wire Variables Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/MVC/Controller/Wire_Variables Contributors: Alicelin, Char, Hawk, Jimmyshiau, Matthieu,
Tomyeh
Wire Event Listeners Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/MVC/Controller/Wire_Event_Listeners Contributors: Alicelin, Char, SimonPai, Tomyeh,
Wenninghsu
List Model Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/MVC/Model/List_Model Contributors: Alicelin, Hawk, Jumperchen, SimonPai, Tendysu, Tomyeh
Tree Model Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/MVC/Model/Tree_Model Contributors: Alicelin, Hawk, Jimmyshiau, Jumperchen, MontyPan, Simbo,
SimonPai, Southerncrossie, Tomyeh, Vincent
Hflex and Vflex Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/UI_Patterns/Hflex_and_Vflex Contributors: Alicelin, Hawk, Jimmyshiau, Peterkuo, SimonPai,
Southerncrossie, Tomyeh, Vincent
Forward and Redirect Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/UI_Patterns/Forward_and_Redirect Contributors: Alicelin, Matthieu, Tomyeh, V0v87,
Vincent
Error Handling Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/UI_Patterns/Error_Handling Contributors: Alicelin, Ashishd, Hawk, Iantsai, Robertwenzel,
Tomyeh
Useful Java Utilities Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/UI_Patterns/Useful_Java_Utilities Contributors: Hawk, Jumperchen, MontyPan, SimonPai,
Vincent
Theming and Styling Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/Theming_and_Styling Contributors: Alicelin, Jeanher, Neillee2, Tomyeh
Labels Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/Internationalization/Labels Contributors: Alicelin, Char, Hawk, Jimmyshiau, Maya001122, Robertwenzel,
SimonPai, Tomyeh, Tonyq
The First Day of the Week Source: http://new.zkoss.org/index.php?title=ZK_Developer%27s_Reference/Internationalization/The_First_Day_of_the_Week Contributors: Alicelin, Char,
Maya001122, Tomyeh