The Component Development Guide

Version 3.0.5 and later


Table of Contents

1. Introduction
Two Sides of a Component
View
Handle
Page and Rendering
The Non-Rendering Phases
The Rendering Phase
Trilogy of Component Development
Development of View
Development of Handle
Configuration
2. Handle
Skeletal Implementations
The org.zkoss.zk.ui.AbstractComponent Class
The org.zkoss.zk.ui.HtmlBasedComponent Class
The org.zkoss.zul.impl.XulElement Class
Communication with the Client
Response to the Client
Request to the Server
Miscellaneous Features
Drag-and-Drop, Tooltip and Context Menu
Click, Right Click and Double Click
3. View
HTML Tags
Retrieve Component Being Rendered
DSP File Location
Component Renderer
JavaScript Codes
Component Type
JavaScript File Location
Initialization and Cleanup
Initialization
Cleanup
Event Handling
ZK Callbacks
Browser Event Listeners
Communicate with the Server
Send Requests to the Server
Process Request at the Server
Process Smart Updates
Process AU Responses
JavaScript Utilities
4. Configuration
Language Addon
Location of the Language Addon
A Sample of the Language Addon
Headers of the Language Addon
Component Definitions
Language Definition
Location of the Language Definition
Headers of the Language Definition

SIMPLY RICH

ZKTM

May 2008

Potix Corporation

Revision 1

Copyright © Potix Corporation. All rights reserved.

The material in this document is for information only and is subject to change without notice. While reasonable efforts have been made to assure its accuracy, Potix Corporation assumes no liability resulting from errors or omissions in this document, or from the use of the information contained herein.

Potix Corporation may have patents, patent applications, copyright or other intellectual property rights covering the subject matter of this document. The furnishing of this document does not give you any license to these patents, copyrights or other intellectual property.

Potix Corporation reserves the right to make changes in the product design without reservation and without notification to its users.

The Potix logo and ZK are trademarks of Potix Corporation.

All other product names are trademarks, registered trademarks, or trade names of their respective owners.

1. Introduction

Welcome to ZK, the simplest way to enrich Web Applications.

The Component Development Guide describes how to develop ZK components. For ZK concepts and features refer to the Developer's Guide. For installation refer to the Quick Start Guide. For a full description of component properties and methods refer to the Developer's Reference.

In this chapter, we will introduce you the basic concepts of ZK components and component development.

Two Sides of a Component

A component consists of two parts: view and handle. They are like two sides of the same coin. The view is the visual part of a component, which is running at the browser and interacts with the user. The handle is a Java object running at the server and interacts with the application.

View

The view is the visual part of a component. It is the appearance that the user sees and interacts with. When a component is created and attached to a page, its view is created at the client to provide the visual appearance.

For example, when the application creates a button (at the server), the button-look view will be created accordingly at the client, so the user can click it.

Handle

The handle is actually a Java object running at the server. It have all states and API that the application can access. From application's viewpoint, all it knows and access is this Java object. In other words, the application doesn't communicate with the user directly via Internet. Rather, the application talks to the Java object and the Java object (by and large, the component) updates the visual part accordingly. Similarly, if an user access the visual part, it is component's job to update the Java object and notify the application (with events).

The application don't need to know anything about Ajax, Java Mobile, Google Android or Flash[1]. The client and communication technologies are encapsulated by the implementation of the component(s).

Page and Rendering

When a component is created (e.g., new Button()), it doesn't belong to any page. Furthermore, there is no view at the client if it doesn't belong to a page. It also means any update to it won't have any effect at the client.

On the other hand, once a component is attached to a page[2] (i.e., belongs to a page), any update to it will cse the view to change accordingly[3].

To maximize the performance, ZK does not update the client immediately. Rather, it accumulated all updates, optimize them, and then send a minimal collection of updates to the client at the final phase of the processing. The final phase is called the Rendering phase.

From application's viewpoint, there are four different phases[4]. However, from component's viewpoint, all other phases are the same, so you might say there are the rendering phase and the non-rendering phases.

The Non-Rendering Phases

In the phases other than Rendering, the application and ZK are allowed to access any methods of the component except the redraw method. It is the component's job to notify ZK if a component needs to be redrawn, or some states of the view have to change.

For example, you have a method that will cause the client to redraw the view, then you can call back the invalidate method.

public MyComp extends org.zkoss.zk.ui.AbstractComponent {
    public void setValue(String value) {    
        _value = value;        
        invalidate();        
    }    
}

The invocation of invalidate notifies that a component has to be redrawn in the Rendering phase. It doesn't redraw it immediately. Rather, ZK accumulates all these updates and processes them later in the Rendering phase.

The Rendering Phase

After all events are processed, ZK starts the rendering phase to redraw components that are invalidated in other phases. In other words, if a component's invalidate method was ever called, ZK will invoke the redraw method in the Rendering phase.

In the Rendering phase, only the redraw method is invoked and the invalidate method cannot be called – it also implies almost all methods other than redraw shall not be called.

Molds

The skeletal implementation of AbstractComponent introduces the concept of molds. A mold is a view of a component. For example, tabbox supports the default and accordion molds to provide different views for the same component.

<tabbox mold="default">
</tabbox>
<tabbox mold="accordion">
</tabbox>

To minimize the development effect, it is recommended to extend from AbstractComponent or one of its deriving classes. With AbstractComponent, you don't implement the redraw method directly. Rather, you can use your favorite Servlet technologies to generate the view, such as DSP[5] and JSP.

Trilogy of Component Development

From development perspectives, there are three steps to implement a ZK component. First, you have to design the view. Second, you have to implement the handle (as a Java class) at the server. Finally, you have to configure it with a XML file, such that it is accessible to the applications.

How to implement the view depends on the client technology. For example, HTML tags and Java codes are required for the Ajax browsers, while Java MIDlet is required for Java Mobile clients. For the convenience of description, we focus on the Ajax browsers in this guide. The following illustrates the relationship among them.

Development of View

For Ajax browsers, the view of a component is basically a collection of HTML tags and, optionally, JavaScript methods. The HTML tags are the visual presentation, while JavaScript methods initialize, clean up, listen to browser events and communicate with the server.

HTML Tags

For Ajax browser, the visual presentation is implemented with a collection of HTML tags. For example, assume you want to use the HTML's BUTTON tag to represent a component, and then the visual presentation might look as follows.

<BUTTON id="z_ed_0" z.type="mycomps.MyButton">I am a button</BUTTON>

where z_ed_0 is the component's UUID (which is assigned by ZK Loader), and z.type is a special attribute used to indicate the type of the component.

The visual presentation are running at and interpreted by the browser. However, they are generated at the server, when the redraw method is called. Everything written to the writer argument passed to the redraw method will be sent to the client.

Instead of implementing redraw, you can extend from AbstractComponent and provide a or its deriving classes), and not to implement redraw directly. Rather, implement the so-called mold with your favorite Servlet technology, such as DSP, JSP, and so on. For convenience of description, we will use DSP mostly in this guide.

In addition to Java Servlet technologies, you can implement it with a so-called component renderer (org.zkoss.zk.ui.util.ComponentRenderer) instead. It is a bit harder to read but with better performance. We will talk more about it later.

JavaScript Methods

Except implementing a very simple component, you usually need to provide some JavaScript codes to interact with the user, to manipulate HTML tags, and to communicate with the server. The JavaScript codes depending on their purpose can be grouped into separate methods. For example, assume you want to register an event listener when the component is initialized at the browser, and then the JavaScript codes might look as follows.

zkMyButton.init = function (cmp) {
    zk.listen(cmp, "click", function (evt) {/*my listener*/});    
};

where the method name is determined by the component type and when to call. In this example, the button's z.type is mycomps.MyButton (specified in the z.type attribute as described above). It means the JavaScript codes are located in a JavaScript file called, /web/js/mycomps.js. (and it must be locatable by the class loader), and the component type is MyButton.

Since we want it to run during initialization, the method name shall be zkMyButton.init (= "zk" + "Type" + ".when"). Similarly, If you want a method to be called when the browser is resized, the method name will be zkMyButton.onSize. We will talk more about it later.

Development of Handle

The handle is the states and API of the component that Web applications access. More precisely, it is a Java class that implements the org.zkoss.zk.ui.Component interface.

You generally don't implement this interface directly. Rather, you, depending on the requrirement, extend from one of the existent classes, such as org.zkoss.zk.ui.AbstractComponent, org.zkoss.zk.ui.HtmlBasedComponent, and org.zkoss.zul.XulComponent.

For example, assume we want to extend the most skeletal implementation AbstractComponent, and then the Java class might look as follows.

public MyButton extends AbstractComponent {
}

If you are extending from one of the skeletal implementations, there is basically no abstract method you have to implement other than component-specific methods.

Configuration

Once you implement the view and handle, you can specify the component in a XML file such that ZK will load it and the application can use it. The configuration file is called lang-addon.xml[6]. There are two locations you can place this file. One is under the /metainfo/zk directory that is locatable by classpath (usually part of a JAR file). The other is inside a Web application (part of a WAR file) by specifying the correct path in /WEB-INF/zk.xml[7].

Let us assume the view is /web/myaddon/button.dsp and the handle is the com.myaddon.MyButton class. Then, the configuration will look like as follows.

<language-addon>
        <addon-name>myAddon</addon-name><language-name>xul/html</language-name>        
    <component>    
        <component-name>mybutton</component-name>        
        <component-class>com.myaddon.MyButton</component-class>        
        <mold>        
            <mold-name>default</mold-name>            
            <mold-uri>~./web/myaddon/button.dsp</mold-uri>            
        </mold>        
    </component>    
</language-addon>

As shown, each lang-addon.xml must specify an unique name (addon-name) and the language it belongs to (language-name). Each component definition must specify the component name (component-name), the handle (component-class) and the view (mold). Each component can have multiple views and each view has a name (mold-name). The default mold name is default., which the mold you must have (unless you want to implement the redraw method directly).



[1] The application can have his own client codes to communicate with the client and/or user directly. It is an option.

[2] To attach a component to a page, the application can call either setPage() with a page, or setParent() with a component belonging to a page.

[3] Whether to update the view is under control of the component when it belongs to a page After all, it is a component spec.

[4] Refer to the Developer's Guide for more information

[5] DSP (Dynamical Server Page) is a template technology developed by Potix. It is similar to JSP, but it can be embedded into a JAR file and no need to be compiled to Java codes first.

[6] If you want to define a brand new language rather than adding components to an existent one, you shall use lang.xml instead.

[7] Refer to the Developer's Reference for details.

2. Handle

The handle is a Java object running at the server, with which the application access. From application's viewpoint, it is everything about a component, so, in the context of application development, we usually just call it a component rather than separating the concept of view and handle.

The handle must implement two interfaces: org.zkoss.zk.ui.Component and org.zkoss.zk.ui.sys.ComonentCtrl. Component is a collection of APIs that applications could access, while ComponentCtrl is a collection of APIs that are accessible only by ZK and component development.

However, you generally don't implement them from scratch. Rather, you pick up one of skeletal implementations or an existent component that fulfill your requirement, and then extend from them. The following section describes each of these skeletal implementations.

Skeletal Implementations

ZK provides several skeletal implementations to minimize the effort required to implement a component.

The org.zkoss.zk.ui.AbstractComponent Class

AbstractComponent is the most base skeletal implementation. Use it only if the component you implement is for non-HTML clients (such as mobile devices).

AbstractComponent implements the redraw method to support molds, annotations, ID space, page association, parent-children relationship, event listener registration, serialization, clone, and all component APIs.

The use of AbstractComponent is straightforward: just implement your own methods – no abstract method to implement at all. For example,

public class MyComp extends org.zkoss.zk.ui.AbstractComponent {
    private Object _value;    
    public Object getValue() {    
        return _value;        
    }    
    public void setValue(Object value) {    
        _value = value;        
    }    
}

In addition to Component and ComponentCtrl, AbstractComponent also provides a collection of utilities to minimize the effort of the implementation.

Utilities to Communicate with the Client

AbstractComponent provides three kinds of methods to communicate with the client: invalidates, smart updates and AU responses. Invalidates notify ZK that a component needs to be redrawn completely. Smart updates notify ZK that a property of a component has been changed. AU responses allows a handle to ask the view to do anything it wants, such as change focus, start a timer, execute a piece of JavaScript codes and so on.

public class MyComp extends org.zkoss.zk.ui.AbstractComponent {
    private String _label = "";    
    public String getLabel() {    
        return _label;        
    }    
    public void setLabel(String label) {    
        if (!label.equals(_label)) {        
            _label = label;            
            smartUpdate("label", _label);            
        }        
    }    
    public void updateContent(Object value) {    
        //whatever...        
        invalidate();        
    }    
}

The communication between the view and the handle is one of the most important topic. We will discuss it later more detailedly.

Utilities to Generate the View

Method

Description

isAsapRequired

Returns if any non-deferrable event listener is registered for the specified event.

appendAsapAttr

Detects if a non-deferrable event is registered, and appends a special attribute to denote it if true.

Extra Controls

To encapsulate API that are used only for component development, we introduce a concept called extra controls (aka., extra interfaces). If a component needs to provide some extra interfaces that will be used only for component development, it can override the newExtraCtrl method to instantiate an instance carrying the extra interfaces. For example, Slider allows the client to change the position of the slider, and then it has to implements the org.zkoss.zk.ui.ext.client.Scrollable interface. As depicted below, Slider first implements the ExtraCtrl class to implement the Scrollable interface. Then, Slider overrides newExtraCtrl to instantiate an ExtraCtrl instance.

public class Slider extends org.zkoss.zk.ui.HtmlBasedComponent {
    private int _curpos;    
protected Object newExtraCtrl() {
return new ExtraCtrl();
}
protected class ExtraCtrl extends HtmlBasedComponent.ExtraCtrl
implements Scrollable {
            public final void setCurposByClient(int curpos) {            
            _curpos = curpos;            
        }        
    }    
}

Notice that you generally have to extend from base class's extra controls, such that it inherits all extra controls. In the above case, it extends from the HtmlBasedComponent.ExtraCtrl class.

The org.zkoss.zk.ui.HtmlBasedComponent Class

If you are implementing a component for HTML-based browser, you might consider to extend from HtmlBasedComponent. It extends from AbstractComponent to provide more HTML features, such as CSS style, CSS class, width, height, drag-and-drop, tooltip text, and so on. By extending from HtmlBasedComponent, your component inherits the HTML features for free – no special initialization, implementation, or invocation is required.

In additions, it provides

Utilities to Generate the View

Name

Description

getInnerAttrsgetOuterAttrs

HtmlBasedComponent assumes the view might have nested tags – the outer and inner tags. Then, getInnerAttrs and getOuterAttrs are used to generate the attributes for the inner and outer tags correspondingly. If a component isn't implemented with the nested tags, it just concatenates the return of both methods as shown below. Refer to the next chapter for more detailed about the view.

<span id="${self.uuid}"${self.outAttrs}${self.innerAttrs}>
</span>

getRealSclassgetRealStyle

When getOuterAttrs is called, it invokes these two methods to generate the class and style attributes. By default, they delegate the invocation to getSclass and getStyle, respectively.

If you need to add a CSS style (in addition to that an user specifies by calling setStyle), you can override getRealStyle. For exmple,

    protected String getRealStyle() {return super.getRealStyle() + "padding:5px;";    
}

getAllOnClickAttrs

Used to generate attributes for onClick, onDoubleClick and onRightClick events. If the component supports these events, it shall call back this method in getOuterAttrs. Then, if the application registers a listener for them, the event will be sent back from the client automically (non-deferrable; no JavaScript codes required).

    public String getOuterAttrs() {String attrs = super.getOuterAttrs();    
        String clkattrs = getAllOnClickAttrs();rerturn clkattrs != null ? attrs + clkattrs: attrs;        
}

The org.zkoss.zul.impl.XulElement Class

XulElement is the skeletal implementation for XUL components. If you want to implement a component that will be added to the xul/html language, this class is a good starting point.

XulElement extends from HtmlBasedComonent. It provides the XUL features, such as popup, context menu, tooltip and client-side-action.

Utilities to Generate the View

Name

Description

None

None

Communication with the Client

It is the component's job to communicate between the view and the handle, while the communication is transparent to the application developers.

The communication is kind of pitch-and-catch. It usually involves the view and the handle. In this section, we focus on the handle (the server side). The view (the client side) will be described in the later chapter.

Response to the Client

There are three ways to send a response to the view (the client side): invalidates, smart updates and responses. An invalidate causes the whole view of the component to be redrawn. A smart update causes a particular attribute of the component's view to be changed. An AU response is a fully customizable response to manipulate the component's view.

Invalidates

public void invalidate();

When the invalidate method is called, the component is marked as dirty, and then the redraw method will be called to generate the whole view to client in the Rendering phase.

The typical use is to call invalidate(), when the invocation of a method changes the view.

public void setLabel(String label) {
    if (!Objects8org.zkoss.lang.Objects.equals() compares if two objects equal (even both null)..equals(_label, label)) {    
        _label = label;        
        invalidate();        
    }    
}

It is the simplest way to update the view since we don't need to write any JavaScript codes. The view is updated with the redrawn view automatically by ZK. However, if the component contains a lot of child components (such as a grid), it is costly to redraw the whole view (since it has to also redraw all child components). Thus, if the change is mirror, the performance will be better if you use smart updates or AU responses.

Note: ZK optimizes the invalidates before invoking the redraw method. For example, a component will be redrawn at most one time, even if it is invalidated multiple times, or its parent is invalidated, too.

Smart Updates

public void smartUpdate(String attrName, String value);public void smartUpdate(String attrName, DeferredValue value);public void smartUpdate(String attrName, boolean value);public void smartUpdate(String attrName, int value);

A smart update is used to modify an attribute of the view. Unlike an invalidate, it won't cause the component to be redrawn.

public void setReadonly(boolean readonly) {
    if (_readonly != readonly) {    
_readonly = readonly;
        smartUpdate("readOnly", _readonly);        
    }    
}

By default, ZK Client Engine will update the attribute of the most outer DOM element (of the view) with the specified name. For example, assume the most outer DOM element of the above example is elem, it is equivalent to invoke the following JavaScript codes at the client:

elem.readOnly = _readonly;

ZK Client Engine is smart enough to invoke the correct method for updating a field. For example, elem.className for the class attribute, elem.setAttribute() for unrecognized attributes, and so on.

In additions, you can provide your own JavaScript codes to handle smart updates at the client, if you want to handle smart updates in a component-specific way.

Note: if both smart updates and invalidates are called against the same component, the smart updates will be ignored – after all, the whole view will be redrawn.

AU Responses

public void response(String key, AuResponse[8] response);

An AU response is the lowest-level response. You can fully customize it to do whatever you want.

However, you rarely need to use AU responses directly, since invalidates and smart updates can do almost everything you want. Remember you can provide your custom JavaScript codes to handle smart updates at the client. In other words, you can use smart updates to execute something at the client; not just modify an attribute.

Unlike smart updates, an AU responses are always sent to the client even if an invalidate is called against the same component. It is one of the reason we have to use AU responses for certain circumstances.

For example, let us say we want to implement the focus feature. Since we want to change the focus to a component even if it is invalidated, an AU response is the only choice.

    public void focus() {response("focus", new AuFocus(this));}    

where AuFocus is one of built-in AU responses extending from AuResponse with predefined functionality.

Depends

We say an AU response depends on a component, if it shall not be sent to the client when the component is removed. It is controlled by the depends argument of the constructor.

In most cases, an AU response depends on a component.

Note: Unlike smart updates, the AU response is sent to the client even if the component it depends is invalidated (until the component is removed).

Built-in AU Responses

There are a lot of built-in AU responses such as AuAlert, AuInvoke, AuPrint and so on in the org.zkoss.zk.au.out package. When you think you have to develop your own AU response, check Javadoc first if any of them fulfills your requirement.

AuInvoke and AuScript are AU response used to invoke the JavaScript codes at the client. AuInoke is used to invoke a JavaScript method depending on the component's type, while AuScript invokes a piece of codes regardless if it is part of a component's view.

public void play() {
    response("play", new AuScript(this, "alert('Hi')"));    
}

We discuss how to create your custom AU response in the later chapter, since it requires some JavaScript codes.

Request to the Server

To notify what happens at the client, the view has to send an AU request (org.zkoss.zk.au.AuRequest) to the server. The AU request is processed by the so-called command. Whether to update the component and whether to post an event depends on how a command is implemented.

Command

A command (org.zkoss.zk.au.Command) is used to process certain type of AU requests. When ZK Update Engine receives an AU request, it associates a command with it. Then, it invokes the process method of the command to process it. The processing, depending on the requirement, usually updates the content of the component and then posts an event to notify the application.

For example, the org.zkoss.zk.au.in.RemoveCommand command is registered to process all AU requests called remove. Thus, if an AU request called remove is received, the process method of RemoveCommand will be invoked. Here is how RemoveCommand is implemented:

public class RemoveCommand extends Command {
    public RemoveCommand(String evtnm, int flags) {    
        super(evtnm, flags);        
    }    
    protected void process(AuRequest request) {    
        final Component comp = request.getComponent();        
        if (comp != null) comp.detach();        
    }    
}

Note: ZK uses the same command instance for the same type of AU requests, so concurrent access of the command is possible.

See Also: Refer to the Process Request at the Server section in the next chapter for more details.

Event

When a command processing a request, it might post one or several events. For example, InputCommand will post the onChange event as an instance of org.zkoss.zk.ui.event.InputEvent. The events are queued until all commands are processed. In other words, the events are processed by the application, after the components have been updated.

There is a common question for developing a component: shall I update the component's state in a command or in an event listener. In general, it is better to use event to notify the application only. And, it is better to update the component's state in a command. After all, the application might stop the event propagation at any time (Event.stopPropagation).

On the other hand, we can utilize the stop-propagation feature to allow the application to customize some behavior. For example, the list box sorts the list items in the onSort listener, such that the application can provide its own sorting by adding its own listener and stop the event propagation.

Miscellaneous Features

Drag-and-Drop, Tooltip and Context Menu

They are supported automatically with custom JavaScript codes as long as you extends your component handle from org.zkoss.zul.impl.XulElement.

Click, Right Click and Double Click

The onClick, onRightClick and onDoubleClick events are handled automatically by ZK Client Engine. All you need to do is to generate the correct attributes by use of the appendAsapAttr method.

public String getOuterAttrs() {
    final StringBuffer sb = new StringBuffer(super.getOuterAttrs());    
    appendAsapAttr(sb, Events.ON_CLICK);    
    appendAsapAttr(sb, Events.ON_DOUBLE_CLICK);    
    appendAsapAttr(sb, Events.ON_RIGHT_CLICK);    
    return sb.otString();    
}

HtmlBasedCcomponent provides an utility called getAllOnClickAttrs to simplify the job.

public String getOuterAttrs() {
    String attrs = super.getOuterAttrs();    
    String clkattrs = getAllOnClickAttrs();    
    rerturn clkattrs != null ? attrs + clkattrs: attrs;    
}


[8] org.zkoss.zk.au.AuResponse

3. View

The view is the visual presentation of a component at the client. Depending on the client, it could be HTML tags plus JavaScript codes, MIDlet, XML node and Flash. For sake of description, we discuss only the view for the Ajax browsers, i.e., HTML tags plus JavaScript codes.

HTML Tags

You can choose whatever Servlet technologies you prefer to generate HTML tags. ZK (more precisely, AbstractComponent) uses the include method of javax.servlet.RequestDispatcher to include the HTML tags you generate for the view.

For sake of description, we use DSP (Dynamical Server Page) in most examples. It is a template technology developed by ZK. The use is similar to JSP except you cannot embed Java codes in it. There are several benefits, such as no compilation required, ability to be part of JAR file, no JSP/EL requirement and so on.

Retrieve Component Being Rendered

When the view being rendered, the related information is passed through the request attribute called arg, which is a Map instance. In arg, the component being rendered is stored in the entry called self. That is, you can retrieve it with the following EL expression:

${requestScope.arg.self}

With Java codes, you can retrieve it as follows.

void doGet(HttpServletRequest request, HttpServletResponse response) {
    Component self = (Component)((Map)request.getAttribute("arg")).get("self");    
...

Here is the example of org.zkoss.zul.Image's view (in DSP):

<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<img id="${self.uuid}" z.type="zul.widget.Img"${self.outerAttrs}${self.innerAttrs}/>

where

  • You have to generate the id attribute with component's UUID.

  • Generate the required attributes by calling the getOuterAttrs and getInnerAttrs methods of HtmlBasedComponent.

  • If the view requires some specific JavaScript codes, the z.type attribute might be specified with an unique value to denote the component type.

Notice that each view must have exactly one topmost HTML element – which is img in the above case. The following view is incorrect since it has two topmost element.

<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<img id="${self.uuid}" ${self.outerAttrs}${self.innerAttrs}/>
<img/>

To correct it, you can enclose them with span or div.

<span id="${self.uuid}" ${self.outerAttrs}${self.innerAttrs}/>
    <img/>    
    <img/>    
</span>

DSP File Location

Depending on your requirement, you can place the DSP file representing a view in the Web application, or in a JAR library.

If you put in the Web application, say, /WEB-INF/myaddon/button.dsp, just specify the correct path in the mold-uri element as follows.

<component>
    <mold>    
        <mold-name>default</mold-name>        
        <mold-uri>/WEB-INF/myaddon/button.dsp</mold-uri>        
    </mold>    
...

If you want to put it to a JAR file, you must place it under the /web directory. For example, /web/myaddon/button.dsp is a legal path, and the mold URI is as follows.

<component>
    <mold>    
        <mold-name>default</mold-name>        
        <mold-uri>~./myaddon/button.dsp</mold-uri>        
</mold>
...

where "~." means the /web directory in a JAR file, or locatable by classpath.

Component Renderer

DSP is an easy way to design a component view, but the performance of EL expressions is much slower than native Java codes. To maximize the performance with native Java codes, you can develop either a Servlet or a so-called component renderer.

A component renderer is a Java class used to render a view directly. It must implement the org.zkoss.zk.ui.render.ComponentRenderer interface, which has only one method: render. For example, the following is the renderer of Image:

public class ImageDefault implements ComponentRenderer {
    public void render(Component comp, Writer out) throws IOException {    
        final Image self = (Image)comp;        
        out.write("<img id=\"");        
        out.write(self.getUuid());        
        out.write("\" z.type=\"zul.widget.Img\"");        
        out.write(self.getOuterAttrs());        
        out.write(self.getInnerAttrs());        
        out.write("/>");        
    }    
}

Then, the mold URI must start with "class:" and end with the class name, as shown below.

<component>
    <mold>    
        <mold-name>default</mold-name>        
        <mold-uri>class:org.zkoss.zkmax.zul.render.ImageDefault</mold-uri>        
</mold>
...

JavaScript Codes

In addition to the visual representation, a component view usually has some codes running at the client to, say, initialize the view and interact with the user. For Ajax devices, the client codes are written in JavaScript. For sake of convenience, we discuss only Ajax devices here.

Component Type

Each component that requires the JavaScript codes to run at the client must provide an unique type in the z.type attribute. For example,

<BUTTON id="z_ed_0" z.type="mycomps.MyButton">I am a button</BUTTON>

The syntax is

dir1.dir2.file.Type

The part after the last dot (.) is the name of the type. The part before the last dot is the path of the JavaScript file that must be loaded (to handle the view). ZK use the type as a naming convention to call the proper JavaScript methods. For example, assume the type is Tx. Then, if the zkT.init method is declared, it will be called when ZK is initializing the component at the client.

ZkTp = {}
zkTp.init = function (cmp) {
zk.listen(cmp, "onClick", zkTp.onclick);
}
zkTp.onclick = function (evt) {
var el = Event.element(evt);
...
};

JavaScript File Location

As described in the previous section. the first part of the z.type attribute is the path of the JavaScript to load. For example, assume the z.type attribute is a.b.c.Tx, then ZK will load the file in the path /web/js/a/b/c.js, right before initializing the view.

Notice that the path must be locatable by classpath, and it always stats at /web/js.

Initialization and Cleanup

Initialization

When ZK Client Engine initializes a component view, it first check if the z.type attribute is defined. If not specified, it means no JavaScript codes required and it ends the initialization of this component.

If z.type is specified, say, widget.SuperButton, ZK Client Engine first loads the JavaScript file, /web/js/widget.js, and then check if any method called zkSuperButton.init. Invoke it if found. We call this method as the init callback.

A component view usually registered event listeners in this method. For example,

zkSuperButton = {} //declare zkSuperButton to hold init, cleanup and other methods

zkSuperButton.init = function (cmp) {
zk.listen(cmp, "focus", zkau.onfocus);
zk.listen(cmp, "blur", zkau.onblur);
};

The initialization order is starting from the child, then to the parent, to the parent's parent, and so on.

After init is called, it invokes zkSuperButton.beforeSize and zkSuperButton.onSize, if any, to initialize the size of the view. Components rarely need to provide these two methods, unless they are providing the layout, such as Hbox and Borderlayout. We will talk more about them in the ZK Callbacks section.

Cleanup

When a component is about to be removed, ZK will check if the cleanup callback exists (i.e., zkSuperButton.cleanup in the above example). Invoke it if found.

The order of cleanups is starting from the topmost component, then to its child, to its child's child and so on.

Tip: You don't need to unregister the event listeners you registered in the init callback. ZK Client Engine will unregister them automatically.

Event Handling

There are two types of event handling. One is ZK callbacks, such as the init and cleanup callback mentioned in the previous section. The other is the browser event listeners.

ZK Callbacks

Like the browser event listeners, ZK callbacks are used to handle a situation, such as when the parent's is resized, or when a view becomes visible. Unlike the browser event listeners, they are called by ZK Client Engine directly and registered by use of name convention. In other words, once you declare the method, it will be called automatically.

Tip: ZK scans any callback to register after invoking the init callback, so you can declare them in the init callback.

zkType.init = function (cmp) {zkType.onSize = function (cmp) {};};

The init Callback

zkType.init = function (cmp) {
};

Called when a component is attached to a page or redrawn (due to being invalidated).

The order of invocations is the child first (like Java constructor). The child without a parent is called first, then its child, then its grandchild, and so on.

The cleanup Callback

zkType.cleanup = function (cmp) {
};

Called when a component is detached, or will be redrawn (due to being invalidated).

When the invalidate method is called at the server, ZK Client Engine will first call the cleanup callback, replace the DOM tree, and then call the init callback.

The order of invocations is the parent first. The topmost parent being cleanup is called first, then its child, then its grandchild, and so on.

The beforeSize and onSize Callback[9]

[10]
zkType.beforeSize = function (cmp) {
};
zkType.onSize = function (cmp) {
};

When the browser window or a resizable parent (such as Borderlayout) is resized, ZK Client Engine first call the beforeSize callback for all components that are affected, and then call the onSize callback for them. In other words, it is a two-pass mechanism. The beforSize callback is used to do some preparation. For example, you might have to reset style.width in the beforeSize callback and then set the correct size in the onSize callback.

The order of invocations is the parent first. The topmost parent that is affected is called first, then its child, then its grandchild, and so on.

Notes

  1. The beforeSize and onSize callbacks are also called when initializing a component, so you don't and shalln't handle the size in the init callback.

  2. The browser doesn't notify JavaScript codes when a DOM element is resized, so these two callbacks are actually triggered by the invocation of the zk.beforeSizeAt and zk.onSizeAt methods (by a layout component).

The onVisi Callback

zkType.onVisi = function (cmp) {
};

Called right after a component has become visible.

The order of invocations is the parent first. The topmost parent becoming visible is called first, then its child, then its grandchild, and so on.

Note

  1. The browser doesn't notify JavaScript codes when a DOM element becomes visible or invisible, so it won't be called if you set display="none" directly. On the hand, it is called automatically by ZK Client Engine if the visiblity is changed due to the visible property is changed at the server, or due to the invocation of zk.show or action.show at the client.

The onHide Callback

zkType.onHide = function (cmp) {
};

Called right before a component becomes invisible. Notice that when this method is called, the component is still visible.

The order of invocations is the parent first. The topmost parent becoming invisible is called first, then its child, then its grandchild, and so on.

The onScroll Callback[11]

[12]
zkType.onScroll = function (cmp) {
};

Called when the user is scrolling the browser window or one of its ancestor component.

The order of invocations is the parent first. The topmost parent affected by scrolling is called first, then its child, then its grandchild, and so on.

Notes

  1. The browser doesn't notify JavaScript codes when the user is scrolling a DOM element (rather than the browser window). It is up to a component view decide whether to invoke zk.onScrollAt.

Browser Event Listeners

You can listen to whatever browser events you care like any other JavaScript codes. However, ZK provide two methods to simplify the job: zk.listen and zk.unlisten. They are not only compatible with all kind of browsers, but also unlisten automatically – so no worry of memory leak. In fact, you rarely need to invoke zk.unlisten.

zk.listen(cmp, "click", function () {zkCkbox.onclick(cmp);});

Communicate with the Server

Send Requests to the Server

To notify what happens at the client, the view has to send an AU request to the server. At the server the AU request is encapulated as an instance of the org.zkoss.zk.au.AuRequest class. At the client side, the AU request is an object with uuid, cmd, data and other properties. For example,

{uuid: cmp.id, cmd: "onClick", data: [10, 20], ctrl: true}

AU Request

The following is a list of the properties that an AU request might have.

Property

Description

uuid

[Optional but exactly one of uuid and dtid must be specified]

The UUID of the component that shall receive this request. It is also known as the targeted component.

dtid

[Optional but exactly one of uuid and dtid must be specified]

The desktop ID that shall receive this request.

cmd

[Required]

The command ID, such as onClick and onSelect. It is used to search for the command. ZK Update Engine first invokes the getCommand method of the targeted component to look for the component-specific command. If not found, it then invokes the static getCommand method of the AuRequest class to look for the global command.

data

[Optional; Default: null]

An array of data to pass to the server. It depends on the command. For example, the onClick command accepts a two-element array to indicate the position of the mouse pointer.

ctl

[Optional; Default: false]

Whether the request is a control command, such as onClick, onOK and so on. To avoid re-execute the long operation twice when an impatient user clicks a button repeatedly, ZK ignores the second control command if two consecutive control commands target the same component and the first one is still in execution.

If any doubt, don't specify it (with true).

ignorable

[Optional, Default: false]

Whether the request can be ignored if the system is busy or it causes an error. Typical example is the onChanging and onScrolling commands. It is too annoying if not to set ignorable to true.

Basic Utilities to Send Requests

zkau.sendasap and zkau.send are two of most common methods to send an AU request to the server.

zkau.send(evt, timeout)

Sends an AU request after the specified milliseconds expires. If there are any pending AU requests, this request will be appended to the end.

evt

The AU request to send.

timeout

The delay before sending the request (unit: milliseconds). If not specified, it is delayed for 0 milliseconds (i.e., calling setTimeout with 0). If a negative value is specified, it means it is a deferrable command and it won't be sent back to the server until another invocation of zkau.send with a non-negative timeout.

To have better performance, it is better to specify -1 if no non-deferrable event listener is registered for this command. It is easy to do this by use of zkau.asapTimeout as shown below.

zkau.send({uuid: cmp.id, cmd: "onChange", data: [cmp.value]},
zkau.asapTimeout(cmp, "onChange"));

zkau.sendasap(evt, timeout)

Sends an AU request by checking if there is a non-deferrable event listener registered for it. It is a shortcut of the following.

zkau.send(evt, zkau.asapTimeout(evt.uuid, evt.cmd, timeout));

evt

The AU request to send.

timeout

The delay before sending the request (unit: milliseconds) if non-deferrable.

Notice that it is the timeout used only if there is a non-deferrable event listener registered for it.

zkau.sendAhead(evt, timeout)

Sends an AU request by placing it in front of any other pending AU requests.

evt

The AU request to send.

timeout

The delay before sending the request (unit: milliseconds). If not specified, it is delayed for 0 milliseconds (i.e., calling setTimeout with 0). If a negative value is specified, it means it is a deferrable command and it won't be sent back to the server until another invocation of zkau.send with a non-negative timeout.

Callbacks

There are two callbacks you can register to do something before sending AU requests, and after receiving the responses.

zkau.addOnSend(func);zkau.removeOnSend(func);

Register a callback function that will be called before AU requests are sending to the server.

It is useful if you are using timer or other mechanism to monitor the DOM element's value. FCKeditor is a typical example. By using the onSend callback, we can check the value again and send back the correct value if changed with other AU requests.

zkau.addOnSend(function() {
//check any change and call zkau.sendAhead to carry the change back
});

zkau.addOnResponse(script)

Register a callback function that will be called after processing all responses.

script

It could be a function or a string containing JavaScript codes.

More Utilities to Send Requests

Here is a list of methods to simply the sending of particular AU requests, such as onClose. They are all based on zkau.send.

zkau.sendRemove(uuid)

Sends the remove command to remove the specified component (at the server).

zkau.sendOnMove

Sends the onMove command to denote a component is moved.

zkau.sendOnSize

Sends the onSize command to denote a component's size is changed.

zkau.sendOnZIndex

Sends the onZIndex command to denote the Z-index of a component is changed.

zkau.sendOnClose

Sends the onClose command that causes the onClose event being sent to the component. How the onClose event is handled depends on the component. For window, it is detached.

Process Request at the Server

When an AU request arrives at the server, it is packed as an instance of org.zkoss.zk.au.AuRequest by associating with a command (org.zkoss.zk.au.Command). The command is responsible to process the associated request. ZK Update Engine doesn't assume anything but invoking the command's process method. Up to the component developer, the command might then update the component's state, post an event or others when process is invoked.

Global and Component-specific Commands

There are two kinds of commands: global commands and component-specific commands. The global commands are available to all kind of components, while component-specific commands are available only to a particular kind of components. More precisely, global commands are maintained in a global map, while component-specific commands is maintained in an individual component.

How to Identify a Command

ZK takes two steps to look for the command for an AU request:

  1. ZK first check whether any component-specific command is defined for the command ID of the AU request. This is done by invoking the getCommand method of the ComponentCtrl interface.

  2. Then, ZK check whether any global command is defined for the command ID of the AU request, if no component-specific command is found. This is done by invoking the static method called getCommand of the AuRequest class.

Built-in Commands

There are a lot of built-in AU responses such as InputCommand, MouseCommand, KeyCommand and so on in the org.zkoss.zk.au.in package. All of them are global commands. In other words, they are available to all components.

To minimize the development effort, use the built-in commands if possible.

Develop Your Own Command

If none of built-in command fulfills your requirement, you can develop your own command.

Develop Your Own Component-specific Command

First, you have to decide if the command is available to two or more different kinds of components. If it is unique to particular kind of components, you shall develop a component-specific command:

  1. Extend from ComponentCommand and implement the process method.

  2. Return the command when the getCommand method of the component is called.

For example,

public class SuperButton extends AbstractComponent {
    private static Command _flycmd = new ComponentCommand("onFly", 0) {    
        protected void process(AuRequest request) {        
            final SuperButton btn = (SuperButton)request.getComponent();            
            btn.doFly();            
                    Events.postEvent(new Event(getId(), btn, request.getData()));}                    
    }; //note: it won't register itself to the global map    
        //because it is extended from ComponentCommand)        
    public Command getCommand(String cmdId) {    
        return "fly".equals(cmdId) ? _flycmd: null;        
    }    
    void doFly() {    
        //whatever        
    }    
}
Develop Your Own Global Command

If the command is available to two or more other kinds of components, you might consider to implement a global command[13]. Since it is available to all components, make sure the naming doesn't conflict with others.

A global command is extended from the org.zkoss.zk.au.Command class.

class FlyCommand extends Command {
public FlyCommand(String id, int flags) {
super(id, flags);
}
protected process(AuRequest request) {
final SuperButton btn = (SuperButton)request.getComponent();
        btn.doFly();        
Events.postEvent(new Event(getId(), btn, request.getData()));
}
}

Since it is global, you have to instantiate an instance at startup. A typical usage is to declare a static instance of a class. For example,

public class SuperButton extends AbstractComponent {
    static {    
            new FlyCommand("onFly", 0); //register itself automatically}            
...

It is interesting to note you don't need to keep a reference to a global command. It is registered automatically as soon as it is instantiated.

Also notice you must instantiate them in a class that will be loaded – the component's class is always loaded (if it is registered in lang.xml or lang-addon.xml).

The getExtraCtrl Method

When implementing a command, you can call any method you want. However, ZK introduces a special method to encapsulate the implementation to be invisible to application developers. This method is called getExtraCtrl (in the ComponentCtrl interface) – aka., extra control.

public Object getExtraCtrl();

Note: You don't have to follow this. First, it is simply a design pattern we use to implement components. Second, command related methods can be encapsulated well if you use only component-specific commands.

Here is how it use. Let us assume you don't want doFly to be accessed by applications. The easiest way is to declare it as private or protected and then implement it as a component-specific command as we did in the Develop Your Own Component-specific Command section.

However, what if FlyCommand has to be a global command? First, you can design an interface, say, Flyable.

public interface Flyable {
public void doFly();
}

Then, in the component implementation, we can do as follows.

public class SuperButton extends AbstractComponent {
    public Object newExtraCtrl() {    
        return new ExtraCtrl();        
    }    
    private void doFly() { //make it inaccessible    
        //whatever        
    }    
    private class ExtraCtrl implements Flyable {    
public void doFly() {
SuperButto.this.doFly();
}
}
...

where we override newExtraCtrl rather than getExtraCtrl, since getExtraCtrl handles the lifecycle of the extra control and will invoke newExtraCtrl to instantiate the object.

Finally, you can implement FlyCommand this way:

class FlyCommand extends Command {
public FlyCommand(String id, int flags) {
super(id, flags);
}
protected process(AuRequest request) {
final Component comp = request.getComponent();
        if (comp instanceof Flyable)        
((Flyable)comp).doFly();
Events.postEvent(new Event(getId(), comp, request.getData()));
}
}

Process Smart Updates

Handling the smart updates sent from the server is straightforward. Just declare a callback called setAttr. For example, assume the component type is SuperButton, then the setAttr callback is as follows.

zkSuperButton.setAttr = function (cmp, name, value) {
if ("fly" == name) {
//whatever
}
return false;
}

The return value is whether you want the default behavior to take place, i.e., whether to update the attribute. More precisely, zkau.setAttr(cmp, name, value) will be called if false is returned.

If you prefer to update the attribute first, you can invoke zkau.setAttr manually. For example,

zkSuperButton.setAttr = function (cmp, name, value) {
if ("disabled" == name) {
zkau.setAttr(cmp, name, value); //update cmp.disabled first
//whatever
return true; //attribute has been updated
}
return false;
}

Smart Updates with Multiple Values[14]

[15]

When implementing a sophisticated component, you might want to pass multiple values (i.e., an array of values) to an attribute. At the server side, you invoke the smartUpdateValues method. At the client, the setAttr callback will be called with additional arguments. For example, at the server, you invoke

smartUpdateValues("fly", new String[] {"low", "fast"});

Then, at the client, you can handle it as follows.

zkSuperButton.setAttr = function (cmp, name, value1, value2) {
if ("fly" == name) {
fly(value1, value2);
...

JavaScript Tip: You can use arguments.length to determine how many arguments are passed.

Process AU Responses

There are many built-in responses you can use (in the org.zkoss.zk.au.out package). However, the invoke (AuInvoke) and script (AuScript) responses are almost all you need.

The invoke Response

The invoke response is used to invoke a callback. For example, you can send an AU response at the server as follows.

    public void fly() {response("ctrl", new AuInvoke(this, "fly", "low", "fast"));}    

Then, at the client, the following callback will be called.

    zkSuperButton.fly = function (cmp, height, speed) {//whatever    
}

Tip: As shown above, we can use a AU response and a smart update to do the similar task (fly in this example). But, there is a difference: the AU response is sent even if the component is invalidated, while the smart update won't.

Thus, if the init callback will handle everything, the smart update shall be used (otherwise, you will do it twice when it is invalidated).

The script Response

The script response is used to execute any JavaScript codes at the client, not just the callbacks. For example,

public void fly() {
    response("ctrl",    
new AuScript(this, "zkSuperButton.fly('" + getUuid() + "','low','fast')"));
}

Since the JavaScript codes are hardcoded, it is sometimes not easy to support multiple molds. For example, assume you want to use the same Java class to implement another mold. Then, the JavaScript codes that implements fly might be different. So, you might have to do as follows.

public void fly() {
    response("ctrl",    
new AuScript(this,
("SuperButton".equals(getMold()) ? "zkSuperButton": "zkGiantButton")
+ ".fly('" + getUuid() + "','low','fast')"));
}

On the other hand, if you use the invoke response, you can have the same response but define a different component type, say GiantButton. Then, you don't modify Java class. Rather, provide another callback:

zkGiantButton.fly = function (cmp, height, speed) {
    //whatever    
}

In general, the invoke response is recommended if applicable.

Develop Your Own AU Responses

Unlike the AU request's commands, you rarely need to develop your own AU response since the invoke response can serve almost everything. However, if you prefer to provide your own response, you can follow the instructions described in this section.

First, you have to implement a Java class extending from the org.zkoss.zk.au.AuResponse class.

public AuFly extends AuResponse {
    public AuFly(SuperButton comp, String height, String speed) {    
super("fly", comp, new String[] {comp.getUuid(), height, speed});
}
}

where "fly" is the response name, and the third argument (String[]) of AuResponse's constructor will be passed to the client. The response name must be unique. The third argument is called the response data.

Then, at the client side, you shall provide a method called zkau.cmd1.fly to handle it.

zkau.cmd1.fly = function (uuid, cmp, height, speed) {
//whatever
}

There are two set of response handlers: zkau.cmd0 and zkau.cmd1. zkau.cmd0 is used to handle responses that are applied to the whole browser window, while zkau.cmd1 is for individual component.

If zkau.cmd1 is overridden, the first element of the response data must be the component UUID (comp.getUuid() in the above example). Then, ZK Client Engine will convert it to a reference to a component, and then invoke the response's handler with UUID and the reference as the first two arguments (refer to zkau.cmd.fly in the above example).

JavaScript Utilities

To be added.



[9] Available in ZK 3.0.5 or later.

[10] Available in ZK 3.0.5 or later.

[11] Available in ZK 3.0.5 or later.

[12] Available in ZK 3.0.5 or later.

[13] Of course, you can still implement as component-specific – just return it in several implementations of the getCommand method.

[14] Available in ZK 3.0.5 or later.

[15] Available in ZK 3.0.5 or later.

4. Configuration

The configuration provides the component definitions that can be accessed by ZK applications. There are two types of configuration: the language definition and the language addon. The language definition is used to define a new language (such as xul/html, xhtml, and xml), while the language addon enhances an existent language. The syntax are very similar and we will discuss the language addon first.

Language Addon

The language addon is a XML file providing the component definitions and other language features.

Location of the Language Addon

Location of the language addon depends on how you pack your component definitions: JAR or WAR.

Location in a JAR File

If you pack the component definition(s) in a JAR file, the language addon must be named lang-addon.xml and placed at the /metainfo/zk directory accessible by the classpath.

Location in a WAR File

If you pack the component definition(s) in a WAR file, you can specify the path of the XML file in /WEB-INF/zk.xml. For example, let us assume the path of the XML file is /WEB-INF/zk/lang-addon.xml, then specify the following in /WEB-INF/zk.xml. You can specify multiple language addons.

<language-config>
    <addon-uri>/WEB-INF/zk/lang-addon.xml</addon-uri>    
</language-config>

A Sample of the Language Addon

Here is a sample of the language addon (ZK FCKeditor).

<language-addon>
    <addon-name>fckez</addon-name>    

    <version>    
        <version-class>org.zkforge.fckez.Version</version-class>        
        <version-uid>2.5.1_1</version-uid>        
        <zk-version>2.4.0</zk-version><!-- or later →        
    </version>    

    <language-name>xul/html</language-name>    
        
    <javascript-module name="fckez.fckez" version="2.5.1_1"/>    
        
    <zscript>    
    import org.zkforge.fckez.*;    
    </zscript>    

    <component>    
        <component-name>fckeditor</component-name>        
        <component-class>org.zkforge.fckez.FCKeditor</component-class>        
        <mold>        
            <mold-name>default</mold-name>            
            <mold-uri>~./fckez/fckeditor.dsp</mold-uri>            
        </mold>        
    </component>    
</language-addon>

Headers of the Language Addon

The headers of the language addon specifies the name of the addon, the language to addon, and so on.

The addon-name Element

[Required]

<addon-name>name</addon-name>

Specifies the unique name of the language addon.

The depends Element

[Optional]

<depends>another1, another2</depends>

Specifies a list of the names of other addons, which this addon depends on. If specified, this addon won't be loaded until all depended addons are loaded. If not specified, the loading sequence is unpredictable.

The language-name Element

[Required]

<language-name>xul/html</language-name>

Specified the language that this addon shall be added to.

The verson Element

[Optional[16]]

<version>
    <version-class>org.zkforge.fckez.Version</version-class>    
    <version-uid>2.5.1_1</version-uid>    
    <zk-version>2.4.0</zk-version><!-- or later →    
</version>

There are two parts: addon version and ZK version. Addon version is specified in version-uid element. In certain environments, it is possible to have several versions of JAR file containing the same set of component definitions[17]. To ensure the correct XML file is loaded, you have to specify a class carrying the version. The class must have a static data member called UID as shown below.

public class Version {
    public static final String UID = "2.5.1_1";    
}

Then, ZK will compare UID with the value specified in version-uid, and the addon is ignored if not matched – it means the XML file is not from the same JAR file being loaded.

ZK Version that this addon requires is specified in the zk-version element. The addon is ignored if ZK installed is an older version.

The javascript-module Element

[Optional]

<javascript-module name="fckez.fckez" version="2.5.1_1"/>

Specifies the version of JavaScript codes that will run at the client. If specified, ZK will mangle the URL of JavaScript files with the version, such that the browsers won't use the wrong cached version. It is recommended to change the version each time you deliver a new version of your components (so JavaScript files with the most update version will be loaded by the browser).

Component Definitions

There are two kinds components: macro and primitive components. By macro we mean to implement a component based on a ZUML page. By primitive we mean to implement the Component interface (actually extending from AbstractComponent, HtmlBasedComponent and others). This guide focuses on the implementation of primitive components. For implementation of macro components, refer to the Developer's Guide.

Define a Primitive Component

<component>
    <component-name>fckeditor</component-name>    
    <component-class>org.zkforge.fckez.FCKeditor</component-class>    
    <mold>    
        <mold-name>default</mold-name>        
        <mold-uri>~./fckez/fckeditor.dsp</mold-uri>        
    </mold>    
</component>
The component-class Element

[Required]

Specifies the implementation class of this kind of components.

The component-name Element

[Required]

Specifies the component name. If an existent component is defined with the same name, the existent component is completely invisible in this page.

The mold Element

[Optional]

<mold>
    <mold-name>default</mold-name>    
    <mold-uri>~./fckez/fckeditor.dsp</mold-uri>    
</mold>

Specifies a mold. You can have any number of molds for one component definition.

The mold-name Element

[Required if mold is specified]

Specified the mold name. The default mold shall be named as default.

The mold-uri Element

[Required if mold is specified][EL is allowed]

Specifies the mold URI.

The custom-attribute Element

[Optional]

<custom-attribute>
    <attribute-name>my.attr</attribute-name>    
    <attribute-value>my value</attribute-value>    
</custom-attribute>

Specifies an attribute in the component scope. In other words, the setAttribute method of the Component interface will be called to store the attribute.

The property Element

[Optional]

<property>
    <property-name>border</property-name>    
    <property-value>blue</property-value>    
</property>

Specifies a property to initialize. For example, assume the property name is border, then the setBorder method will be called to set the value specified in property-value.

The annotation Element

[Optional]

<annotation>
    <annotation-name></annotation-name>    
    <property-name></property-name>    
    <attribute>    
        <attribute-name></attribute-name>        
        <attribute-value></attribute-value>        
    </attribute>    
    <attribute>    
        <attribute-name></attribute-name>        
        <attribute-value></attribute-value>        
    </attribute>    
</annotation>

Specifies an annotation to the component definition. It can be accessed by the getAnnotationMap method of the ComponentDefinition interface.

Define a Macro Component

<component>
    <component-name>mycomp</component-name>    
    <component-class>com.mine.Mycomp</component-class>    
    <macro-uri>~./mine/mycomp.zul</macro-uri>    
</component>

The syntax of a macro definition is similar to the definition of the primitive component, except

  • mold is not allowed

  • macro-uri is required to specify the template (a ZUML file) used to generate the macro component

The macro-uri Element

[Required][EL is not allowed]

Specifies the URI of the ZUML page, which is used as the template to create the macro component.

The inline Element

[Optional][Default: false][EL is not allowed]

<inline>true</inline>

Specifies whether this is an inline macro component.

Extend from Existent Component

<component>
    <component-name>funnybutton</component-name>    
    <extends>button</extends>    
</component>

In addition to define a new component, you can extend from the existent one. By extending, it inherits all properties such the as component class, molds and so on. You can override any element you want. The syntax is the same, but they are optional except the component-name and extends element.

extends

[Required]

Specifies the name of the component definition to extend from (aka., extendee). If specified, the extendee's definition will be loaded to initialize the new component definition. In other words, it extends the existent definition instead of defining a brand-new one.

Language Definition

The language definition is a XML file definition a new language, which includes component definitions and other language features.

The syntax of the language definition is similar to the language addon except the location and the headers are a bit different.

Location of the Language Definition

The language definition must be named lang.xml and placed at the /metainfo/zk directory accessible by the classpath.

Headers of the Language Definition

The language-name Element

[Required]

<language-name>xul/html</language-name>

Specified the language name that uniquely identifies this langauge.

The device-type Element

[Required]

<<device-type>ajax</device-type>

Specified the device type, such as ajax, xml, mil and hil.

The namespace Element

[Required]

<namespace>http://www.zkoss.org/2005/zul</namespace>

Specified the namespace that also uniquely identifies this language.

The extension Element

[Required; Multiple]

<extension>zul</extension>
<extension>xul</extension>

Specified the file extension that shall be considered as using this language. For example, if zul is specified, this language will be used as the default language for all files with the .zul extension. Of course, the file can change the language later with the page directive.

The verson Element

[Optional[18]]

<version>
    <version-class>org.zkoss.zul.Version</version-class>    
    <version-uid>3.0.5</version-uid>    
    <zk-version>3.0.0</zk-version><!-- or later →    
</version>

There are two parts: language version and ZK version. Language version is specified in version-uid element. In certain environments, it is possible to have several versions of JAR file containing the same set of component definitions[19]. To ensure the correct XML file is loaded, you have to specify a class carrying the version. The class must have a static data member called UID as shown below.

public class Version {
    public static final String UID = "3.0.5";    
}

Then, ZK will compare UID with the value specified in version-uid, and the language is ignored if not matched – it means the XML file is not from the same JAR file being loaded.

ZK Version that this language requires is specified in the zk-version element. The language is ignored if ZK installed is an older version.



[16] It is required for ZK 3.0.4 and prior.

[17] For example, you might have an older version in WEB-INF/lib, and a newer version in shared/lib.

[18] It is required for ZK 3.0.4 and prior.

[19] For example, you might have an older version in WEB-INF/lib, and a newer version in shared/lib.