Professional Documents
Culture Documents
Essentials
For ZK 5.0.7
PDF generated using the open source mwlib toolkit. See http://code.pediapress.com/ for more information.
PDF generated at: Wed, 11 May 2011 09:41:04 CST
Contents
Articles
ZK Component Development Essentials 1
ZK Component Overview 1
What is a ZK component 1
How does a ZK Component work 2
Development Tools 3
Creating a simple ZK Component 3
ZK's JavaScript Extension 3
Implementing the Component 4
Implementing a Component Property 5
Render All Properties to the Client 6
Putting it all Together 7
Implementing the Widget 8
Implementing a Widget Property 9
Putting it all Together 10
Rendering Widgets 11
Implementing Molds 11
The Redraw Method 12
Creating the Configuration Files 12
The language-addon 12
The Widget Package Descriptor 13
Handling Events 15
How we Implement the Event 15
Overriding bind and unbind 16
Registering Appropriate Listeners 17
Client-Server Communication 18
Server-side Listeners 20
Declaring an Important Event 21
Packing as a Jar 21
Conclusion 25
References
Article Sources and Contributors 26
Image Sources, Licenses and Contributors 27
ZK Component Development Essentials 1
If you have any feedback regarding this book, please leave it here.
<comment>http://books.zkoss.org/wiki/ZK_Component_Development_Essentials2</comment>
ZK Component Overview
This section provides an overview to ZK Component development.
What is a ZK component
Each UI object in ZK consists of a component and a widget.
Component
A component is a Java object running at the server which represents a UI object which can be manipulated by a Java
application. A component has all the behavior of a UI object except it has no visual part.
Widget
A widget is a JavaScript object running at the client. This object represents the UI object which interacts with the
user. Therefore, a widget usually has a visual appearance, and handles events happening at the client.
Having established that there are two parts to a ZK Component one needs to explore how these parts interact to form
a fully interactive user experience.
How does a ZK Component work 2
For example, when an application invokes the setLabel method to change the label of a button component, the
setLabel method of corresponding button widget (aka., peer widget) will be invoked at the client to change the visual
appearance (as shown below).
When the user clicks the button widget, the onClick event will be sent back to the server and notify the application
(as demonstrated below).
In additions to manipulate a component at the server, it is also possible to control a widget at the client. For example,
an application may hide or change the order of grid columns at the client, while the application running at the server
handles the reloading of the grid’s content. This technique is called Server+client fusion. It can be used to improve
responsiveness and reduce network traffic.
Development Tools 3
Development Tools
Here is a list of development tools. Though optional, they are helpful in many situations.
Tool Description
[1] A component development tool providing wizards to simplify the creation of components.
ZK CDT
[2] A JavaScript debugger. It is an addon for Firefox. It is not built-in, so you have to download and install it
Firebug
separately.
Internet Explorer 9 Developer It is built-in and you could start it by pressing F12.
Tools
References
[1] http:/ / code. google. com/ a/ eclipselabs. org/ p/ zk-cdt/
[2] http:/ / getfirebug. com/
zk.$package('com.foo');
com.foo.Location = zk.$extends(zk.Object, {
x: 0,
y: 0,
distance: function (loc) {
return Math.sqrt(Math.pow(this.x - loc.x, 2) + Math.pow(this.y -
loc.y, 2));
}
},{
find: function (name) {
if (name == 'ZK')
return new com.foo.Location(10, 10);
throw 'unknown: "+name;
}
})
The first argument of _global_.Map, _global_.Map) zk.$extends(zk.Class, _global_.Map, _global_.Map) [1] is the
base class to extend from. In this case, we extend from Object [2], which is the root of the class hierarchy. The
ZK's JavaScript Extension 4
second argument consists of the (non-static) members of the class. In this case, we define two data members (x and
y) and one method (distance).
The third argument is optional and if omitted means that the extended class will contain no static members. In the
example we define a static method (<mp>find</mp>). For now this is all that is required for us to create our first
component so let’s move onto talk about the component implementation.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ _global_/ zk. html#$extends(zk. Class,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Object. html#
For tutorial purpose, we use HtmlBasedComponent [2], which is the base class for HTML-based component.
To implement a component class, we need to decide on
• The class name. Let’s name it <mp>com.foo.SimpleLabel</mp>
• The properties to support. In this case, we'd like to implement a property called value, which is the visual content
at the client.
Let’s investigate the component properties.
Implementing the Component 5
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ AbstractComponent. html#
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ HtmlBasedComponent. html#
The setter is similar except we have to notify the client. This is achieved by using the java.lang.Object)
AbstractComponent.smartUpdate(java.lang.String, java.lang.Object) [1] function.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ AbstractComponent. html#smartUpdate(java. lang. String,
Render All Properties to the Client 6
Sent to client if name is the same Only the last value is sent Yes
Render All Properties to the Client 7
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ AbstractComponent. html#renderProperties(org. zkoss. zk. ui. sys.
ContentRenderer)
[2] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ AbstractComponent. html#redraw(java. io. Writer)
package com.foo;
protected void
renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
throws java.io.IOException {
super.renderProperties(renderer);
render(renderer, "value", _value);
}
}
For the purposes of this tutorial, we will use Widget [1]. Before we proceed, we need to decide the name of the
widget class. Let’s assume <mp>com.foo.SimpleLabel</mp>.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#
Implementing a Widget Property 9
getValue: function () {
return this._value;
}
The setter is defined in a similar manner except we have to modify the DOM tree if it has been attached. A widget
inherits a property called node which is assigned a reference to a DOM element if the widget has been attached to the
DOM tree. If a widget is attached to DOM, <mp>this.desktop</mp> will be a reference to the desktop (Desktop [1])
it belongs. Otherwise, it is null.
How we update depends on the DOM content. In this example, we use HTML's span to enclose the value, so we only
need to change innerHTML.
setValue: function(value) {
if (this._value != value) {
this._value = value;
if (this.desktop) this.$n().innerHTML = zUtl.encodeXML(value);
}
}
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ zk/ Desktop. html#
Putting it all Together 10
com.foo.SimpleLabel = zk.$extends(zk.Widget, {
_value : '', // default value
getValue : function() {
return this._value;
},
setValue : function(value) {
if (this._value != value) {
this._value = value;
if (this.desktop)
this.$n().innerHTML = zUtl.encodeXML(value);
}
}
});
Having set-up the ability to handle states we now need to create the component view. This can be accomplished
using two methods.
Rendering Widgets 11
Rendering Widgets
Rendering of widgets can be achieved using two methods. The first is named mold which enables a widget to have
multiple display types which a developer or user can select at will. The second is a redraw method which only
supports one type of view. Firstly let us discuss how to implement a mold.
Implementing Molds
A widget can have several molds. Each mold needs to be placed in an independent JavaScript file. Under a
subdirectory named mold. The full path of the directory in this example will be
/web/js/com/foo/mold/simple-label.js.
Let us assume we want to generate the following DOM content:
<mp>value</mp>
Then, the content of simple-label.js will be as follows.
function (out) {
out.push('<span', this.domAttrs_(), '>', this.getValue(), '</span>');
}
As shown above, the mold is actually a JavaScript method. More precisely, it is a method member of the widget
class (the name is assigned by ZK Client Engine automatically), so you can access the widget object by use of this.
The mold method takes an argument named out, which behaves like a writer in Java. The out object at least
implements the push and unshift method to write the content to the end or to the beginning. It is by default an array,
but the client application might use different kinds of objects.
[1]
<mp>domAttrs_</mp> is a (protected) method inherited from Widget . It returns all HTML attributes required,
such as style, id and so on. You can override it if you want.
If we do not require multiple styles per component we can just implement the redraw method directly.
The Redraw Method 12
The default implementation of Widget.redraw(_global_.Array) [1] delegates to a mold method depending on the
mold. In this instance we override the function to provide one implementation of redraw which doesn’t use molds.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#redraw(_global_. Array)
The language-addon
The language-addon contains a description of the relation between the component and widget referred to as the
component definition. The path of the file must be /metainfo/zk/lang-addon.xml and be located in the Java class
path.
<language-addon>
<addon-name>simplelabel</addon-name>
<language-name>xul/html</language-name>
<component>
<component-name>simplelabel</component-name>
<component-class>com.foo.SimpleLabel</component-class>
<widget-class>com.foo.SimpleLabel</widget-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>mold/simple-label.js</mold-uri>
<css-uri>css/simple-label.css.dsp</css-uri>
</mold>
</component>
</language-addon>
The table below describes the elements used within the above XML and their descriptions.
The language-addon 13
Name Description
language-name The language name that this addon belongs to (for instance, xul/html or
zhtml)
mold-name The name of the mold. The default mold is named default.
A component may have multiple molds which may or may not have different widget classes. To handle this you may
specify the <widget-class> inside <mold>. You can specify more than one mold.
After creating the component descriptor language-addon we need to create the widget package descriptor.
For more information, please refer to ZK Client-side Reference: Language Definition.
The table below describes the elements used within the above XML and their descriptions.
Name Description
package The root element denotes the package name and the language it belongs to
widget The widget class name (without the package name). If the package contains multiple widgets list them one by one
Having created the configuration the basic implementation of our component is complete. However it doesn』t have
any interactive events. Therefore the next logical step is to start adding events to the component.
The Widget Package Descriptor 14
Package Dependence
It is common for JavaScript packages to depend on another package. For example, zul.grid depends on
zul.mesh and zul.menu. This can easily be specified by placing them within the depends attribute as
follows.
For more information, please refer to ZK Client-side Reference: Widget Package Descriptor.
Handling Events 15
Handling Events
The next logical step is to add an event to our ZK component. To this we are going add some more functionality to
the SimpleLabel. A new div will be added which when clicked should clear the displayed text and fire a custom
event named <mp>onClear</mp>.
This means that firstly we need to change the mold of the label to include a div which can be used as a target. The
code below satisfies this requirement:
function (out) {
out.push('<span', this.domAttrs_(), '><div id="value" style="float:left;">', this.getVal
}
As you can see we have now split the contents of label into two by introducing two div tags, one will be used to
display the value and the other will be a click target.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#bind_(zk. Desktop,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#unbind_(zk. Skipper,
Overriding bind and unbind 16
The _global_.Array) unbind_(zk.Skipper, _global_.Array) [2] function is very similar. Upon detaching of the widget
from the DOM the _global_.Array) unbind_(zk.Skipper, _global_.Array) [2] method is called enabling us to perform
tasks such as adding and removing listeners to avoid memory leaks. The diagram below demonstrates this.
Now that we have had a brief introduction of zk.Skipper, _global_.Array) bind_(zk.Desktop, zk.Skipper,
_global_.Array) [1] and _global_.Array) unbind_(zk.Skipper, _global_.Array) [2] let’s see the methods put into action
when we bind appropriate listeners for events.
Registering Appropriate Listeners 17
bind_ : function(evt) {
this.$supers('bind_', arguments);
this.domListen_(this.$n().lastChild, "onClick",
'_doClear');
},
unbind_ : function(evt) {
this.domUnlisten_(this.$n().lastChild, "onClick",
'_doClear');
this.$supers('unbind_', arguments);
},
The key to this is the domListen method which takes the target as the first parameter, in this case we pass it the
<mp>lastChild</mp> which is our target, the name of the event you want to listen for as the second parameter and
finally the name of the callback.
Please note that you also are required to call <mp>$supers</mp> so that parent classes can register and remove their
events successfully. Now we need to move on to firing our custom <mp>"onClear"</mp> event, let’s take a look at
how to do this.
_doClear: function(evt) {
this._cleared = !(this._cleared);
if(this._cleared) {
this.$n().firstChild.innerHTML = this._value;
} else {
this.$n().firstChild.innerHTML = "";
}
We have a data member named _cleared which contains the state of the application. Depending on the state the
method either shows or clears the displayed value. The method <mp>onClear</mp> is then fired and the cleared
state is sent along with the instruction to fire the event.
The client side widget will now communicate with the component at the server side. This is handled by ZK. The
following section explores how this communication works.
Client-Server Communication
The following diagram outlines how communication works between a ZK Widget and Component.
zk.Object, _global_.Map, int) Widget.fire(_global_.String, zk.Object, _global_.Map, int) [1] fires a client event (an
instance of Event [2]), and the client event is converted to an AU request if all the following conditions are satisfied.
• The widget is a peer of a component, that is, it was created automatically to represent a component. Notice that
the Server states whether a widget is a peer of a component.
• The event propagation is not stopped (i.e., zk.Object, _global_.Map, int) Widget.fire(_global_.String, zk.Object,
_global_.Map, int) [1] not set.
• The event is listened by a server-side Listener [3], or it is an important event.
The above image demonstrates that the onClear event is sent to the server and processed. The code to do is located in
the component’s Java file SimpleLabel.java and is as follows.
if (cmd.equals(ClearEvent.NAME)) {
ClearEvent evt = ClearEvent.getClearEvent(request);
_cleared = evt.getCleared();
Events.postEvent(evt);
Client-Server Communication 19
} else
super.service(request, everError);
}
Here the ClearEvent is in fact a completely customized event created for the purposes of this component. The event
is created using its static method <mp>getClearEvent</mp>, shown below.
The retrieval of the event is easy as the ClearEvent extends Event and its constructor has the following signature
public ClearEvent(String name, Component target, boolean cleared). The only custom information is the
Boolean cleared which is the state we sent back when the event was fired. As demonstrated in the above code this is
easily acquired using the java.lang.String) AuRequests.getBoolean(java.util.Map, java.lang.String) [4].
We have now followed the process of how data is transferred between the client and server. Of course when building
component we also need to specify server side listeners to invoke Java code on calling of specific events, let’s
investigate this.
References
[1] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Widget. html#fire(_global_. String,
[2] http:/ / www. zkoss. org/ javadoc/ latest/ jsdoc/ zk/ Event. html#
[3] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ ui/ Listener. html#
[4] http:/ / www. zkoss. org/ javadoc/ latest/ zk/ org/ zkoss/ zk/ au/ AuRequests. html#getBoolean(java. util. Map,
Server-side Listeners 20
Server-side Listeners
Then we move on and fire the <mp>"onClear"</mp> event. To fully understand what happens next we now need to
investigate server side listeners which handle these events. A client event will not be sent to the server if it has not
been registered. To give the developer the ability to register an event listener at the serverside we need to declare the
events. We do this using the function addClientEvent.
The following example demonstrates declaring a clear event at the server.
static {
addClientEvent(SimpleLabel.class, ClearEvent.NAME, 0);
}
We have noted that if an event is not registered at the server side then it will not be sent from the client. However, in
our example there is a problem if the event is not sent back. Let’s cast our mind back to the service method we
implemented.
if (cmd.equals(ClearEvent.NAME)) {
ClearEvent evt = ClearEvent.getClearEvent(request);
_cleared = evt.getCleared();
Events.postEvent(evt);
} else
super.service(request, everError);
}
Notice that on the receipt of the <mp>ClearEvent</mp> we update the <mp>_cleared</mp> property at the server
side. Imagine if this event is not sent then we have a problem as the client and server side will be out of sync. To get
around this we can register the event as important.
Declaring an Important Event 21
static {
addClientEvent(SimpleLabel.class, ClearEvent.NAME,
CE_IMPORTANT);
}
Now the ClearEvent is guaranteed to be sent to the server which solves our syncing problem. Now we have a simple
label with some additional features which works as advertised. This label has served as a good introduction to
component development with ZK.
Packing as a Jar
Jar Files
Packing a component as a Jar file will make it easy to deployment, this article is telling you steps and requirements
of it.
Configurations
Packing as a Jar 22
• /META-INF/
• MANIFEST.MF
• /metainfo/
• mesg/
• msg<jar name>.properties(optional)
• msg<jar name>_<locale>.properties (optional ...)
• xml/
• <component-name>.xsd (optional)
• zk/
• lang.xml (optional)
• lang-addon.xml (optional)
File descriptions
• /META-INF/
• MANIFEST.MF
• The file for jar defino
• /metainfo/
• mesg/
• msg<jar name>.properties
• These files are i18n resource files,you can define with the locale you want , the default file is
msg<jar name>.properties.
ZK Internationalization
• xml/
• <jar-name>.xsd
• The xml schema for component tags in zul
• tld
• config.xml
• For taglibs definition. (ex. zweb.jar use this for dsp.)
• zk/
• lang-addon.xml
• The language add-on define components , and it defined the component classes, javascript
widget/mold/css.
it should contains At least one lang.xml or lang-addon.xml usually you will need a
lang-addon.xml .
• lang.xml
• The language definition file with namespace (ex. http:/ / www. w3. org/ 1999/ xhtml for zhtml)
Component classes
Packing as a Jar 23
File Structure
• /package-folder
• classes
File descriptions
• The java classes of component. just like normal jar
file.
Widget Resources
• /web/
• js/
• component-package/
• mold/
• <widget-mold-js-file>
• css/
• <widget-css-dsp-file> (optional)
• <widget-css-file> (optional)
• <widget-class-js-file>
• /zk.wpd
File descriptions
• /web/
• js/
• <component-package>/
• mold/
• <widget-mold-js-file> (ex. simple-label.js )
• Widget mold file , you can write widget's html with javascript function
here.
• css/
• <widget-css-dsp-file> (ex. simple-label.css.dsp )
• The css dsp files ,in the dsp you can use some variable with zk
enviroment to write it.
• <widget-css-file> (ex. simple-label.css )
• the pure css files.
• <widget-class-file> (ex. Simple-label.js)
• The widget class you write .
• zk.wpd
• Define your component's package and widgets here, and the dependency
with other package.
Packing as a Jar 24
Static Resources
File descriptions
• /web/
• <component-package>/
• css/
• <css files>
• For some static css file you might need.
• zk.wcs
• Let you can config the CSS file for particular language
• img/
• <img files>
• This is a folder for some image files , and you can access them in xxx.css.dsp files through
${c:encodeURL('~./img/<component-package>/xxx.png')}
Conclusion 25
Conclusion
At this point we have enough knowledge to go ahead and produce components for ZK. The next port of call for
advancing our skills is ZK’s source code itself which exposes hundreds of components.
Article Sources and Contributors 26