Tutorial"
m ((via JWB)) |
|||
(18 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
This tutorial guides you through the most fundamental features and concepts of ZK. | This tutorial guides you through the most fundamental features and concepts of ZK. | ||
+ | * To create web application, please refer to [http://books.zkoss.org/wiki/ZK_Installation_Guide/Quick_Start/Create_and_Run_Your_First_ZK_Application_with_Eclipse_and_ZK_Studio Create and Run Your First ZK Application with Eclipse and ZK Studio] | ||
* For product description, please refer to [http://www.zkoss.org/product/zk the ZK product page] and [http://www.zkoss.org/whyzk/features the feature list]. | * For product description, please refer to [http://www.zkoss.org/product/zk the ZK product page] and [http://www.zkoss.org/whyzk/features the feature list]. | ||
* For a real world example, please refer to [[ZK Getting Started/Creating a Database-driven Application|Creating a database-driven application]]. | * For a real world example, please refer to [[ZK Getting Started/Creating a Database-driven Application|Creating a database-driven application]]. | ||
Line 6: | Line 7: | ||
==Hello World!== | ==Hello World!== | ||
− | After ZK is installed on your favorite Web server<ref>Please refer to [[ZK Installation Guide]].</ref>, writing applications is straightforward. Just create a ZUML file, and name it as hello.zul<ref>The other way to try examples is to use [http://www.zkoss.org/zksandbox/ | + | After ZK is installed on your favorite Web server<ref>Please refer to [[ZK Installation Guide]].</ref>, writing applications is straightforward. Just create a ZUML file<ref>ZUML [http://books.zkoss.org/wiki/ZUML%20Reference/ZUML]</ref>, and name it as hello.zul<ref>The other way to try examples is to use [http://www.zkoss.org/zksandbox/ ZK Sandbox] to run them.</ref>, under one of the Web application's directories just as you would do for an HTML file. |
<source lang="xml" > | <source lang="xml" > | ||
Line 19: | Line 20: | ||
[[Image:dgGettingStartedHello.zul.png]] | [[Image:dgGettingStartedHello.zul.png]] | ||
− | On a ZUML page, an XML element describes what a component<ref>Interface : <javadoc type="interface">org.zkoss.zk.ui.Component</ref> can create while the XML attributes are used to assign values to a component's properties. In this example, a < | + | On a ZUML page, an XML element describes what a component<ref>Interface : <javadoc type="interface">org.zkoss.zk.ui.Component</ref> can create while the XML attributes are used to assign values to a component's properties. In this example, a <code>window</code> component is created and its <code>title</code> is set to <code>"My First ZK Application"</code> and its <code>border</code> is set to <code>normal</code>. |
− | The text enclosed in the XML elements can also be interpreted as a special component called < | + | The text enclosed in the XML elements can also be interpreted as a special component called <code>label</code>. Thus, the above example is equivalent to the following code: |
<source lang="xml" > | <source lang="xml" > | ||
Line 46: | Line 47: | ||
[[Image:DgGettingStartedHello2.png]] | [[Image:DgGettingStartedHello2.png]] | ||
− | The < | + | The <code>onClick</code> attribute is a special attribute used to add an event listener(<javadoc type="interface">org.zkoss.zk.ui.event.EventListener</javadoc>) to the component such as that it is invoked when an end user clicks the component. The attribute value could be any legal Java code. Notice that it is '''NOT''' JavaScript, and you have to use double quotes (") in a string. To escape a double quote in an XML string, you could use single quotes (') to enclose it<ref>If you are not familiar with XML, you might take a look at [[ZK Developer's Reference/UI Composing/ZUML/XML Background|the XML background section]].</ref>. |
Here we invoke <javadoc method="show(java.lang.String)">org.zkoss.zul.Messagebox</javadoc> to display a message box shown above. | Here we invoke <javadoc method="show(java.lang.String)">org.zkoss.zul.Messagebox</javadoc> to display a message box shown above. | ||
− | The Java code is interpreted by [http://www.beanshell.org/ BeanShell] at runtime. In addition to event handling, you could embed the code in a ZUML page by specifying it in a special element called < | + | The Java code is interpreted by [http://www.beanshell.org/ BeanShell] at runtime. In addition to event handling, you could embed the code in a ZUML page by specifying it in a special element called <code>zscript</code>. For example, you could simply define a function in the code as the following: |
− | <source lang="xml" > | + | <source lang="xml" high="3"> |
<window title="My First ZK Application" border="normal"> | <window title="My First ZK Application" border="normal"> | ||
<button label="Say Hello" onClick='alert("Hello World!")'/> | <button label="Say Hello" onClick='alert("Hello World!")'/> | ||
Line 64: | Line 65: | ||
</source> | </source> | ||
− | In fact, < | + | In fact, <code>alert</code> is a built-in function that you can use directly from the embedded Java code. |
<blockquote> | <blockquote> | ||
Line 81: | Line 82: | ||
</source> | </source> | ||
− | where [[ZUML Reference/EL Expressions/Implicit Objects/self|< | + | where [[ZUML Reference/EL Expressions/Implicit Objects/self|<code>self</code>]] is a built-in variable which refers a component receiving the event. |
− | If you enter < | + | If you enter <code>java.version</code> and then click the button, the result will be shown as the following: |
[[Image:DgGettingStartedProperty.png]] | [[Image:DgGettingStartedProperty.png]] | ||
Line 117: | Line 118: | ||
Instead of introducing different components for different purposes, our components are designed to build blocks. You are free to compose blocks together to realize sophisticated UI without customizing any components. For example, you could put anything in a grid, including grid itself; anything in any layout, including the layout itself. Please see [http://www.zkoss.org/zkdemo our demo] for more examples. | Instead of introducing different components for different purposes, our components are designed to build blocks. You are free to compose blocks together to realize sophisticated UI without customizing any components. For example, you could put anything in a grid, including grid itself; anything in any layout, including the layout itself. Please see [http://www.zkoss.org/zkdemo our demo] for more examples. | ||
+ | |||
+ | ==Express data with variable resolver and EL expressions== | ||
+ | |||
+ | On a ZUML page, you could locate data with a variable resolver (<javadoc type="interface">org.zkoss.xel.VariableResolver</javadoc>), and then express it with [[ZK Developer's Reference/UI Composing/ZUML/EL Expressions|EL expressions]]. | ||
+ | |||
+ | For example, assumes that we have a class called <code>foo.Users</code>, and we can retrieve a list of users by employing its static method called <code>getAll()</code>. Then, we can implement a variable resolver as follows. | ||
+ | |||
+ | <source lang=" java"> | ||
+ | package foo; | ||
+ | public class UserResolver implements org.zkoss.xel.VariableResolver { | ||
+ | public Object resolveVariable(String name) { | ||
+ | return "users".equals(name) ? Users.getAll(): null; | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | And, we can list all users as follows. | ||
+ | |||
+ | <source lang="xml"> | ||
+ | <?variable-resolver class="foo.UserResolver"?> | ||
+ | <grid> | ||
+ | <columns> | ||
+ | <column label="Name" sort="auto"/> | ||
+ | <column label="Title" sort="auto"/> | ||
+ | <column label="Age" sort="auto"/> | ||
+ | </columns> | ||
+ | <rows> | ||
+ | <row forEach="${users}"> | ||
+ | <label value="${each.name}"/> | ||
+ | <label value="${each.title}"/> | ||
+ | <label value="${each.age}"/> | ||
+ | </row> | ||
+ | </rows> | ||
+ | </grid> | ||
+ | </source> | ||
+ | |||
+ | There are three methods that we can assume <code>foo.User</code>: <code>getName()</code>, <code>getTitle()</code> and <code>getAge()</code>. <code>forEach</code> is used to instantiate components by iterating through a collection of objects. | ||
+ | |||
+ | [[File:DgGettingStartedUsers.png]] | ||
==MVC: Separate code from user interface== | ==MVC: Separate code from user interface== | ||
− | Embedding Java code in a ZUML page is straightforward and easy | + | Embedding Java code in a ZUML page is straightforward and easy for prototyping. However, in a production environment, it is better to separate the code from user interfaces. The code can be compiled at the development time. It is easier to develop and test, and runs much faster than the embedded code which is interpreted at runtime. |
To separate codes from UI, you can implement a Java class (aka., the controller) that implements <javadoc type="interface">org.zkoss.zk.ui.util.Composer</javadoc>, and then handle UI in <javadoc type="interface" method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.Composer</javadoc>. For example, you can redo the previous example by registering an event listener in <javadoc type="interface" method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.Composer</javadoc>, and then retrieve the result by instantiating a label to represent it in the event listener as follows. | To separate codes from UI, you can implement a Java class (aka., the controller) that implements <javadoc type="interface">org.zkoss.zk.ui.util.Composer</javadoc>, and then handle UI in <javadoc type="interface" method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.Composer</javadoc>. For example, you can redo the previous example by registering an event listener in <javadoc type="interface" method="doAfterCompose(org.zkoss.zk.ui.Component)">org.zkoss.zk.ui.util.Composer</javadoc>, and then retrieve the result by instantiating a label to represent it in the event listener as follows. | ||
− | <source lang="java"> | + | <source lang="java" high="8"> |
package foo; | package foo; | ||
import org.zkoss.zk.ui.Component; | import org.zkoss.zk.ui.Component; | ||
Line 135: | Line 175: | ||
target.addEventListener("onClick", new EventListener() { //add a event listener in Java | target.addEventListener("onClick", new EventListener() { //add a event listener in Java | ||
public void onEvent(Event event) { | public void onEvent(Event event) { | ||
− | String prop = System.getProperty(((Textbox)target. | + | String prop = System.getProperty(((Textbox)target.query("#input")).getValue()); |
− | target. | + | target.query("#result").appendChild(new Label(prop)); |
} | } | ||
}); | }); | ||
Line 145: | Line 185: | ||
As shown, an event listener could be registered with the use of <javadoc type="interface" method="addEventListener(java.lang.String, org.zkoss.zk.ui.event.EventListener)">org.zkoss.zk.ui.Component</javadoc>. An event listener must implement <javadoc type="interface">org.zkoss.zk.ui.event.EventListener</javadoc>, and then handle the event in <javadoc type="interface" method="onEvent(org.zkoss.zk.ui.event.Event">org.zkoss.zk.ui.event.EventListener</javadoc>. | As shown, an event listener could be registered with the use of <javadoc type="interface" method="addEventListener(java.lang.String, org.zkoss.zk.ui.event.EventListener)">org.zkoss.zk.ui.Component</javadoc>. An event listener must implement <javadoc type="interface">org.zkoss.zk.ui.event.EventListener</javadoc>, and then handle the event in <javadoc type="interface" method="onEvent(org.zkoss.zk.ui.event.Event">org.zkoss.zk.ui.event.EventListener</javadoc>. | ||
− | Also notice that a component | + | Also notice that a component could be retrieved with the use of <javadoc type="interface" method="query(java.lang.String)" type="interface">org.zkoss.zk.ui.Component</javadoc>, which allows the developer to use a CSS 3 selector to select a component, such as <code>query("#id1 grid textbox")</code>. |
− | Then, you could associate the controller (< | + | Then, you could associate the controller (<code>foo.PropertyRetriever</code>) with a component using the <code>apply</code> attribute as shown below. |
− | <source lang="xml"> | + | <source lang="xml" high="3"> |
<window title="Property Retrieval" border="normal"> | <window title="Property Retrieval" border="normal"> | ||
Enter a property name: <textbox id="input"/> | Enter a property name: <textbox id="input"/> | ||
Line 156: | Line 196: | ||
</window> | </window> | ||
</source> | </source> | ||
+ | |||
+ | '''For more information, please refer to [http://books.zkoss.org/wiki/ZK_Getting_Started/Get_ZK_Up_and_Running_with_MVC Get ZK Up and Running with MVC ]''' | ||
==MVC: Autowire UI objects to data members== | ==MVC: Autowire UI objects to data members== | ||
− | Implementing and registering event listeners is a bit tedious. Thus, ZK provides a feature called autowiring. By extending from <javadoc>org.zkoss.zk.ui. | + | Implementing and registering event listeners is a bit tedious. Thus, ZK provides a feature called autowiring. By extending from <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, ZK looks for the members annotated with <code>@Wire</code> or <code>@Listen</code> to match the components. For example, you could rewrite <code>foo.PropertyRetriever</code> by utilizing the autowriing as follows. |
− | <source lang="java" title="PropertyRetriever.java"> | + | <source lang="java" title="PropertyRetriever.java" high="8,9,11,14"> |
package foo; | package foo; | ||
import org.zkoss.zk.ui.Component; | import org.zkoss.zk.ui.Component; | ||
import org.zkoss.zk.ui.event.Event; | import org.zkoss.zk.ui.event.Event; | ||
− | import org.zkoss.zk.ui. | + | import org.zkoss.zk.ui.select.SelectorComposer; |
+ | import org.zkoss.zk.ui.select.annotation.*; | ||
import org.zkoss.zul.*; | import org.zkoss.zul.*; | ||
− | public class PropertyRetriever extends | + | public class PropertyRetriever extends SelectorComposer<Window> { |
− | + | @Wire | |
− | + | Textbox input; //wired to a component called input | |
− | + | @Wire | |
− | + | Vlayout result; //wired to a component called result | |
− | + | ||
− | + | @Listen("onClick=#retrieve") | |
− | + | public void submit(Event event) { //register a listener to a component called retrieve | |
− | + | String prop = System.getProperty(input.getValue()); | |
+ | result.appendChild(new Label(prop)); | ||
+ | } | ||
} | } | ||
</source> | </source> | ||
Line 190: | Line 235: | ||
</source> | </source> | ||
− | As shown above, < | + | As shown above, <code>@Wire</code> will cause <code>input</code> and <code>result</code> to be ''wired'' automatically, such that you could access the components directly. |
− | Also < | + | Also <code>@Listen("onClick=#retrieve")</code> indicates that the annotated method will be registered as an event listener to the component called <code>retrieve</code> to handle the <code>onClick</code> event. |
− | |||
− | |||
− | |||
− | |||
− | + | If the component's ID is different from the member's name or the pattern is complicated, you could specify a CSS 3 selector such as <code>@Wire("#id")</code>, <code>@Wire("window > div > button")</code> and <code>@Listen("onClick = button[label='Clear']")</code>. | |
− | + | You can use with Spring or CDI managed bean in the composer too. For example, | |
− | + | <source lang="java" high="1,3"> | |
+ | @VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver) | ||
+ | public class PasswordSetter extends SelectorComposer<Window> { | ||
+ | @WireVariable //wire Spring managed bean | ||
+ | private User user; | ||
+ | @Wire | ||
+ | private Textbox password; //wired automatically if there is a textbox named password | ||
− | + | @Listen("onClick=#submit") | |
− | + | public void submit() { | |
− | + | user.setPassword(password.getValue()); | |
− | public | ||
− | |||
} | } | ||
} | } | ||
</source> | </source> | ||
− | + | <blockquote style="border:1px solid orange;padding:10px;background-color:#FAF6E3"> | |
+ | '''Notice''' : MVC pattern is recommended for a production application. On the other hand, to maintain readability, many examples in our documents embed code directly into ZUML pages. | ||
+ | </blockquote> | ||
− | + | ==MVVM: Automate the access with data binding== | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | EL expressions are convenient but they are limited to display read-only data. If you allow end users to modify data (such as CRUD), or change how the data can be displayed based on users' selection, you could use ZK data binding to handle the display and modification automatically for you. All you need to do is to implement a so-called ViewModel (a POJO) that provides the data beans and/or describes relationship between UI and data beans<ref>ZK data binding is based on the MVVM design pattern, which is identical to the [http://martinfowler.com/eaaDev/PresentationModel.html Presentation Model] introduced by Martin Fowler. For more information, please refer to [[ZK Developer's Reference/MVVM|ZK Developer's Reference: MVVM]].</ref>. For example,<ref>Here we load the users by assuming there is a utility called <code>Users</code>. However, it is straightforward if you'd like to wire Spring-managed or CDI-managed beans. For more information, please refer to [[ZK Developer's Reference/MVC/Controller/Composer|ZK Developer's Reference: MVC]]</ref>. | |
− | + | <source lang="java"> | |
+ | package foo; | ||
+ | public class UserViewModel { | ||
+ | List<User> users = Users.getAll(); | ||
− | + | public List<User> getUsers() { | |
+ | return users; | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
− | + | Then, you could put them together by applying a built-in composer called <javadoc>org.zkoss.bind.BindComposer</javadoc> in a ZUML document as follows. | |
− | + | <source lang="xml" high="1,2"> | |
− | + | <grid apply="org.zkoss.bind.BindComposer" | |
− | + | viewModel="@id('vm') @init('foo.UserViewModel')" model="@bind(vm.users)"> | |
− | <source lang="xml"> | ||
− | < | ||
− | |||
− | |||
<columns> | <columns> | ||
− | <column label="Name" sort="auto"/> | + | <column label="Name" sort="auto" /> |
− | <column label="Title" sort="auto"/> | + | <column label="Title" sort="auto" /> |
− | <column label="Age" sort="auto"/> | + | <column label="Age" sort="auto" /> |
</columns> | </columns> | ||
− | < | + | <template name="model" var="user"> |
− | <row | + | <row> |
− | <textbox value="@ | + | <textbox value="@bind(user.name)" /> |
− | <textbox value="@ | + | <textbox value="@bind(user.title)" /> |
− | <intbox value="@ | + | <intbox value="@bind(user.age)" /> |
</row> | </row> | ||
− | </ | + | </template> |
</grid> | </grid> | ||
</source> | </source> | ||
− | + | [[File:DgGettingStartedUsers2.png]] | |
− | [[ | + | Please notice that you do not need to write any code to handle the display or modification. Rather, you declare the relation of the UI and data beans in [[ZK Developer's Reference/Annotations|annotations]], such as <code>@bind(user.name)</code>. Any modification made to each input (by the end user) is stored back to the object (<code>foo.User</code>) automatically and vice versa, assuming that the POJO has the required setter methods, such as <code>setName(String)</code>. |
− | + | '''For more information, please refer to [http://books.zkoss.org/wiki/ZK_Getting_Started/Get_ZK_Up_and_Running_with_MVC Get ZK Up and Running with MVVM]''' | |
<blockquote> | <blockquote> | ||
---- | ---- | ||
<references/> | <references/> | ||
+ | </blockquote> | ||
+ | |||
+ | <blockquote style="border:1px solid orange;padding:10px;background-color:#FAF6E3"> | ||
+ | '''Notice''' : | ||
+ | MVVM pattern applies to ZK 6 and later | ||
</blockquote> | </blockquote> | ||
Latest revision as of 07:34, 14 January 2022
This tutorial guides you through the most fundamental features and concepts of ZK.
- To create web application, please refer to Create and Run Your First ZK Application with Eclipse and ZK Studio
- For product description, please refer to the ZK product page and the feature list.
- For a real world example, please refer to Creating a database-driven application.
- For learning step-by-step, please refer to ZK Essentials.
Hello World!
After ZK is installed on your favorite Web server[1], writing applications is straightforward. Just create a ZUML file[2], and name it as hello.zul[3], under one of the Web application's directories just as you would do for an HTML file.
<window title="My First ZK Application" border="normal">
Hello World!
</window>
Assuming the name of the Web project is myapp, then go to the corresponding URL, which is http://localhost/myapp/hello.zul, and you'll see your first ZK application running.
On a ZUML page, an XML element describes what a component[4] can create while the XML attributes are used to assign values to a component's properties. In this example, a window
component is created and its title
is set to "My First ZK Application"
and its border
is set to normal
.
The text enclosed in the XML elements can also be interpreted as a special component called label
. Thus, the above example is equivalent to the following code:
<window title="My First ZK Application" border="normal">
<label value="Hello World!"/>
</window>
- ↑ Please refer to ZK Installation Guide.
- ↑ ZUML [1]
- ↑ The other way to try examples is to use ZK Sandbox to run them.
- ↑ Interface : <javadoc type="interface">org.zkoss.zk.ui.Component
Say Hello in Ajax way
Let us put some interactivity into it.
<button label="Say Hello" onClick='Messagebox.show("Hello World!")'/>
Then, when you click the button, you'll see the following:
The onClick
attribute is a special attribute used to add an event listener(EventListener) to the component such as that it is invoked when an end user clicks the component. The attribute value could be any legal Java code. Notice that it is NOT JavaScript, and you have to use double quotes (") in a string. To escape a double quote in an XML string, you could use single quotes (') to enclose it[1].
Here we invoke Messagebox.show(String) to display a message box shown above.
The Java code is interpreted by BeanShell at runtime. In addition to event handling, you could embed the code in a ZUML page by specifying it in a special element called zscript
. For example, you could simply define a function in the code as the following:
<window title="My First ZK Application" border="normal">
<button label="Say Hello" onClick='alert("Hello World!")'/>
<zscript>
void alert(String message){ //declare a function
Messagebox.show(message);
}
</zscript>
</window>
In fact, alert
is a built-in function that you can use directly from the embedded Java code.
- ↑ If you are not familiar with XML, you might take a look at the XML background section.
It is Java that runs on the server
The embedded Java code runs on the server so as to gain easy access to any resources available on the server. For example,
<window title="Property Retrieval" border="normal">
Enter a property name: <textbox/>
<button label="Retrieve" onClick="alert(System.getProperty(self.getPreviousSibling().getValue()))"/>
</window>
where self
is a built-in variable which refers a component receiving the event.
If you enter java.version
and then click the button, the result will be shown as the following:
A component is a POJO
A component is a POJO. You could instantiate and manipulate them directly. For example, you could generate the result by instantiating component(s) to represent it, and then append them to another component as shown below.
<window title="Property Retrieval" border="normal">
Enter a property name: <textbox id="input"/>
<button label="Retrieve"
onClick="result.appendChild(new Label(System.getProperty(input.getValue())))"/>
<vlayout id="result"/>
</window>
Once appended, the components can be displayed in the browser automatically. Similarly, if components are detached, they are removed from the browser automatically.
In addition, you could change the state of a component directly. All modifications will be synchronized back to the browser automatically.
<window title="Property Retrieval" border="normal">
Enter a property name: <textbox id="input"/>
<button label="Retrieve"
onClick="result.setValue(System.getProperty(input.getValue()))"/>
<separator/>
<label id="result"/>
</window>
A component is a LEGO brick
Instead of introducing different components for different purposes, our components are designed to build blocks. You are free to compose blocks together to realize sophisticated UI without customizing any components. For example, you could put anything in a grid, including grid itself; anything in any layout, including the layout itself. Please see our demo for more examples.
Express data with variable resolver and EL expressions
On a ZUML page, you could locate data with a variable resolver (VariableResolver), and then express it with EL expressions.
For example, assumes that we have a class called foo.Users
, and we can retrieve a list of users by employing its static method called getAll()
. Then, we can implement a variable resolver as follows.
package foo;
public class UserResolver implements org.zkoss.xel.VariableResolver {
public Object resolveVariable(String name) {
return "users".equals(name) ? Users.getAll(): null;
}
}
And, we can list all users as follows.
<?variable-resolver class="foo.UserResolver"?>
<grid>
<columns>
<column label="Name" sort="auto"/>
<column label="Title" sort="auto"/>
<column label="Age" sort="auto"/>
</columns>
<rows>
<row forEach="${users}">
<label value="${each.name}"/>
<label value="${each.title}"/>
<label value="${each.age}"/>
</row>
</rows>
</grid>
There are three methods that we can assume foo.User
: getName()
, getTitle()
and getAge()
. forEach
is used to instantiate components by iterating through a collection of objects.
MVC: Separate code from user interface
Embedding Java code in a ZUML page is straightforward and easy for prototyping. However, in a production environment, it is better to separate the code from user interfaces. The code can be compiled at the development time. It is easier to develop and test, and runs much faster than the embedded code which is interpreted at runtime.
To separate codes from UI, you can implement a Java class (aka., the controller) that implements Composer, and then handle UI in Composer.doAfterCompose(Component). For example, you can redo the previous example by registering an event listener in Composer.doAfterCompose(Component), and then retrieve the result by instantiating a label to represent it in the event listener as follows.
package foo;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Label;
public class PropertyRetriever implements Composer {
public void doAfterCompose(final Component target) { //handle UI here
target.addEventListener("onClick", new EventListener() { //add a event listener in Java
public void onEvent(Event event) {
String prop = System.getProperty(((Textbox)target.query("#input")).getValue());
target.query("#result").appendChild(new Label(prop));
}
});
}
}
As shown, an event listener could be registered with the use of Component.addEventListener(String, EventListener). An event listener must implement EventListener, and then handle the event in EventListener.onEvent(org.zkoss.zk.ui.event.Event.
Also notice that a component could be retrieved with the use of Component.query(String), which allows the developer to use a CSS 3 selector to select a component, such as query("#id1 grid textbox")
.
Then, you could associate the controller (foo.PropertyRetriever
) with a component using the apply
attribute as shown below.
<window title="Property Retrieval" border="normal">
Enter a property name: <textbox id="input"/>
<button label="Retrieve" apply="foo.PropertyRetriever"/>
<vlayout id="result"/>
</window>
For more information, please refer to Get ZK Up and Running with MVC
MVC: Autowire UI objects to data members
Implementing and registering event listeners is a bit tedious. Thus, ZK provides a feature called autowiring. By extending from SelectorComposer, ZK looks for the members annotated with @Wire
or @Listen
to match the components. For example, you could rewrite foo.PropertyRetriever
by utilizing the autowriing as follows.
package foo;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.*;
import org.zkoss.zul.*;
public class PropertyRetriever extends SelectorComposer<Window> {
@Wire
Textbox input; //wired to a component called input
@Wire
Vlayout result; //wired to a component called result
@Listen("onClick=#retrieve")
public void submit(Event event) { //register a listener to a component called retrieve
String prop = System.getProperty(input.getValue());
result.appendChild(new Label(prop));
}
}
and the ZUL page is as follows.
<window title="Property Retrieval" border="normal" apply="foo.PropertyRetriever">
Enter a property name: <textbox id="input"/>
<button label="Retrieve" id="retrieve"/>
<vlayout id="result"/>
</window>
As shown above, @Wire
will cause input
and result
to be wired automatically, such that you could access the components directly.
Also @Listen("onClick=#retrieve")
indicates that the annotated method will be registered as an event listener to the component called retrieve
to handle the onClick
event.
If the component's ID is different from the member's name or the pattern is complicated, you could specify a CSS 3 selector such as @Wire("#id")
, @Wire("window > div > button")
and @Listen("onClick = button[label='Clear']")
.
You can use with Spring or CDI managed bean in the composer too. For example,
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver)
public class PasswordSetter extends SelectorComposer<Window> {
@WireVariable //wire Spring managed bean
private User user;
@Wire
private Textbox password; //wired automatically if there is a textbox named password
@Listen("onClick=#submit")
public void submit() {
user.setPassword(password.getValue());
}
}
Notice : MVC pattern is recommended for a production application. On the other hand, to maintain readability, many examples in our documents embed code directly into ZUML pages.
MVVM: Automate the access with data binding
EL expressions are convenient but they are limited to display read-only data. If you allow end users to modify data (such as CRUD), or change how the data can be displayed based on users' selection, you could use ZK data binding to handle the display and modification automatically for you. All you need to do is to implement a so-called ViewModel (a POJO) that provides the data beans and/or describes relationship between UI and data beans[1]. For example,[2].
package foo;
public class UserViewModel {
List<User> users = Users.getAll();
public List<User> getUsers() {
return users;
}
}
Then, you could put them together by applying a built-in composer called BindComposer in a ZUML document as follows.
<grid apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('foo.UserViewModel')" model="@bind(vm.users)">
<columns>
<column label="Name" sort="auto" />
<column label="Title" sort="auto" />
<column label="Age" sort="auto" />
</columns>
<template name="model" var="user">
<row>
<textbox value="@bind(user.name)" />
<textbox value="@bind(user.title)" />
<intbox value="@bind(user.age)" />
</row>
</template>
</grid>
Please notice that you do not need to write any code to handle the display or modification. Rather, you declare the relation of the UI and data beans in annotations, such as @bind(user.name)
. Any modification made to each input (by the end user) is stored back to the object (foo.User
) automatically and vice versa, assuming that the POJO has the required setter methods, such as setName(String)
.
For more information, please refer to Get ZK Up and Running with MVVM
- ↑ ZK data binding is based on the MVVM design pattern, which is identical to the Presentation Model introduced by Martin Fowler. For more information, please refer to ZK Developer's Reference: MVVM.
- ↑ Here we load the users by assuming there is a utility called
Users
. However, it is straightforward if you'd like to wire Spring-managed or CDI-managed beans. For more information, please refer to ZK Developer's Reference: MVC
Notice : MVVM pattern applies to ZK 6 and later
Define UI in pure Java
In additions to XML, developers could also define UI in pure Java. For example, you could implement the property-retrieval example as follows.
public class PropertyRetrieval extends GenericRichlet {
public void service(Page page) throws Exception {
final Window main = new Window("Property Retrieval", "normal", false);
main.appendChild(new Label("Enter a property name: "));
final Textbox input = new Textbox();
input.setId("input");
main.appendChild(input);
final Button button = new Button("Retrieve");
button.addEventListener("onClick",
new EventListener() {
public void onEvent(Event event) throws Exception {
Messagebox.show(System.getProperty(input.getValue()));
}
});
main.appendChild(button);
main.setPage(page); //attach so it and all descendants will be generated to the client
}
}
A richlet (Richlet) is a small Java program that creates all necessary user interfaces for a given page in response to users' request. Here we extend java.lang.Object from a skeleton called GenericRichlet. Then, we create all the required components in Richlet.service(Page).
Adding client-side functionality
In addition to handling events and components on the server, ZK also provides an option allowing developers to control UI from the client side. We have dubbed this blending of technology, Server+client Fusion.
For example, we could re-implement the Hello World example with the code from the client side as follows.
<button label="Say Hello" w:onClick='jq.alert("Hello World!")' xmlns:w="client"/>
where we declare a XML namespace named client
to indicate the event handler which will be evaluated at the client side. In addition, jq.alert(String, Map) is a client-side method equivalent to Messagebox.show(String).
All components are available and accessible to the client. For example, here is a number guessing game that manipulates UI from the client side.
<window title="Guess a number" border="normal">
<vlayout>
Type number between 0 and 99 and then press Enter to guess:
<intbox w:onOK="guess(this)" xmlns:w="client"/>
</vlayout>
<script><![CDATA[
var num = Math.floor(Math.random() * 100);
function guess(wgt) {
var val = wgt.getValue(),
mesg = val > num ? "smaller than " + val:
val < num ? "larger than "+val: val + " is correct!";
wgt.parent.appendChild(new zul.wgt.Label({value: mesg}));
wgt.setValue("");
}
]]></script>
</window>
where onOK
is an event fired when the user presses Enter
, and script
is used to embed the client-side code (in contrast to zscript
for embedding the server-side code).
Architecture overview
When a ZK application runs on the server, it gives access to backend resources, assemble UI with components, listen to users' activity, and then manipulate components to update UI. All are done on the server. The synchronization of the states of the components between the browser and the server is done automatically by ZK and transparently to the application.
When running on the server, the application can access full Java technology stack. Users' activities, including Ajax and Server Push, are abstracted to event objects. UI are composed of POJO-like components. It is the most productive approach to develop a modern Web application.
With ZK's Server+client Fusion architecture, your application will never stop running on the server. You can enhance your application's interactivity by adding optional client-side functionality, such as client-side event handling, visual effect customizing and even UI composing without server-side coding. ZK is the only framework to enable seamless fusion from pure server-centric to pure client-centric. You can have the best of two worlds: productivity and flexibility.