Component-based UI"

From Documentation
m (remove empty version history (via JWB))
 
(88 intermediate revisions by 7 users not shown)
Line 3: Line 3:
 
=Overview=
 
=Overview=
  
Each UI object is represented with a component (<javadoc type="interface">org.zkoss.zk.ui.Component</javadoc>). Thus, composing UI is all about assembling components. To alert UI is all about modifying the states and relationship of components.
+
Each UI object is represented by a component (<javadoc type="interface">org.zkoss.zk.ui.Component</javadoc>). Thus, composing an UI object is like assembling components. To alter UI one has to modify the states and relationships of components.
  
For example, as shown below, we declared a <javadoc>org.zkoss.zul.Window</javadoc> component, enabled the border (border="normal"), and set its width to a definite 250 pixels. Enclosed in the <javadoc>org.zkoss.zul.Window</javadoc> are two <javadoc>org.zkoss.zul.Button</javadoc> components.
+
For example, as shown below, we declared a <javadoc>org.zkoss.zul.Window</javadoc> component, enabling the border property to normal and setting its width to a definite 250 pixels. Enclosed in the <javadoc>org.zkoss.zul.Window</javadoc> component are two <javadoc>org.zkoss.zul.Button</javadoc> components.
  
 
[[Image:ZKEssentials_Intro_Hello.png]]
 
[[Image: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.
+
As shown above, there are two ways to declare UI: XML-based approach and pure-Java approach. You can mix them if you like.
  
 
=Forest of Trees of Components=
 
=Forest of Trees of Components=
  
Like a tree structure, a component has at most one parent, while it might have multiple children.
+
Like in 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.
+
Some components accept only certain types of components as children. Some do not allow to have any children 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'''. Multiple root components are allowed in each page, though not common.
+
A component without any parents is called a '''root component'''. Each page is allowed to have multiple root components, even though this does not happen very often.
  
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,
+
Notice that if you are using ZUML, there is an XML limitation, which means that only one document root is allowed. To specify multiple roots, you have to enclose the root components with the <code>zk</code> tag. <code>zk</code> tag is a special tag that does not create components. For example,
  
 
<source lang="xml">
 
<source lang="xml">
Line 27: Line 27:
 
</zk>
 
</zk>
 
</source>
 
</source>
 +
 +
==getChildren()==
 +
 +
Most of the collections returned by a component, such as <javadoc method="getChildren()" type="interface">org.zkoss.zk.ui.Component</javadoc>, are live structures. It means that you can add, remove or clear a child by manipulating the returned list directly. For example, to detach all children, you could do it in one statement:
 +
 +
<source lang="xml">
 +
comp.getChildren().clear();
 +
</source>
 +
 +
It is equivalent to
 +
 +
<source lang="xml">
 +
for (Iterator it = comp.getChildren().iterator(); it.hasNext();) {
 +
    it.next();
 +
    it.remove();
 +
}
 +
</source>
 +
 +
Note that the following code will never work because it would cause ConcurrentModificationException.
 +
 +
<source lang="xml">
 +
for (Iterator it = comp.getChildren().iterator(); it.hasNext();)
 +
    ((Component)it.next()).detach();
 +
</source>
 +
 +
==Sorting the children==
 +
 +
The following statement will fail for sure because the list is live and a component will be detached first before we move it to different location.
 +
 +
<source lang="java">
 +
Collections.sort(comp.getChildren());
 +
</source>
 +
 +
More precisely, a component has at most one parent and it has only one spot in the list of children. It means, the list is actually a set (no duplicate elements allowed). On the other hand, <code>Collections.sort()</code> cannot handle a set correctly.
 +
 +
Thus, we have to copy the list to another list or array and then sort it. <javadoc method="sort(java.util.List, java.util.Comparator)">org.zkoss.zk.ui.Components</javadoc> is a utility to simplify the job.
  
 
=Desktop, Page and Component=
 
=Desktop, Page and Component=
  
A page (<javadoc type="interface">org.zkoss.zk.ui.Page</javadoc>) 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 page (<javadoc type="interface">org.zkoss.zk.ui.Page</javadoc>) is a collection 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 (<javadoc type="interface">org.zkoss.zk.ui.Desktop</javadoc>) is a collection of pages. It represents a browser window (or a tab or a frame of the browser)<ref>Under portal environment, there might be multiple desktops in one browser window. However, it is really important in the developer's viewpoint.</ref>. You might image a desktop represents an independent HTTP request.
+
A desktop (<javadoc type="interface">org.zkoss.zk.ui.Desktop</javadoc>) is a collection of pages. It represents a browser window (a tab or a frame of the browser)<ref>Under portal environment, there might be multiple desktops in one browser window. However, it is really important in the developer's viewpoint.</ref>. You might image a desktop representing an independent HTTP request.
  
[[Image:ZKEssentials_Intro_MultiPage.png]]
+
[[Image:ZKEssentials_Intro_MultiPage.png | center]]
  
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 <javadoc method="service(org.zkoss.zk.au.AuRequest, boolean)" type="interface>org.zkoss.zk.ui.sys.DesktopCtrl</javadoc>, and then forwarded to <javadoc method="service(org.zkoss.zk.au.AuRequest, boolean)">org.zkoss.zk.ui.sys.ComponentCtrl</javadoc>. It also means, the application can not access components in multiple desktops at the same time.
+
A desktop is also a logic scope that an application can access in a request. Each time a request is sent from the client, it is associated with the desktop it belongs. The request is passed to <javadoc method="service(org.zkoss.zk.au.AuRequest, boolean)" type="interface>org.zkoss.zk.ui.sys.DesktopCtrl</javadoc> and then forwarded to <javadoc method="service(org.zkoss.zk.au.AuRequest, boolean)">org.zkoss.zk.ui.sys.ComponentCtrl</javadoc>. This also means that 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 (<javadoc type="interface" method="service(org.zkoss.zk.ui.Page)">org.zkoss.zk.ui.Richlet</javadoc>). The second page is created when the <javadoc>org.zkoss.zul.Include</javadoc> component includes another page with the defer mode. For example, two pages will be created if the following is visited.
+
Both a desktop and a page can be created automatically when ZK Loader loads a ZUML page or calls a richlet (<javadoc type="interface" method="service(org.zkoss.zk.ui.Page)">org.zkoss.zk.ui.Richlet</javadoc>). The second page is created when the <javadoc>org.zkoss.zul.Include</javadoc> component includes another page with the defer mode. For example, two pages will be created if the following is visited:
  
 
<source lang="xml">
 
<source lang="xml">
Line 47: Line 83:
 
</source>
 
</source>
  
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 that if the mode is not specified (i.e., the instant mode), <javadoc>org.zkoss.zul.Include</javadoc> will not be able to create a new page. Rather, it will append all components created by <code>another.zul</code> as its own child components. For example,
  
==Attach Component to Page==
+
<source lang="xml">
 +
<window>
 +
  <include src="another.zul"/> <!-- default: instant mode -->
 +
</window>
 +
</source>
 +
 
 +
is equivalent to the following (except div is not a space owner, see below)
 +
 
 +
<source lang="xml">
 +
<window>
 +
  <div>
 +
    <zscript>
 +
      execution.createComponents("another.zul", self, null);
 +
    </zscript>
 +
  </div>
 +
</window>
 +
</source>
 +
 
 +
<blockquote>
 +
----
 +
<references/>
 +
</blockquote>
 +
 
 +
==Attach a Component to a 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.
 
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.
Line 58: Line 117:
 
</source>
 
</source>
  
A component is a POJO object. If you don't have any reference to it, it will be recycled when JVM starts  [http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29 garbage collection].
+
A component is a POJO object. If you do not have any reference to it, it will be recycled when JVM starts  [http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29 garbage collection].
  
 
There are two ways to attach a component to a page:
 
There are two ways to attach a component to a page:
  
# Append it as a child of another component that is already attached to a page (<javadoc method="appendChild(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.Component</javadoc>, <javadoc method="insertBefore(org.zkoss.zk.ui.Component, org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.Component</javadoc>, or <javadoc method="setParent(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.Component</javadoc>).
+
# Append it as a child of another component that is already attached to a page (<javadoc method="appendChild(org.zkoss.zk.ui.Component)" type="interface">org.zkoss.zk.ui.Component</javadoc>, <javadoc method="insertBefore(org.zkoss.zk.ui.Component, org.zkoss.zk.ui.Component)" type="interface">org.zkoss.zk.ui.Component</javadoc>, or <javadoc method="setParent(org.zkoss.zk.ui.Component)" type="interface">org.zkoss.zk.ui.Component</javadoc>).
# Invoke <javadoc method="setPage(org.zkoss.zk.ui.Page)">org.zkoss.zk.ui.Component</javadoc> to attach it to a page directly. It is also the way to make a component become a root component.
+
# Invoke <javadoc method="setPage(org.zkoss.zk.ui.Page)" type="interface">org.zkoss.zk.ui.Component</javadoc> to attach it to a page directly. It is also another way to make a component become a root component.
  
Since a component can have at most one parent and be attached at most one page, it is detached automatically from the previous parent or page when it is attached to another component or page. For example, <code>b</code> will be a child of <code>win2</code> and <code>win1</code> has no child at the end.
+
Since a component can have at most one parent and be attached at most one page, it will be detached automatically from the previous parent or page when it is attached to another component or page. For example, <code>b</code> will be a child of <code>win2</code> and <code>win1</code> has no child at the end.
  
 
<source lang="xml">
 
<source lang="xml">
Line 74: Line 133:
 
</source>
 
</source>
  
==Don't Cache Components Attached to Page in Static Fields==
+
==Detach a Component from a Page==
 +
 
 +
To detach a Component from the page, you can either invoke <code>comp.setParent(null)</code> if it is not a root component or <code>comp.setPage(null)</code> if it is a root component. <javadoc method="detach()" type="interface">org.zkoss.zk.zk.ui.Component</javadoc> is a shortcut to detach a component without knowing if it is a root component.
 +
 
 +
==Invalidate a Component==
 +
 
 +
When a component is attached to a page, the component and all of its descendants will be rendered. On the other hand, when a state of an attached component is changed, only the changed state is sent to client for update (for better performance). Very rare, you might need to invoke <javadoc method="invalidate()" type="interface">org.zkoss.zk.ui.Component</javadoc> to force the component and its descendants to be rerendered<ref>ZK Update Engine will queue the ''update'' and ''invalidate'' commands, and then optimize them before sending them back to the client (for better performance)</ref>.
 +
 
 +
There are only a few reasons to invalidate a component, but it is still worthwhile to note them down:
 +
 
 +
#If you add more than 20 child components, you could invalidate the parent to improve the performance. Though the result Ajax response might be longer, the browser will be more effective in replacing a DOM tree rather than adding DOM elements.
 +
#If a component has a bug that does not update the DOM tree correctly, you could invalidate its parent to resolve the problem<ref> Of course, remember to let us know and we will fix it in the upcoming version.</ref>.
 +
 
 +
<blockquote>
 +
----
 +
<references/>
 +
</blockquote>
 +
 
 +
==Don't Cache Components Attached to a Page in Static Fields==
 +
 
 +
As described above, a desktop is a logical scope which can be accessed by the application when serving a request. In other words, the application cannot detach a component from one desktop to another desktop. This typically happens when you cache a component accidentally.
 +
 
 +
For example, the following code will cause an exception if it is loaded multiple times.
 +
 
 +
<source lang="xml">
 +
<window apply="foo.Foo"/> <!-- cause foo.Foo to be instantiated and executed -->
 +
</source>
  
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 if the URL is loaded multiple times.
+
and <code>foo.Foo</code> is defined as follows<ref>A composer (<javadoc type="interface">org.zkoss.zk.ui.util.Composer</javadoc>) is a controller that can be associated with a component for handling the UI in Java. For the information, please refer to [[ZK Developer's Reference/MVC/Controller/Composer|the Composer section]].</ref>.
  
<source lang="java">
+
<source lang="java" highlight="6">
 
package foo;
 
package foo;
 
import org.zkoss.zk.ui.*;
 
import org.zkoss.zk.ui.*;
Line 101: Line 186:
 
org.zkoss.zul.Window.insertBefore(Window.java:833)
 
org.zkoss.zul.Window.insertBefore(Window.java:833)
 
org.zkoss.zk.ui.AbstractComponent.appendChild(AbstractComponent.java:1232)
 
org.zkoss.zk.ui.AbstractComponent.appendChild(AbstractComponent.java:1232)
foo.Foo.doAfterCompose(Foo.java:10</source>
+
foo.Foo.doAfterCompose(Foo.java:10)
 
</source>
 
</source>
  
=ID Space=
+
<blockquote>
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.
+
----
 +
<references/>
 +
</blockquote>
  
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.
+
=Component Cloning=
 +
All components are cloneable (java.lang.Cloneable). It is simple to replicate components by invoking <javadoc method="clone()" type="interface">org.zkoss.zk.ui.Component</javadoc>.
  
The simplest form of an ID space is a <tt>window</tt> (<javadoc>org.zkoss.zul.Window</javadoc> ). All descendant components of a <tt>window</tt> (including the <tt>label</tt> window)forms an independent ID space. Thus, you could use a <tt>window</tt> as the topmost component of each page, such that developers need to maintain the uniqueness of each page separately.
+
<source lang="java">
 
+
main.appendChild(listbox.clone());
More generally, any component could form an ID space as long as it implements the <javadoc type="interface">org.zkoss.zk.ui.IdSpace</javadoc> interface. <tt>Page</tt> also implements the IdSpace interface, so it is also a space owner. Besides <tt>window</tt> and <tt>page</tt>, <tt>regular macro</tt> 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 <tt>getSpaceOwner</tt> 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:
 
<source lang="xml" >
 
<?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>
 
 
</source>
 
</source>
  
[[Image:zk the id space.jpg]]
+
'''Notice'''
 +
* It is a ''deep clone''. That is, all children and descendants are cloned too.
 +
* The component returned by <javadoc method="clone()" type="interface">org.zkoss.zk.ui.Component</javadoc> does not belong to any pages. It doesn't have a parent either. You have to attach it manually if necessary.
 +
* ID, if any, is preserved. Thus, you ''cannot'' attach the returned component to the same ID space without modifying ID if there is any.
  
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.
+
Similarly, all components are serializable (java.io.Serializable). Like cloning, all children and descendants are serialized. If you serialize a component and then de-serialize it back, the result will be the same as invoking <javadoc method="clone()" type="interface">org.zkoss.zk.ui.Component</javadoc><ref>Of course, the performance of <javadoc method="clone()" type="interface">org.zkoss.zk.ui.Component</javadoc> is much better.</ref>.
 
 
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 <tt>getFellow</tt> method in the <tt>IdSpace</tt> interface or the <tt>Component</tt> interface.
 
 
 
Notice that the <tt>getFellow</tt> method can be invoked against any components in the same ID space, not just the space owner. Similarly, the <tt>getSpaceOwner</tt> 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 <tt>getSpaceOwner</tt> will get C itself, if C calls <tt>getSpaceOwnerOfParent</tt> will get A.
 
 
 
The <javadoc>org.zkoss.zk.ui.Path</javadoc> class provides utilities to simplify the location of a component among ID spaces. The way of using it is similar to <tt>java.io.File</tt> .
 
 
 
<source lang="java" >
 
//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
 
</source>
 
 
 
Remember that each GUI component has its corresponding java object. Through <tt>Path.getComponent()</tt>, you can get the object in java, and do whatever you want to manipulate the java object.[[example code for path getComponent | Learn more]]
 
 
 
==Namespace and ID Space==
 
<!-- Namespace is deprecated since 5.0 -->
 
To grant the interpreter an access to the components directly, the namespace concept (<javadoc>org.zkoss.zk.scripting.Namespace</javadoc> ) is introduced.
 
 
 
*First, each ID space corresponds to exactly one namespace.
 
*Second, variables defined in a namespace are visible to the zscript codes and [[ZK_ZUML_Reference/EL_Expressions | EL expressions]] that belong to the same namespace.
 
 
 
In the following example, it will show: <tt>Hi, namespace  Hi, namespace</tt>
 
<source lang="xml" >
 
<window border="normal">
 
<label id="l" value="changed by zscript, therefore not shown"/>
 
<zscript>
 
l.value = "Hi, namespace";
 
</zscript>
 
${l.value}
 
</window>
 
</source>
 
 
 
<tt>${l.value}</tt> is an EL expression, it means get the value of <tt>l</tt>.
 
 
 
In the following example, there are two namespaces. One belongs to window <tt>w1</tt> while the other belongs to window <tt>w2</tt>. Thus, the <tt>b1</tt> button's <tt>onClick</tt> script refers to the label defined in window <tt>w2</tt> , while the <tt>b2</tt> button's <tt>onClick</tt> script refers to the checkbox defined in window <tt>w1</tt> .
 
 
 
<source lang="xml" >
 
<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>
 
</source>
 
 
 
*Notice the namespace is '''hierarchical'''.  
 
 
 
In other words, <tt>zscript</tt> in window <tt>w2</tt> can see components in window <tt>w1</tt> , unless it is overridden in window <tt>w2</tt> . Thus, clicking button <tt>b1</tt> will change label of <tt>c</tt> to "OK" in the following example.
 
 
 
<source lang="xml" >
 
<window id="w1">
 
<window id="w2">
 
<button id="b1" onClick="c.value = &quot;OK&quot;" label="Click me"/>
 
</window>
 
<label id="c"/>
 
</window>
 
</source>
 
 
 
It looks strange at first glance, as <tt>onClick="c.value = &amp;quot;OK&amp;quot;"</tt> appears before <tt><label id="c"/></tt> is defined. Don't we have to declare variable before using it? Actually, it's a life cycle issue. <tt>zscript</tt> inside <tt>onClick</tt> is only evaluated when user actually clicks on the button. At that time, <tt><label id="c"/></tt> has been created already and is certainly visible to <tt>zscript</tt>.
 
  
 
<blockquote>
 
<blockquote>
Line 210: Line 213:
 
</blockquote>
 
</blockquote>
  
=Version History=
+
 
Last Update : {{REVISIONYEAR}}/{{REVISIONMONTH}}/{{REVISIONDAY}}
 
{| border='1px' | width="100%"
 
! Version !! Date !! Content
 
|-
 
| &nbsp;
 
| &nbsp;
 
| &nbsp;
 
|}
 
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 10:26, 5 February 2024


Component-based UI


Overview

Each UI object is represented by a component (Component). Thus, composing an UI object is like assembling components. To alter UI one has to modify the states and relationships of components.

For example, as shown below, we declared a Window component, enabling the border property to normal and setting its width to a definite 250 pixels. Enclosed in the Window component are two Button components.

ZKEssentials Intro Hello.png

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

Forest of Trees of Components

Like in 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 do not allow to have any children at all. For example, Grid in XUL accepts Columns and Rows as children only.

A component without any parents is called a root component. Each page is allowed to have multiple root components, even though this does not happen very often.

Notice that if you are using ZUML, there is an XML limitation, which means that only one document root is allowed. To specify multiple roots, you have to enclose the root components with the zk tag. zk tag is a special tag that does not create components. For example,

<zk>
    <window/> <!-- the first root component -->
    <div/> <!-- the second root component -->
</zk>

getChildren()

Most of the collections returned by a component, such as Component.getChildren(), are live structures. It means that you can add, remove or clear a child by manipulating the returned list directly. For example, to detach all children, you could do it in one statement:

comp.getChildren().clear();

It is equivalent to

for (Iterator it = comp.getChildren().iterator(); it.hasNext();) {
    it.next();
    it.remove();
}

Note that the following code will never work because it would cause ConcurrentModificationException.

for (Iterator it = comp.getChildren().iterator(); it.hasNext();)
    ((Component)it.next()).detach();

Sorting the children

The following statement will fail for sure because the list is live and a component will be detached first before we move it to different location.

Collections.sort(comp.getChildren());

More precisely, a component has at most one parent and it has only one spot in the list of children. It means, the list is actually a set (no duplicate elements allowed). On the other hand, Collections.sort() cannot handle a set correctly.

Thus, we have to copy the list to another list or array and then sort it. Components.sort(List, Comparator) is a utility to simplify the job.

Desktop, Page and Component

A page (Page) is a collection 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 (a tab or a frame of the browser)[1]. You might image a desktop representing an independent HTTP request.

ZKEssentials Intro MultiPage.png

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

Both a desktop and a page can be created automatically when ZK Loader loads a ZUML page or calls a richlet (Richlet.service(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 that if the mode is not specified (i.e., the instant mode), Include will not be able to create a new page. Rather, it will append all components created by another.zul as its own child components. For example,

<window>
  <include src="another.zul"/> <!-- default: instant mode -->
</window>

is equivalent to the following (except div is not a space owner, see below)

<window>
  <div>
    <zscript>
      execution.createComponents("another.zul", self, null);
    </zscript>
  </div>
</window>

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

Attach a Component to a 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 do not 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 another way to make a component become a root component.

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

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

Detach a Component from a Page

To detach a Component from the page, you can either invoke comp.setParent(null) if it is not a root component or comp.setPage(null) if it is a root component. Component.detach() is a shortcut to detach a component without knowing if it is a root component.

Invalidate a Component

When a component is attached to a page, the component and all of its descendants will be rendered. On the other hand, when a state of an attached component is changed, only the changed state is sent to client for update (for better performance). Very rare, you might need to invoke Component.invalidate() to force the component and its descendants to be rerendered[1].

There are only a few reasons to invalidate a component, but it is still worthwhile to note them down:

  1. If you add more than 20 child components, you could invalidate the parent to improve the performance. Though the result Ajax response might be longer, the browser will be more effective in replacing a DOM tree rather than adding DOM elements.
  2. If a component has a bug that does not update the DOM tree correctly, you could invalidate its parent to resolve the problem[2].

  1. ZK Update Engine will queue the update and invalidate commands, and then optimize them before sending them back to the client (for better performance)
  2. Of course, remember to let us know and we will fix it in the upcoming version.

Don't Cache Components Attached to a Page in Static Fields

As described above, a desktop is a logical scope which can be accessed by the application when serving a request. In other words, the application cannot detach a component from one desktop to another desktop. This typically happens when you cache a component accidentally.

For example, the following code will cause an exception if it is loaded multiple times.

<window apply="foo.Foo"/> <!-- cause foo.Foo to be instantiated and executed -->

and foo.Foo is defined as follows[1].

package foo;
import org.zkoss.zk.ui.*;
import org.zkoss.zul.*;

public class Foo implements org.zkoss.zk.ui.util.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);
   }
}

The exception is similar to the following:

org.zkoss.zk.ui.UiException: The parent and child must be in the same desktop: <Window u1EP0>
	org.zkoss.zk.ui.AbstractComponent.checkParentChild(AbstractComponent.java:1057)
	org.zkoss.zk.ui.AbstractComponent.insertBefore(AbstractComponent.java:1074)
	org.zkoss.zul.Window.insertBefore(Window.java:833)
	org.zkoss.zk.ui.AbstractComponent.appendChild(AbstractComponent.java:1232)
	foo.Foo.doAfterCompose(Foo.java:10)

  1. A composer (Composer) is a controller that can be associated with a component for handling the UI in Java. For the information, please refer to the Composer section.

Component Cloning

All components are cloneable (java.lang.Cloneable). It is simple to replicate components by invoking Component.clone().

main.appendChild(listbox.clone());

Notice

  • It is a deep clone. That is, all children and descendants are cloned too.
  • The component returned by Component.clone() does not belong to any pages. It doesn't have a parent either. You have to attach it manually if necessary.
  • ID, if any, is preserved. Thus, you cannot attach the returned component to the same ID space without modifying ID if there is any.

Similarly, all components are serializable (java.io.Serializable). Like cloning, all children and descendants are serialized. If you serialize a component and then de-serialize it back, the result will be the same as invoking Component.clone()[1].


  1. Of course, the performance of Component.clone() is much better.




Last Update : 2024/02/05

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