Chapter 4: Controlling Components"

From Documentation
Line 67: Line 67:
 
* event listener
 
* event listener
 
-->
 
-->
In this section, we will demonstrate how to redirect users to external site with a Controller when they click an item in the sidebar.
+
In this section, we will demonstrate how to redirect users to external site with a '''Controller''' and '''event listeners''' 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 perform 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.  
 
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 perform 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.  
Line 145: Line 145:
 
* Line 8: Here we demonstrate a configurable architecture, the <tt>SidebarPageConfig</tt> stores hyperlink's configuration such as URL, and label and we use this configuration to create and setup components in the sidebar.
 
* Line 8: Here we demonstrate a configurable architecture, the <tt>SidebarPageConfig</tt> 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 <tt>doAfterCompose()</tt> method , because it performs initialization like wiring components for you.
 
* Line 12: You have to call super class <tt>doAfterCompose()</tt> method , because it performs initialization like wiring components for you.
* Line 15 - 20: These codes involves concept of creating component dynamically that we have not talked about. All you have to know for now is these codes create ''Row'' with event listeners and put them into ''Grid''.
+
* 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'' with event listeners and put them into ''Grid''. We will discuss them in next section.
  
 
== Create Components & Event Listeners Dynamically ==
 
== Create Components & Event Listeners Dynamically ==
  
<source lang="java">
+
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:
 +
# Create a component object.
 +
# Setup the component's attribute.
 +
# Append to the target parent component.
 +
 
 +
In <tt>constructSidebarRow()</tt> method, we create ''Row''s and add an event listener to each of them.
 +
 
 +
<source lang="java" high='17,21,28,32,36,39,44,48'>
 
public class SidebarChapter4Controller extends SelectorComposer<Component>{
 
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) {
 
private Row constructSidebarRow(String name,String label, String imageSrc, final String locationUri) {

Revision as of 08:10, 15 January 2013

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 item's 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.

Control Components 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.

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 11: Define a event listener method like normal Java method and it redirects a browser according to passed key.
  • Line 14: 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 the event attribute onClick because we want to invoke the event listener when clicking.

<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.

Control Components in Controller

In this section, we will demonstrate how to redirect users to external site with a Controller and event listeners 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 perform 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. Through the composer, you can listen events sent from widgets and manipulate 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 composer with a component in the zul by specifying full qualified class name in apply attribute.

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 3: A component id can be used to retrieve the component in a composer, please see the next section.
  • 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 equals to the variable name.

public class SidebarChapter4Controller extends SelectorComposer<Component>{

	//wire components
	@Wire
	Grid fnList;

	...
}
  • Line 4: 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 Component 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 Row with event listeners and put them into Grid. We will discuss them in next section.

Create Components & Event Listeners Dynamically

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 attribute.
  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;
	}

}