Book not found

From Documentation
⧼coll-notfound_msg⧽

Return to Documentation.

DocumentationZK EssentialsChapter 4: Controlling Components
Chapter 4: Controlling Components



WarningTriangle-32x32.png This page is under construction, so we cannot guarantee the accuracy of the content!



ZK's Components are not just only for constructing user interface, we even can control them. In this chapter, we continue to use the last chapter's example but we remove 3 items with hyper links in the sidebar and replace them with redirecting action. To achieve this, we should write codes in Java for each item to response a user's clicking and redirect the user to an external site.

In Zscript

The simplest way to response a user's clicking is to write a event listener method and invoke it in onClick attribute. We could define a event listener in Java inside a <zscript> element and those codes will be interpreted when the ZUL is visited. This element also allows other script language like 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 a event listener method like normal Java method and it redirects a browser according to passed key.
  • 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>

Through above steps, now if you click a Row of the Grid in the sidebar, your browser will be redirected to corresponding site.

This approach is very simple and fast, so it is especially suitable for building prototype. But if you need a better architecture for your application, you had better separate these codes from a ZUL.

In Controller

In this section, we will demonstrate how to redirect users to external site with a event listeners in a Controller when they click an item in the sidebar.

The most commonly-used architecture to divide an web application is MVC (Model-View-Controller) which separates an application into 3 parts. The Model is responsible for exposing data and 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, there is 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 is simply creating a class that inherits SelectorComposer.

public class SidebarChapter4Controller extends SelectorComposer<Component>{
	//other codes...
}

Then you have to "connect" the controller with a component in the zul by specifying full qualified class name in apply attribute. Then, the component we apply the controller and all its child components are under dominance of the controller.

chapter4/sidebar.zul

<grid hflex="1" vflex="1" sclass="sidebar" 
	id="fnList" 
	apply="org.zkoss.tutorial.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 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, that is, Grid in our example. This method will be called after the applied component's all child components are created, so we can change components' attributes or even creates 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, and this will trigger onClick sent to the server. If there is a 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 Dynamically

Manipulating components is the most powerful feature of ZK. You can change the user interface as you want by creating, removing, or changing components and all change you made will reflect to clients.

Now we are going to explain how to create components and add event listener to response users' clicking. Basically, there are 3 steps to create a component:

  1. Create a component object.
  2. Setup the component's attributes.
  3. 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 convenient. Under a clustering environment, your event listener class should implement SerializableEventListener.
  • Line 44: Implement the business logic inonEvent() 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 click a Row of 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/tutorial/chapter4.




Last Update : 2013/02/26

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