Chapter 4: Controlling Components"
m (correct highlight (via JWB)) |
|||
(2 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
− | |||
− | |||
Line 17: | Line 15: | ||
* explain example usage | * explain example usage | ||
--> | --> | ||
− | The simplest way to respond to a user's clicking is to write an event listener method and invoke it in the < | + | The simplest way to respond to a user's clicking is to write an event listener method and invoke it in the <code>onClick</code> attribute. We can define an event listener in Java inside a <code><zscript></code> element and those codes will be interpreted when the ZUL is visited. This element also allows other scripting languages such as JavaScript, Ruby, or Groovy. <code><zscript></code> is ''very suitable for fast prototyping'' and it's interpreted when a zul page is evaluated. You can see the changed result without re-deployment. But it has issues in performance and clustering environment, ''we don't recommend to use it in production environment''. |
'''Event listener redirect()''' | '''Event listener redirect()''' | ||
− | <source lang='xml' | + | <source lang='xml' highlight='2,11,14'> |
<grid hflex="1" vflex="1" sclass="sidebar"> | <grid hflex="1" vflex="1" sclass="sidebar"> | ||
<zscript><![CDATA[ | <zscript><![CDATA[ | ||
Line 40: | Line 38: | ||
... | ... | ||
</source> | </source> | ||
− | * Line 2: It's better to enclose your code with < | + | * Line 2: It's better to enclose your code with <code><![CDATA[ ]]></code>. |
* Line 11: Define an event listener method like a normal Java method and it redirects a browser according to the passed variable. | * Line 11: Define an event listener method like a normal Java method and it redirects a browser according to the passed variable. | ||
* Line 14: The [[ZUML_Reference/EL_Expressions/Implicit_Objects/execution| execution]] is a implicit variable which you can use it directly without declaration. It represents an execution of a client request that holds relevant information. | * Line 14: The [[ZUML_Reference/EL_Expressions/Implicit_Objects/execution| execution]] is a implicit variable which you can use it directly without declaration. It represents an execution of a client request that holds relevant information. | ||
− | After defining the event listener, we should specify it in a ''Row'''s event attribute < | + | After defining the event listener, we should specify it in a ''Row'''s event attribute <code>onClick</code> because we want to invoke the event listener when clicking a ''Row''. |
'''Invoke event listeners at "onClick"''' | '''Invoke event listeners at "onClick"''' | ||
− | <source lang="xml" | + | <source lang="xml" highlight='4, 7,10'> |
<grid> | <grid> | ||
... | ... | ||
Line 92: | Line 90: | ||
</source> | </source> | ||
− | Then "connect" the controller with a component in the zul by specifying fully qualified class name in < | + | Then "connect" the controller with a component in the zul by specifying fully qualified class name in <code>apply</code> attribute. After that the component and all its child components are under the control of the controller. |
'''chapter4/sidebar.zul''' | '''chapter4/sidebar.zul''' | ||
Line 113: | Line 111: | ||
== Wire Components == | == Wire Components == | ||
− | To control a component, we must retrieve it first. In <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, when you specify a <code>@Wire</code> annotation on a field or setter method, the SelectorComposer will automatically find the component and assign it to the field or pass it into the setter method. By default < | + | To control a component, we must retrieve it first. In <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc>, when you specify a <code>@Wire</code> annotation on a field or setter method, the SelectorComposer will automatically find the component and assign it to the field or pass it into the setter method. By default <code>SelectorComposer</code> will find the component whose id and type both equal to the variable name and type respectively. |
− | <source lang='java' | + | <source lang='java' highlight="4,5"> |
public class SidebarChapter4Controller extends SelectorComposer<Component>{ | public class SidebarChapter4Controller extends SelectorComposer<Component>{ | ||
Line 126: | Line 124: | ||
</source> | </source> | ||
− | * Line 4,5 : SelectorComposer looks for a ''Grid'' whose id is "fnList" and assign it to the variable < | + | * Line 4,5 : SelectorComposer looks for a ''Grid'' whose id is "fnList" and assign it to the variable <code>fnList</code>. |
== Initialize the View == | == Initialize the View == | ||
− | It is very common that we need to initialize components when a zul file is loaded. In our example, we need to create ''Row''s of the ''Grid'' for the sidebar, therefore we should override a [[ZK_Developer%27s_Reference/MVC/Controller/Composer#Lifecycle| composer life-cycle method]] < | + | It is very common that we need to initialize components when a zul file is loaded. In our example, we need to create ''Row''s of the ''Grid'' for the sidebar, therefore we should override a [[ZK_Developer%27s_Reference/MVC/Controller/Composer#Lifecycle| composer life-cycle method]] <code>doAfterCompose(Component)</code>. The passed argument, <code>comp</code>, is the component that the composer applies to, which in our example is the ''Grid''. This method will be called after all the child components under the component which has the composer applied to it are created, so we can change components' attributes or even create other components in it. |
− | <source lang='java' | + | <source lang='java' highlight='8,12 , 15,16,17,18,19,20'> |
public class SidebarChapter4Controller extends SelectorComposer<Component>{ | public class SidebarChapter4Controller extends SelectorComposer<Component>{ | ||
Line 156: | Line 154: | ||
} | } | ||
</source> | </source> | ||
− | * Line 8: Here we demonstrate a configurable architecture, the < | + | * Line 8: Here we demonstrate a configurable architecture, the <code>SidebarPageConfig</code> stores hyperlink's configuration such as URL, and label and we use this configuration to create and setup components in the sidebar. |
− | * Line 12: You have to call super class < | + | * Line 12: You have to call super class <code>doAfterCompose()</code> method , because it performs initialization like wiring components for you. |
* Line 15 - 20: These codes involve the concept that we have not talked about yet. All you have to know for now is these codes create ''Row''s with event listeners and put them into ''Grid''. We will discuss them in next section. | * Line 15 - 20: These codes involve the concept that we have not talked about yet. All you have to know for now is these codes create ''Row''s with event listeners and put them into ''Grid''. We will discuss them in next section. | ||
== Events & Event Listeners == | == Events & Event Listeners == | ||
− | A ZK event (<javadoc>org.zkoss.zk.event.Event</javadoc>) is an abstraction of an activity made by user, a notification made by an application, and an invocation of server push. For example, a user clicks a button on the browser, this will trigger < | + | A ZK event (<javadoc>org.zkoss.zk.event.Event</javadoc>) is an abstraction of an activity made by user, a notification made by an application, and an invocation of server push. For example, a user clicks a button on the browser, this will trigger <code>onClick</code> sent to the server. If there is an event listener registered for the button's <code>onClick</code> event, ZK will pass the event to the listener to handle it. The event-listener mechanism allows us to handle all user interaction at server side. |
== Create Components & Event Listeners Programmatically == | == Create Components & Event Listeners Programmatically == | ||
Line 175: | Line 173: | ||
− | In < | + | In <code>constructSidebarRow()</code> method, we create ''Row''s and add an event listener to each of them. |
− | <source lang="java" | + | <source lang="java" highlight='21,28,32,36,39,44,48'> |
public class SidebarChapter4Controller extends SelectorComposer<Component>{ | public class SidebarChapter4Controller extends SelectorComposer<Component>{ | ||
Line 236: | Line 234: | ||
* Line 32: Append a component to establish the parent-child relationship. | * Line 32: Append a component to establish the parent-child relationship. | ||
* Line 36: You can change a component's attributes by various setter methods and their method names correspond to tag's attribute name. | * Line 36: You can change a component's attributes by various setter methods and their method names correspond to tag's attribute name. | ||
− | * Line 39: We create an < | + | * Line 39: We create an <code>EventListener</code> anonymous class for convenience. Under a clustering environment, your event listener class should implement <javadoc>org.zkoss.zk.ui.event.SerializableEventListener</javadoc>. |
− | * Line 44: Implement the business logic in< | + | * Line 44: Implement the business logic in<code>onEvent()</code> method, and this method will be called if the listened event is sent to the server. Here we get current execution by <javadoc>org.zkoss.zk.ui.Executions</javadoc> and redirect a client to a new URL. |
− | * Line 48: Apply the event listener to a ''Row'' for listening < | + | * Line 48: Apply the event listener to a ''Row'' for listening <code>Events.ON_CLICK</code> event which is triggered by a mouse clicking action. |
In Line 28 ~ 36, those codes work equally to writing in a zul as follows: | In Line 28 ~ 36, those codes work equally to writing in a zul as follows: | ||
Line 248: | Line 246: | ||
</source> | </source> | ||
− | After completing above steps, when a user clicks a ''Row'' on the sidebar, ZK will call a corresponding < | + | After completing above steps, when a user clicks a ''Row'' on the sidebar, ZK will call a corresponding <code>actionListener </code> then the browser will be redirected to a specified URL. You can see the result via ''http://localhost:8080/essentials/chapter4''. |
+ | |||
+ | |||
+ | = Source Code = | ||
+ | |||
+ | * [https://github.com/zkoss/zkessentials/tree/master/src/main/webapp/chapter4 ZUL pages] | ||
+ | * [https://github.com/zkoss/zkessentials/tree/master/src/main/java/org/zkoss/essentials/chapter4 Java] | ||
Latest revision as of 10:50, 19 January 2022
This article is out of date, please refer to http://books.zkoss.org/zkessentials-book/master/ for more up to date information.
ZK's components are not only for constructing the user interface, we even can control them. In this chapter, we continue to use the last chapter's example but we will remove the 3 items with hyper links in the sidebar and replace them with a redirecting action. To achieve this, we will write code in Java for each item to respond to a user's clicking and redirect the user to an external site.
In Zscript
The simplest way to respond to a user's clicking is to write an event listener method and invoke it in the onClick
attribute. We can define an event listener in Java inside a <zscript>
element and those codes will be interpreted when the ZUL is visited. This element also allows other scripting languages such as JavaScript, Ruby, or Groovy. <zscript>
is very suitable for fast prototyping and it's interpreted when a zul page is evaluated. You can see the changed result without re-deployment. But it has issues in performance and clustering environment, we don't recommend to use it in production environment.
Event listener redirect()
<grid hflex="1" vflex="1" sclass="sidebar">
<zscript><![CDATA[
//zscript code, it runs on server site, use it for fast prototyping
java.util.Map sites = new java.util.HashMap();
sites.put("zk","http://www.zkoss.org/");
sites.put("demo","http://www.zkoss.org/zkdemo");
sites.put("devref","http://books.zkoss.org/wiki/ZK_Developer's_Reference");
void redirect(String name){
String loc = sites.get(name);
if(loc!=null){
execution.sendRedirect(loc);
}
}
]]></zscript>
...
- Line 2: It's better to enclose your code with
<![CDATA[ ]]>
. - Line 11: Define an event listener method like a normal Java method and it redirects a browser according to the passed variable.
- Line 14: The execution is a implicit variable which you can use it directly without declaration. It represents an execution of a client request that holds relevant information.
After defining the event listener, we should specify it in a Row's event attribute onClick
because we want to invoke the event listener when clicking a Row.
Invoke event listeners at "onClick"
<grid>
...
<rows>
<row sclass="sidebar-fn" onClick='redirect("zk")'>
<image src="/imgs/site.png"/> ZK
</row>
<row sclass="sidebar-fn" onClick='redirect("demo")'>
<image src="/imgs/demo.png"/> ZK Demo
</row>
<row sclass="sidebar-fn" onClick='redirect("devref")'>
<image src="/imgs/doc.png"/> ZK Developer Reference
</row>
</rows>
</grid>
Now if you click a Row of the Grid in the sidebar, your browser will be redirected to a corresponding site.
This approach is very simple and fast, so it is especially suitable for building a prototype. However, if you need a better architecture for your application, you had better not use ZScript.
In Controller
In this section, we will demonstrate how to redirect users to an external site with event listeners in a Controller when they click an item in the sidebar.
The most commonly used architecture in web applications is MVC (Model-View-Controller) which separates an application into 3 parts. The Model is responsible for exposing data while performing business logic which is usually implemented by users, and the View is responsible for displaying data which is what ZUL does. The Controller can change the View's presentation and handle events from the View. The benefit of designing an application in MVC architecture is that your application is more modularized.
In ZK world, a Composer plays the same role as the Controller and you can assign it to a target component. Through the composer, you can listen events of the target component and manipulate target component's child components to change View's presentation according to your requirement. To create a Controller in ZK you simply create a class that inherits SelectorComposer.
public class SidebarChapter4Controller extends SelectorComposer<Component>{
//other codes...
}
Then "connect" the controller with a component in the zul by specifying fully qualified class name in apply
attribute. After that the component and all its child components are under the control of the controller.
chapter4/sidebar.zul
<grid hflex="1" vflex="1" sclass="sidebar"
id="fnList"
apply="org.zkoss.essentials.chapter4.SidebarChapter4Controller">
<columns>
<column width="36px"/>
<column/>
</columns>
<rows/>
</grid>
- Line 2: A component id can be used to retrieve the component in a composer, please see the next section.
- Line 3: Apply a controller to a component.
- Line 8: Here we don't create 3 Rows in the zul because we need to add an event listener programmatically on each Row in the composer.
Wire Components
To control a component, we must retrieve it first. In SelectorComposer, when you specify a @Wire
annotation on a field or setter method, the SelectorComposer will automatically find the component and assign it to the field or pass it into the setter method. By default SelectorComposer
will find the component whose id and type both equal to the variable name and type respectively.
public class SidebarChapter4Controller extends SelectorComposer<Component>{
//wire components
@Wire
Grid fnList;
...
}
- Line 4,5 : SelectorComposer looks for a Grid whose id is "fnList" and assign it to the variable
fnList
.
Initialize the View
It is very common that we need to initialize components when a zul file is loaded. In our example, we need to create Rows of the Grid for the sidebar, therefore we should override a composer life-cycle method doAfterCompose(Component)
. The passed argument, comp
, is the component that the composer applies to, which in our example is the Grid. This method will be called after all the child components under the component which has the composer applied to it are created, so we can change components' attributes or even create other components in it.
public class SidebarChapter4Controller extends SelectorComposer<Component>{
//wire components
@Wire
Grid fnList;
//services
SidebarPageConfig pageConfig = new SidebarPageConfigChapter4Impl();
@Override
public void doAfterCompose(Component comp) throws Exception{
super.doAfterCompose(comp);
//initialize view after view construction.
Rows rows = fnList.getRows();
for(SidebarPage page:pageConfig.getPages()){
Row row = constructSidebarRow(page.getLabel(),page.getIconUri(),page.getUri());
rows.appendChild(row);
}
}
}
- Line 8: Here we demonstrate a configurable architecture, the
SidebarPageConfig
stores hyperlink's configuration such as URL, and label and we use this configuration to create and setup components in the sidebar. - Line 12: You have to call super class
doAfterCompose()
method , because it performs initialization like wiring components for you. - Line 15 - 20: These codes involve the concept that we have not talked about yet. All you have to know for now is these codes create Rows with event listeners and put them into Grid. We will discuss them in next section.
Events & Event Listeners
A ZK event (Event) is an abstraction of an activity made by user, a notification made by an application, and an invocation of server push. For example, a user clicks a button on the browser, this will trigger onClick
sent to the server. If there is an event listener registered for the button's onClick
event, ZK will pass the event to the listener to handle it. The event-listener mechanism allows us to handle all user interaction at server side.
Create Components & Event Listeners Programmatically
Manipulating components is the most powerful feature of ZK. You can change the user interface by creating, removing, or changing components and all changes you made will reflect to clients.
Now we are going to explain how to create components and add an event listener to respond to users' clicking. Basically, there are 3 steps to create a component:
- Create a component object.
- Setup the component's attributes.
- Append to the target parent component.
In constructSidebarRow()
method, we create Rows and add an event listener to each of them.
public class SidebarChapter4Controller extends SelectorComposer<Component>{
//...
//wire components
@Wire
Grid fnList;
//services
SidebarPageConfig pageConfig = new SidebarPageConfigChapter4Impl();
@Override
public void doAfterCompose(Component comp) throws Exception{
super.doAfterCompose(comp);
//initialize view after view construction.
Rows rows = fnList.getRows();
for(SidebarPage page:pageConfig.getPages()){
Row row = constructSidebarRow(page.getLabel(),page.getIconUri(),page.getUri());
rows.appendChild(row);
}
}
private Row constructSidebarRow(String name,String label, String imageSrc, final String locationUri) {
//construct component and hierarchy
Row row = new Row();
Image image = new Image(imageSrc);
Label lab = new Label(label);
row.appendChild(image);
row.appendChild(lab);
//set style attribute
row.setSclass("sidebar-fn");
//create and register event listener
EventListener<Event> actionListener = new SerializableEventListener<Event>() {
private static final long serialVersionUID = 1L;
public void onEvent(Event event) throws Exception {
//redirect current url to new location
Executions.getCurrent().sendRedirect(locationUri);
}
};
row.addEventListener(Events.ON_CLICK, actionListener);
return row;
}
}
- Line 21: Append a newly-created Row will make it become a child component of Rows.
- Line 28: The first step to create a component is instantiating its class.
- Line 32: Append a component to establish the parent-child relationship.
- Line 36: You can change a component's attributes by various setter methods and their method names correspond to tag's attribute name.
- Line 39: We create an
EventListener
anonymous class for convenience. Under a clustering environment, your event listener class should implement SerializableEventListener. - Line 44: Implement the business logic in
onEvent()
method, and this method will be called if the listened event is sent to the server. Here we get current execution by Executions and redirect a client to a new URL. - Line 48: Apply the event listener to a Row for listening
Events.ON_CLICK
event which is triggered by a mouse clicking action.
In Line 28 ~ 36, those codes work equally to writing in a zul as follows:
<row sclass="sidebar-fn">
<image/><label/>
</row>
After completing above steps, when a user clicks a Row on the sidebar, ZK will call a corresponding actionListener
then the browser will be redirected to a specified URL. You can see the result via http://localhost:8080/essentials/chapter4.
Source Code