Component-based UI"

From Documentation
Line 32: Line 32:
 
Notice, if the mode is not specified (i.e., the instant mode), <javadoc>org.zkoss.zul.Include</javadoc> won't create a new page. Rather, it append all components created by <code>another.zul</code> as its own child components.
 
Notice, if the mode is not specified (i.e., the instant mode), <javadoc>org.zkoss.zul.Include</javadoc> won't create a new page. Rather, it append all components created by <code>another.zul</code> as its own child components.
  
=Forest of Trees of Components=
 
 
Like a tree structure, a component has at most one parent, while it might have multiple children.
 
 
Some components accept only certain types of components as children. Some don't allow any child at all. For example, <javadoc>org.zkoss.zul.Grid</javadoc> in XUL accepts <javadoc>org.zkoss.zul.Columns</javadoc> and <<javadoc>org.zkoss.zul.Rows</javadoc> as children only.
 
 
A component without any parent is called a '''root component'''. A page might have multiple root components.
 
 
Notice that, if you are using ZUML, there is a XML limitation: exactly one document root is allowed. To specify multiple roots, you have to enclose the root components with the <tt>zk</tt> tag. It is a special tag that does not create components. For example,
 
 
<source lang="xml">
 
<zk>
 
    <window/> <!-- the first root component -->
 
    <div/> <!-- the second root component -->
 
</zk>
 
</source>
 
 
==Attach Component to Page==
 
==Attach Component to Page==
  

Revision as of 09:54, 3 November 2010


Component-based UI


Overview

Each UI object is represented with a component (Component). Thus, composing UI is all about assembling components. To alert UI is all about modifying the states and relationship of components.

For example, as shown below, 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.

ZKEssentials Intro Hello.png

As shown, there are two ways to declare UI: XML-based approach and pure-Java approach. Furthermore, you could mix them if you like.

Desktop, Page and Component

A page (Page) is a collections of components. It represents a portion of the browser window. Only components attached to a page are available at the client. They are removed when they are detached from a page.

A desktop (Desktop) is a collection of pages. It represents a browser window (or a tab or a frame of the browser)[1]. You might image a desktop represents an independent HTTP request.

ZKEssentials Intro MultiPage.png

A desktop is also the logic scope that an application can access in a request. Each time a request is sent from the client, it is associated with the desktop it belongs. The request is passed to DesktopCtrl.service(AuRequest, boolean), and then forwarded to ComponentCtrl.service(AuRequest, boolean). It also means, the application can not access components in multiple desktops at the same time.

Both a desktop and a page is created automatically when ZK Loader loads a ZUML page or calls a richlet (Richlet.service(Page)). The second page is created when the Include component includes another page with the defer mode. For example, two pages will be created if the following is visited.

<!-- the main page -->
<window>
  <include src="another.zul" mode="defer"/> <!-- creates another page -->
</window>

Notice, if the mode is not specified (i.e., the instant mode), Include won't create a new page. Rather, it append all components created by another.zul as its own child components.

Attach Component to Page

A component is available at the client only if it is attached to a page. For example, the window created below will not be available at the client.

Window win = new Window();
win.appendChild(new Label("foo"));

A component is a POJO object. If you don't have any reference to it, it will be recycled when JVM starts garbage collection.

There are two ways to attach a component to a page:

  1. Append it as a child of another component that is already attached to a page (Component.appendChild(Component), Component.insertBefore(Component, Component), or Component.setParent(Component)).
  2. Invoke Component.setPage(Page) to attach it to a page directly. It is also the way to make a component become a root component.

Since a component can have at most one parent and be attached at most one page, it is detached automatically from the previous parent or page when it is attached to another component or page. For example, b will be a child of win2 and win1 has no child at the end.

Window win1 = new Window;
Button b = new Button();
win1.appendChild(b);
win2.appendChild(b); //implies detach b from win1

Don't Cache Components Attached to Page in Static Fields

As described above, a desktop is a logical scope that the application can access when serving a request. In other words, the application cannot detach a component from one desktop from another desktop. It typically happens when you cache a component accidentally. For example, the following code will cause an exception.

//Foo.java
public class Foo implements Composer {
   private static Window main; //WRONG! don't cache it in a static field
   public void doAfterCompose(Component comp) {
       if (main == null)
           main = new Window();
       comp.appendChild(main);
   }
}

ID Space

It is common to decompose a visual presentation into several ZUML pages. For example, a page for displaying a purchase order, and a modal dialog for entering the payment term. If all components are uniquely identifiable in the same desktop, developers have to maintain the uniqueness of all identifiers for all pages that might created to the same desktop.

The concept of ID space is hence introduced to resolve this issue. An ID space is a subset of components of a desktop. The uniqueness is guaranteed only in the scope of an ID space.

The simplest form of an ID space is a window (Window ). All descendant components of a window (including the label window)forms an independent ID space. Thus, you could use a window as the topmost component of each page, such that developers need to maintain the uniqueness of each page separately.

More generally, any component could form an ID space as long as it implements the IdSpace interface. Page also implements the IdSpace interface, so it is also a space owner. Besides window and page, regular macro is another built-in space owner.

The topmost component of an ID space is called the owner of the ID space, which could be retrieved by the getSpaceOwner method in the Component interface.

If an ID space, say X, is a descendant of another ID space, say Y, then space X's owner is part of space Y but descendants of X is not part of space Y.


Following zul example will get an ID space graph as below:

<?page id="P"?>
<zk>
	<window id="A">
		<hbox id="B">
			<button id="D" />
		</hbox>
		<window id="C">
			<button id="E" />
		</window>
	</window>
	<hbox id="F">
		<button id="G" />
	</hbox>
</zk>

Zk the id space.jpg

As depicted in the figure, there are three spaces: P, A and C. Space P includes P, A, F and G. Space A includes A, B, C and D. Space C includes C and E.

Components in the same ID spaces are called fellows. For example, A, B, C and D are fellows of the same ID space.

To retrieve another fellow, you could use the getFellow method in the IdSpace interface or the Component interface.

Notice that the getFellow method can be invoked against any components in the same ID space, not just the space owner. Similarly, the getSpaceOwner method returns the same object for any components in the same ID space, no matter it is the space owner or not. In the example above, if C calls getSpaceOwner will get C itself, if C calls getSpaceOwnerOfParent will get A.

The Path class provides utilities to simplify the location of a component among ID spaces. The way of using it is similar to java.io.File .

//below are 3 different ways to get the same component E
Path.getComponent("/A/C/E");//if call Path.getComponent under the same page.
new Path("A/C", "E").getComponent();
Path.getComponent("//P/A/C/E");//for page, you have to use // as prefix

Remember that each GUI component has its corresponding java object. Through Path.getComponent(), you can get the object in java, and do whatever you want to manipulate the java object. Learn more

Namespace and ID Space

To grant the interpreter an access to the components directly, the namespace concept (Namespace ) is introduced.

  • First, each ID space corresponds to exactly one namespace.
  • Second, variables defined in a namespace are visible to the zscript codes and EL expressions that belong to the same namespace.

In the following example, it will show: Hi, namespace Hi, namespace

<window border="normal">
	<label id="l" value="changed by zscript, therefore not shown"/>
	<zscript>
		l.value = "Hi, namespace";
	</zscript>
	${l.value}
</window>

${l.value} is an EL expression, it means get the value of l.

In the following example, there are two namespaces. One belongs to window w1 while the other belongs to window w2. Thus, the b1 button's onClick script refers to the label defined in window w2 , while the b2 button's onClick script refers to the checkbox defined in window w1 .

<window id="w1">
	<window id="w2">
		<label id="c"/>
		<button id="b1" onClick="c.value = &quot;OK&quot;" label="click me"/>
	</window>
	<checkbox id="c"/>
	<button id="b2" onClick="c.label = &quot;OK&quot;" label="click me"/>
</window>
  • Notice the namespace is hierarchical.

In other words, zscript in window w2 can see components in window w1 , unless it is overridden in window w2 . Thus, clicking button b1 will change label of c to "OK" in the following example.

<window id="w1">
	<window id="w2">
		<button id="b1" onClick="c.value = &quot;OK&quot;" label="Click me"/>
	</window>
	<label id="c"/>
</window>

It looks strange at first glance, as onClick="c.value = &quot;OK&quot;" appears before <label id="c"/> is defined. Don't we have to declare variable before using it? Actually, it's a life cycle issue. zscript inside onClick is only evaluated when user actually clicks on the button. At that time, <label id="c"/> has been created already and is certainly visible to zscript.


  1. Under portal environment, there might be multiple desktops in one browser window. However, it is really important in the developer's viewpoint.

Version History

Last Update : 2010/11/3

Version Date Content
     



Last Update : 2010/11/03

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