Component Based UI

From Documentation
Revision as of 01:52, 1 November 2010 by Sphota (talk | contribs)

Stop.png This article is out of date, please refer to http://books.zkoss.org/zkessentials-book/master/ for more up to date information.



In ZK, we work with the UI components to assemble together our application GUI. We could declare components using markup language, or Java. ZKEssentials Intro Hello.png

Here we declared a Window component, enabled the border (border="normal"), and set its width to a definite 250 pixels. Enclosed in the Window are two Button components.

Where We Declare the Components

The components are declared in files with the extension ".zul". A ZUL page is interpreted dynamically at the server; we could think of it as a JSP empowered with Ajax capabilities. ZK components could be declared entirely in Java also, please refer Richlets

How We Declare the Components

The language we use to declare the components is ZUML, an abbreviation for ZK UI Markup Language. ZUML follows the syntax of XML. Here are a couple of basic quick notes if you're not familiar with XML[1].

  • Elements must be well formed
    • close declaration with an end tag:
<window></window>
    • close declaration without an end tag (equivalent to the above statement):
<window/>
  • Elements must be properly nested:
    • Correct:
    <window>
	<groupbox>
		Hello World!
	</groupbox>
    </window>
    • Wrong:
    <window>
	<groupbox>
		Hello World!
	</window>
     </groupbox>
  • Only a single "root" component is allowed:
    • one root - legal
    <button />
    • two roots - illegal
    <button/>
    <button/>
    • one root containing all other components - legal
    <window>
	<button/>
        <button/>
    </window>
  • Attribute value must be quoted
    • Correct:
    <window width="600px"/>
    • Incorrect:
    <window width=600px/>

Using XML tags, we declare a component and set a component's attributes; as an alternative to coding in Java files, we could set a component's attributes to initialize values, evaluate conditions/expressions, and handle events. The figure belows shows an example of how we could easily dictate whether a component is to be displayed or not on a page by a simple "if" condition declared as its attribute.
ZKEssentials Intro Goodbye.png


  1. Please refer to resources on Internet, such as http://www.w3schools.com/xml/xml_whatis.asp and http://www.xml.com/pub/a/98/10/guide0.html should you need to get comfortable with its syntax and conventions

What the Components Declarations Become

Components declared using ZUML in a ZUL file are parsed by a ZK enhanced XML parser. The components declared are created as POJO (Plain Old Java Objects) in the JVM at the server. Suppose we have a ZUL page that outlines a tree of components as the following:

<window title="ZK Essentials" border="normal" width="250px">
	<button label="Hello"/>
	<button label="Good-bye "/>
</window>

The ZUL page renders to a window containing two buttons as shown in the image below:
ZKEssentials Intro ZULSample.png

The markup in ZUL is equivalent to the following POJO declarations in Java:

Window win = new Window();
	win.setTitle("ZK Essentials");
	win.setBorder("normal");
	win.setWidth("250px");
		
Button helloBtn = new Button();
	helloBtn.setLabel("Hello");
	helloBtn.setParent(win);
		
Button byeBtn = new Button();
	byeBtn.setLabel("Good-bye");
	byeBtn.setParent(win);

Components at server are then translated to instructions(in JSON) needed for widget (JavaScript objects) creation and sent to the client. ZKEssentials Intro ZULtoPOJO.png


Where the Components Belong

The Page

A Page is not a component; it does not implement the Component interface. A page is automatically created when user requests a resource such as a ZUL page. The page serves as a container for the components belonging to it so that the components will be displayed in an orientation in the browser window that is defined by the developer.

The Desktop

A Desktop is created automatically when a page is created. A desktop may contain one or more pages, serving requests from the same URL. To elaborate on how a desktop can contain many pages, suppose we have a shopping cart application deployed on www.killerapp.com, where we have index.zul serving this URL. We could opt to layout the page into divisions, and instead of embedding the markup for the "Product List" in the division, we could save the markup as a different ZUL page that's included in the index.zul page. This is conceptually illustrated below:

ZKEssentials Intro MultiPage.png

The sample markup that could accomplish this may look something like this:
index.zul

<? page title="ZK Shopping Cart" ?>
<window border="normal" ...>
       <borderlayout>
              <center ...>
                    <include src="/product.zul"/>
              </center>
              <east ...>
                     //shopping cart item list implementation
              </east>
              <south ...>
                    //orders processed list implementation
              </south>
        </borderlayout>
</window>

The implementation for the "Product List" could be saved in another ZUL page.
product.zul

<grid >
       //product list implementation
</grid>


How to Find a Component

With the components nested and stacked up together to give us our application UI, we need a way to identify the necessary ones for processing. For example, we might need to dynamically append a component to an existing component, or if one component's behavior depends on that of another.
The sample below illustrates such an incident:
ZKEssentials Intro HelloGoodbye.png

The markup source is:

<window title="ZK Essentials" mode="overlapped" border="normal" width="250px">
	<label id="lbl"/>World !
	<button label="Hello " onClick="lbl.value = self.label"/>
	<button label="Good-bye " onClick="lbl.value = self.label"/>
</window>

The value of the label with ID "lbl" depends on user's clicking of the button "Hello", or "Good-bye". When a button is clicked, the value of the button's label is assigned to the value of the label. Note that the string "World !" is automatically converted to a label. Without first assigning "lbl" to the desired label component, we would not have been able to fetch it and assign it with a value. In fact, ZK assigns each component with a UUID (Universal Unique Identifier) to keep track of its tree of components internally. This UUID is over-ridden when develop assigns it a more legible ID.

Finding a Component Programmatically in Java

Suppose we have a POJO declaration for a simple UI that looks like this:

Window outerWin = new Window();

Button outerBtn = new Button();
btn.setParent(outerWin);

Window innerWin = new Window();
innerWin.setParent(outerWin);

Button innerBtn = new Button();
innerBtn.setParent(innerWin);


For better readability, the equivalent declaration using ZUML in ZUL file is given here:

<window id="outerWin">
       <button id="outerBtn">
        <window id="innerWin">
             <button id="innerBtn"/>
        </window>
</window>

Now suppose we have a controller class where we want to programmatically access and modify the children components (outerBtn, innerWin, innerBtn); how could we go about accomplishing that if we have access to the Window component only?

ZK provides various ways to accomplish this, we summarize them in a table for clarity:

Getting components from outerWin

Component method Note
outerBtn = (Button)outerWin.getFellow("outerBtn"); The components outerWin, outerBtn, and innerWin form an ID Space. Within this ID Space, a component can call another component using the callee's ID. outerBtn is the Space Owner for this ID Space.
innerWin = (Window)outerWin.getFellow("innerWin"); innerWin belongs to the same ID Space as outerWin, hence, it could be called using the getFellow method.
innerBtn = (Button)outerWin.getFellow("innerWin").getFellow("innerBtn"); innerWin and innerBtn belong to an ID Space of their own, with innerWin being the Space Owner. innerWin is also a member of the ID Space which outerWin is the Space Owner, hence, we can call the getFellow method on outerWin, get the innerWin, then call getFellow on innerWin to get innerBtn.
outerBtn = (Button)Path.getComponent("/outerBtn"); The Path provides the utility method getComponent which takes the relative path of the component as its argument. /outerBtn is equivalent to outerWin/outerBtn
innerWin = (Window)Path.getComponent("/innerWin"); innerWin and outerBtn both have outerWin as an ID Space Owner
innerBtn = (Button)Path.getComponent("/innerWin/innerBtn"); innerBtn has innerWin as its ID Space Owner, innerWin in turn has outerWin as its Space Owner; hence, we write /innerWin/innerBtn, which is equivalent to outerWin/innerWin/innerBtn
outerBtn = (Button)outerWin.getFirstChild(); The getFirstChild method returns the first child component of the caller; the advantage of using this method is that you don't even need to know the component ID to fetch the component.
innerWin = (Window)outerWin.getFirstChild().getNextSibling(); The getFirstChild method gets the outerBtn since it's outerWin's first child component, we then call the getNextSibling method to find the innerWin; again, we don't need to specify the component ID.
innerBtn = (Button)outerWin.getFirstChild().getNextSibling().getFirstChild(); We compound another getFirstChild method to get the first, and only, child component of innerWin.

In the figure below, we illustrate the concept of ID Space which underlines how these methods work:
ZKEssentials Intro IDSpace.png

The Window component and Page are SpaceOwners by default. We can make any component a space owner by implementing the IdSpace. To identify a component's space owner, call its getSpaceOwner method.



Last Update : 2010/11/01

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.