Chapter 7: Navigation and Templating"

From Documentation
m (correct highlight (via JWB))
 
(29 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{TutorialPageHeader}}
+
{{ZKEssentialsPageHeader}}
{{UnderConstruction}}
 
  
  
Line 10: Line 9:
 
* ZK's desktop concept
 
* ZK's desktop concept
 
-->
 
-->
In traditional navigation, a user usually swiches to different functions by visiting different pages of an application, a.k.a page-based navigation. In ZK, we can have another choice to design the navigation in AJAX-based way which users don't need to visit different pages. In page-based navigation, users need to swich pages frequently, we should maintain a consistent page design throughout whole application to help users keep track of where they are. Luckily ZK provides '''Templating''' to keep multiple pages in the same style easily.  
+
In traditional navigation, a user usually switches to different functions by visiting different pages of an application, a.k.a page-based navigation. In ZK, we can have another choice to design the navigation in AJAX-based where users don't need to visit different pages. In page-based navigation, users need to switch pages frequently, we should maintain a consistent page design throughout whole application to help users keep track of where they are. Luckily ZK provides '''Templating''' to keep multiple pages in the same style easily.  
  
 
In this chapter, the example application we are going to build looks as follows:
 
In this chapter, the example application we are going to build looks as follows:
Line 16: Line 15:
 
[[File:Tutorial-ch7-ajax-based.png | center | 600px]]
 
[[File:Tutorial-ch7-ajax-based.png | center | 600px]]
  
The sidebar is used for navigation control. The lower 4 menu items lead you to different functions and they only change the central area's content. All other areas keep unchanged to form a consistent layout style among functions.
+
The sidebar is used for navigation control. The lower 4 menu items lead you to different functions and they only change the central area's content. All other areas are unchanged to maintain a consistent layout style among functions.
  
 
= Templating =
 
= Templating =
Line 24: Line 23:
 
* usage
 
* usage
 
-->
 
-->
In our example application, we want to keep the header, the sidebar, and the footer unchanged whatever function a user chooses. Only the central area changes its content according to the function chosen by users from the sidebar.
+
In our example application, we want to keep the header, the sidebar, and the footer unchanged regardless of which function a user chooses. Only the central area changes its content according to the function chosen by users.
  
If we put each function in a separated page, to keep mutiple pages in a consistent style, copying the duplicated part from one zul to another is hard to maintain. Fortunately, ZK provides a [[ZK Developer%27s Reference/UI_Patterns/Templating/Composition| Templating]] technique that let you define a template zul and apply it to multiple zul pages afterward. All zul pages that apply the same template zul have same layout, so change the template zul can change the layout of all pages once.
+
In page-based navigation, each function is put into a separated page and we need to have a consistent style. One way is to copy the duplicated part from one zul to another, but it is hard to maintain. Fortunately, ZK provides a [[ZK Developer%27s Reference/UI_Patterns/Templating/Composition| Templating]] technique that lets you define a template zul and apply it to multiple zul pages afterwards. All zul pages that apply the same template zul have the same layout, so changing the template zul can change the layout of all pages once.
  
 
The steps to use templating are:
 
The steps to use templating are:
Line 36: Line 35:
 
== Create a Template ZUL File ==
 
== Create a Template ZUL File ==
  
Creating a template is nothing different from creating a normal zul, but you should define one or more anchor by specifying annotation <tt>@inser()</tt> at <tt>self</tt>. You can give any name to identify an anchor that will be used to insert a zul fragment with the same anchor name later.
+
Creating a template is nothing different from creating a normal zul, but you should define one or more anchors by specifying annotation <code>@insert()</code> at <code>self</code>. You can give any name to identify an anchor that will be used to insert a zul fragment with the same anchor name later.
  
'''chapter7/pagebase/layout/template.zul'''
+
'''chapter7/pagebased/layout/template.zul'''
<source lang='xml' high='9'>
+
<source lang='xml' highlight='10,11'>
 
<zk>
 
<zk>
 
<borderlayout hflex="1" vflex="1">
 
<borderlayout hflex="1" vflex="1">
Line 45: Line 44:
 
<include src="/chapter3/banner.zul"/>
 
<include src="/chapter3/banner.zul"/>
 
</north>
 
</north>
<west width="260px" border="none" collapsible="true" splittable="true" minsize="300">
+
<west width="260px" border="none" collapsible="true"  
<include src="/chapter7/pagebase/layout/sidebar.zul"/>
+
splittable="true" minsize="300">
 +
<include src="/chapter7/pagebased/layout/sidebar.zul"/>
 
</west>
 
</west>
<center id="mainContent" autoscroll="true" border="none" self="@insert(content)">
+
<center id="mainContent" autoscroll="true" border="none"  
 +
self="@insert(content)">
 
</center>
 
</center>
 
<south height="50px" border="none">
 
<south height="50px" border="none">
Line 56: Line 57:
 
</zk>
 
</zk>
 
</source>
 
</source>
* Line 9: Define an anchor with name <tt>content</tt>
+
* Line 10,11: Define an anchor with name <code>content</code>
  
 
== Apply the Template ==
 
== Apply the Template ==
Line 62: Line 63:
 
After creating a template zul, we can apply it in another zul with directive  
 
After creating a template zul, we can apply it in another zul with directive  
  
<tt>&lt;?init class="org.zkoss.zk.ui.util.Composition" arg0="template_path"?&gt;</tt>
+
<code>&lt;?init class="org.zkoss.zk.ui.util.Composition" arg0="template_path"?&gt;</code>
  
This directive tells ZK that this page uses the specified template. Then we also have to define zul fragments that are inserted to the anchor in the template zul with annotation <tt>@define(anchorName)</tt> at <tt>self</tt> attribute. The <tt>anchorName</tt> you specify should correspond to one of anchors defined in the template zul.
+
This directive tells ZK that this page uses the specified template. Then we also have to define zul fragments that are inserted to the anchor in the template zul with annotation <code>@define(anchorName)</code> at <code>self</code> attribute. The <code>anchorName</code> you specify should correspond to one of anchors defined in the template zul.
  
'''chapter7/pagebase/index.zul'''
+
'''chapter7/pagebased/index.zul'''
<source lang='xml' high='2, 4'>
+
<source lang='xml' highlight='2,3,5'>
  
 
<?link rel="stylesheet" type="text/css" href="/style.css"?>
 
<?link rel="stylesheet" type="text/css" href="/style.css"?>
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/chapter7/pagebase/layout/template.zul"?>
+
<?init class="org.zkoss.zk.ui.util.Composition"  
 +
arg0="/chapter7/pagebased/layout/template.zul"?>
 
<zk>
 
<zk>
<include self="@define(content)" src="/chapter7/pagebase/home.zul"/>
+
<include self="@define(content)" src="/chapter7/pagebased/home.zul"/>
 
</zk>
 
</zk>
 
</source>
 
</source>
* Line 2: Tell ZK that we want to use a template zul for current page and give the path of template zul.
+
* Line 2,3: Tell ZK that we want to use a template zul for current page and give the path of template zul.
* Line 4: The anchor name <tt>content</tt> correspond to the anchor name defined in the template zul in previous section.
+
* Line 5: The anchor name <code>content</code> correspond to the anchor name defined in the template zul in previous section.
  
  
After above steps, when you visit http://localhost:8080/tutorial/chapter7/pagebase/index.zul, ZK will render the page based on <tt>template.zul</tt> and attach ''Include'' component to be the children of <tt>&lt;center&gt;</tt>.
+
After above steps, when you visit http://localhost:8080/essentials/chapter7/pagebased/index.zul, ZK will render the page based on <code>template.zul</code> and attach the ''Include'' component to <code>&lt;center&gt;</code>.
  
 
= Page-based Navigation =
 
= Page-based Navigation =
  
Traditional web applications are usually designed as page-based navigation. Each function corresponds to a independent page with independent URL. It is very clear for users to know where they are from the URL and they can press "go back" button on their browser to go back to previous pages in history. But the drawback is users have to wait whole page reloading every time they switch to a function. Besides, developers also have to maintain multiple pages that have similar contents but applying a template zul can reduce this problem.
+
Traditional web applications are usually designed with page-based navigation. Each function corresponds to an independent page with independent URL. The navigation is very clear for users as they know where they are from the URL and they can press "go back" button on their browser to go back to previous pages in history. But the drawback is users have to wait whole page reloading every time they switch to a function. Additionally, developers also have to maintain multiple pages that have similar contents but applying a template zul can reduce this problem.
  
[[File:Tutorial-ch7-page-based-navigation.png | center | ]]
+
[[File:Tutorial-ch7-page-based-navigation.png | center | 600px]]
 
<div style="text-align:center">'''Page-based Navigation'''</div>
 
<div style="text-align:center">'''Page-based Navigation'''</div>
  
Line 93: Line 95:
 
[[File:Tutorial-ch7-pagebased.png | center]]
 
[[File:Tutorial-ch7-pagebased.png | center]]
  
From above image, you can see there are four zul pages under "chpater7\pagebase" (index-profile-mvc.zul, index-profile-mvvm.zul, index-todolist-mvc.zul, index-todolist-mvvm.zul) and each page corresponds to each item in the sidebar. Then we can link four items of the sidebar to these zul pages by redirecting a browser.
+
From above image, you can see there are four zul pages which correspond to items in the sidebar under "chpater7\pagebased" (index-profile-mvc.zul, index-profile-mvvm.zul, index-todolist-mvc.zul, index-todolist-mvvm.zul). Then we can link four items of the sidebar to these zul pages by redirecting a browser.
  
 
Next, we apply the template zul created before on those 4 pages.
 
Next, we apply the template zul created before on those 4 pages.
  
'''/chapter7/pagebase/index-profile-mvc.zul'''
+
'''/chapter7/pagebased/index-profile-mvc.zul'''
<source lang='xml' high='2,4'>
+
<source lang='xml' highlight='2,3,5'>
 
<?link rel="stylesheet" type="text/css" href="/style.css"?>
 
<?link rel="stylesheet" type="text/css" href="/style.css"?>
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/chapter7/pagebase/layout/template.zul"?>
+
<?init class="org.zkoss.zk.ui.util.Composition"  
 +
arg0="/chapter7/pagebased/layout/template.zul"?>
 
<zk>
 
<zk>
 
<include self="@define(content)" src="/chapter5/profile-mvc.zul"/>
 
<include self="@define(content)" src="/chapter5/profile-mvc.zul"/>
 
</zk>
 
</zk>
 
</source>
 
</source>
* Line 2: Apply the template in <tt>/chapter7/pagebase/layout/template.zul</tt>.
+
* Line 2,3: Apply the template in <code>/chapter7/pagebased/layout/template.zul</code>.
* Line 4: Define a zul fragment for the anchor <tt>content</tt>.
+
* Line 5: Define a zul fragment for the anchor <code>content</code>.
  
  
Line 112: Line 115:
  
 
'''Page-based navigation's sidebar configuration'''
 
'''Page-based navigation's sidebar configuration'''
<source lang='java' high='9,10, 11,12'>
+
<source lang='java' highlight='10, 11,12,13,14,15,16,17'>
public class SidebarPageConfigPagebaseImpl implements SidebarPageConfig{
+
public class SidebarPageConfigPagebasedImpl implements SidebarPageConfig{
 
 
 
HashMap<String,SidebarPage> pageMap = new LinkedHashMap<String,SidebarPage>();
 
HashMap<String,SidebarPage> pageMap = new LinkedHashMap<String,SidebarPage>();
public SidebarPageConfigPagebaseImpl(){
+
public SidebarPageConfigPagebasedImpl(){
 
pageMap.put("zk",new SidebarPage("zk","www.zkoss.org","/imgs/site.png","http://www.zkoss.org/"));
 
pageMap.put("zk",new SidebarPage("zk","www.zkoss.org","/imgs/site.png","http://www.zkoss.org/"));
 
pageMap.put("demo",new SidebarPage("demo","ZK Demo","/imgs/demo.png","http://www.zkoss.org/zkdemo"));
 
pageMap.put("demo",new SidebarPage("demo","ZK Demo","/imgs/demo.png","http://www.zkoss.org/zkdemo"));
pageMap.put("devref",new SidebarPage("devref","ZK Developer Reference","/imgs/doc.png","http://books.zkoss.org/wiki/ZK_Developer's_Reference"));
+
pageMap.put("devref",new SidebarPage("devref","ZK Developer Reference","/imgs/doc.png"
 +
,"http://books.zkoss.org/wiki/ZK_Developer's_Reference"));
 
 
pageMap.put("fn1",new SidebarPage("fn1","Profile (MVC)","/imgs/fn.png","/chapter7/pagebase/index-profile-mvc.zul"));
+
pageMap.put("fn1",new SidebarPage("fn1","Profile (MVC)","/imgs/fn.png"
pageMap.put("fn2",new SidebarPage("fn2","Profile (MVVM)","/imgs/fn.png","/chapter7/pagebase/index-profile-mvvm.zul"));
+
,"/chapter7/pagebased/index-profile-mvc.zul"));
pageMap.put("fn3",new SidebarPage("fn3","Todo List (MVC)","/imgs/fn.png","/chapter7/pagebase/index-todolist-mvc.zul"));
+
pageMap.put("fn2",new SidebarPage("fn2","Profile (MVVM)","/imgs/fn.png"
pageMap.put("fn4",new SidebarPage("fn4","Todo List (MVVM)","/imgs/fn.png","/chapter7/pagebase/index-todolist-mvvm.zul"));
+
,"/chapter7/pagebased/index-profile-mvvm.zul"));
 +
pageMap.put("fn3",new SidebarPage("fn3","Todo List (MVC)","/imgs/fn.png"
 +
,"/chapter7/pagebased/index-todolist-mvc.zul"));
 +
pageMap.put("fn4",new SidebarPage("fn4","Todo List (MVVM)","/imgs/fn.png"
 +
,"/chapter7/pagebased/index-todolist-mvvm.zul"));
 
}
 
}
 
 
Line 131: Line 139:
 
}
 
}
 
</source>
 
</source>
* Line 9~12: Specify URL and related data for each menu item's configuration.
+
* Line 10~17: Specify URL and related data for each menu item's configuration.
  
The following codes show how to redirect user to a independent page when they click a menu item in the sidebar.
+
The following codes show how to redirect a user to an independent page when they click a menu item in the sidebar.
  
 
'''Controller for page-based navigation'''
 
'''Controller for page-based navigation'''
<source lang='java' high='15,39'>
+
<source lang='java' highlight='15,39'>
public class SidebarPagebaseController extends SelectorComposer<Component>{
+
public class SidebarPagebasedController extends SelectorComposer<Component>{
  
 
...
 
...
  
 
//wire service
 
//wire service
SidebarPageConfig pageConfig = new SidebarPageConfigPagebaseImpl();
+
SidebarPageConfig pageConfig = new SidebarPageConfigPagebasedImpl();
 
 
 
@Override
 
@Override
Line 189: Line 197:
  
  
Visit http://localhost:8080/tutorial/chapter7/pagebase/index.zul. You will see the URL changes and whole page reloads when each time you click a different menu item.
+
Visit http://localhost:8080/essentials/chapter7/pagebased/index.zul. You will see the URL changes and whole page reloads each time you click a different menu item.
  
 
= AJAX-based Navigation =
 
= AJAX-based Navigation =
 
<!--
 
<!--
A ZK page (<javadoc>org.zkoss.zk.ui.Page</javadoc>) is a collection of components. Only those components attached to a page are available at the client and you can get a <tt>Page</tt> object by <tt>Component.getPage()</tt>. A desktop (<javadoc>org.zkoss.zk.ui.Desktop</javadoc>) is a collection of ZK pages. Both a desktop and a ZK page are created automatically when ZK loads a zul page, and it won't re-create a new desktop until the browser reloads the whole page.
+
A ZK page (<javadoc>org.zkoss.zk.ui.Page</javadoc>) is a collection of components. Only those components attached to a page are available at the client and you can get a <code>Page</code> object by <code>Component.getPage()</code>. A desktop (<javadoc>org.zkoss.zk.ui.Desktop</javadoc>) is a collection of ZK pages. Both a desktop and a ZK page are created automatically when ZK loads a zul page, and it won't re-create a new desktop until the browser reloads the whole page.
 
-->
 
-->
  
When switching between different functions in paged-based navigation, you can find that only central area's content is different among those pages and other three areas (header, sidebar, and footer) contain identical content. But in page-based navigation, a browser have to reload all contents no matter they are identical to previous page when switching to another function. With AJAX's help, ZK allows you to implement another navigation way that only updates necessary part of a page instead of reloading the whole page.  
+
When switching between different functions in paged-based navigation, you find that only central area's content is different among those pages and other three areas (header, sidebar, and footer) contain identical content. But in page-based navigation, a browser has to reload all contents no matter they are identical to previous page when switching to another function. With AJAX's help, ZK allows you to implement another navigation way that only updates necessary part of a page instead of reloading the whole page.  
  
  
Line 203: Line 211:
  
  
The easiest way to implement AJAX-based navigation is changing <tt>src</tt> attribute of ''Include'' component. It can change only partial content of an page instead of redirecting to another page to achieve the navigation purpose. This navigation way switches functions by only replacing a group of components instead of whole page and therefore has faster response than page-based one. But it doesn't change a browser's URL after switching to a different function. However, if you want users can keep track of different functions with bookmark, please refer to [[ZK_Developer's_Reference/UI_Patterns/Browser_History_Management| Browser History Management]].
+
The easiest way to implement AJAX-based navigation is changing <code>src</code> attribute of ''Include'' component. It can change only partial content of an page instead of redirecting to another page to achieve the navigation purpose. This navigation way switches functions by only replacing a group of components instead of whole page and therefore has faster response than page-based one. But it doesn't change a browser's URL when each time switching to a different function. However, if you want users can keep track of different functions with bookmark, please refer to [[ZK_Developer's_Reference/UI_Patterns/Browser_History_Management| Browser History Management]].
  
  
We will use the same layout example to demonstrate the AJAX-based navigation.
+
We will use the same layout example to demonstrate AJAX-based navigation.
  
 
Below is the index page, its content is nearly the same as the index page of page based example except it includes a different zul.
 
Below is the index page, its content is nearly the same as the index page of page based example except it includes a different zul.
  
'''chapter7/single/index.zul'''
+
'''chapter7/ajaxbased/index.zul'''
<source lang='xml' high='4'>
+
<source lang='xml' highlight='5,6'>
 
<?link rel="stylesheet" type="text/css" href="/style.css"?>
 
<?link rel="stylesheet" type="text/css" href="/style.css"?>
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/chapter7/single/layout/template.zul"?>
+
<?init class="org.zkoss.zk.ui.util.Composition"  
 +
arg0="/chapter7/ajaxbased/layout/template.zul"?>
 
<zk>
 
<zk>
<include id="mainInclude" self="@define(content)" src="/chapter7/single/home.zul"/>
+
<include id="mainInclude" self="@define(content)"  
 +
src="/chapter7/ajaxbased/home.zul"/>
 
</zk>
 
</zk>
 
</source>
 
</source>
* Line 4: We give the component id for we can find it later with ZK selector.
+
* Line 5,6: We give the component id for we can find it later with ZK selector.
  
This navigation way is mainly implemented by changing <tt>src</tt> of ''Include'' component to switch between different zul pages so that it only reloads included components without affecting other areas . We still need to initialize sidebar configuration:
+
This navigation is mainly implemented by changing the <code>src</code> attribute of the ''Include'' component to switch between different zul pages so that it only reloads included components without affecting other areas . We still need to initialize sidebar configuration:
  
 
'''AJAX-based navigation's sidebar configuration'''
 
'''AJAX-based navigation's sidebar configuration'''
<source lang='java' high='9,10,11,12'>
+
<source lang='java' highlight='9,10,11,12'>
public class SidebarPageConfigSingleDesktopImpl implements SidebarPageConfig{
+
public class SidebarPageConfigAjaxbasedImpl implements SidebarPageConfig{
 
 
 
HashMap<String,SidebarPage> pageMap = new LinkedHashMap<String,SidebarPage>();
 
HashMap<String,SidebarPage> pageMap = new LinkedHashMap<String,SidebarPage>();
public SidebarPageConfigSingleDesktopImpl(){
+
public SidebarPageConfigAjaxbasedImpl(){
 
pageMap.put("zk",new SidebarPage("zk","www.zkoss.org","/imgs/site.png","http://www.zkoss.org/"));
 
pageMap.put("zk",new SidebarPage("zk","www.zkoss.org","/imgs/site.png","http://www.zkoss.org/"));
 
pageMap.put("demo",new SidebarPage("demo","ZK Demo","/imgs/demo.png","http://www.zkoss.org/zkdemo"));
 
pageMap.put("demo",new SidebarPage("demo","ZK Demo","/imgs/demo.png","http://www.zkoss.org/zkdemo"));
Line 243: Line 253:
  
  
In sidebar controller, we get the ''Include'' and change its <tt>src</tt> according to the menu item's URL.
+
In the sidebar controller, we get the ''Include'' and change its <code>src</code> according to the menu item's URL.
  
 
'''Controller for AJAX-based navigation'''
 
'''Controller for AJAX-based navigation'''
<source lang='java' high='46, 47'>
+
<source lang='java' highlight='46, 47,48'>
public class SidebarSingleDesktopController extends SelectorComposer<Component>{
+
public class SidebarAjaxbasedController extends SelectorComposer<Component>{
  
 
@Wire
 
@Wire
Line 253: Line 263:
 
 
 
//wire service
 
//wire service
SidebarPageConfig pageConfig = new SidebarPageConfigSingleDesktopImpl();
+
SidebarPageConfig pageConfig = new SidebarPageConfigAjaxbasedImpl();
 
 
 
@Override
 
@Override
Line 292: Line 302:
 
}else{
 
}else{
 
//use iterable to find the first include only
 
//use iterable to find the first include only
Include include = (Include)Selectors.iterable(fnList.getPage(), "#mainInclude").iterator().next();
+
Include include = (Include)Selectors.iterable(fnList.getPage(), "#mainInclude")
 +
.iterator().next();
 
include.setSrc(locationUri);
 
include.setSrc(locationUri);
 
 
Line 306: Line 317:
 
}
 
}
 
</source>
 
</source>
* Line 46: Since ''Include'' is not a child component of the component that <tt>SidebarSingleDesktopController</tt> applies to, we cannot use <tt>@Wire</tt> to retrieve it. Therefore, we use <tt>Selectors.iterable()</tt> to get components with the id selector from the page. Because ZK combines included components and its parent into one ZK page.
+
* Line 46,47: Since ''Include'' is not a child component of the component that <code>SidebarAjaxbasedController</code> applies to, we cannot use <code>@Wire</code> to retrieve it. Therefore, we use <code>Selectors.iterable()</code> to get components with the id selector from the page. Because ZK combines included components and its parent into one ZK page.
* Line 47: Change the <tt>src</tt> to corresponding URL that belongs to the clicked menu item.
+
* Line 48: Change the <code>src</code> to the corresponding URL that belongs to the clicked menu item.
 +
 
 +
 
 +
Visit the http://localhost:8080/essentials/chapter7/ajaxbased/index.zul to see the result.
  
 +
= Source Code =
  
Visit the http://localhost:8080/tutorial/chapter7/single/index.zul see the result.
+
* [https://github.com/zkoss/zkessentials/tree/master/src/main/webapp/chapter7 ZUL pages]
 +
* [https://github.com/zkoss/zkessentials/tree/master/src/main/java/org/zkoss/essentials/chapter7 Java]
  
  
  
  
{{TutorialPageFooter}}
+
{{ZKEssentialsPageFooter}}

Latest revision as of 10:59, 19 January 2022

Stop.png This article is out of date, please refer to http://books.zkoss.org/zkessentials-book/master/ for more up to date information.



Overview

In traditional navigation, a user usually switches to different functions by visiting different pages of an application, a.k.a page-based navigation. In ZK, we can have another choice to design the navigation in AJAX-based where users don't need to visit different pages. In page-based navigation, users need to switch pages frequently, we should maintain a consistent page design throughout whole application to help users keep track of where they are. Luckily ZK provides Templating to keep multiple pages in the same style easily.

In this chapter, the example application we are going to build looks as follows:

Tutorial-ch7-ajax-based.png

The sidebar is used for navigation control. The lower 4 menu items lead you to different functions and they only change the central area's content. All other areas are unchanged to maintain a consistent layout style among functions.

Templating

In our example application, we want to keep the header, the sidebar, and the footer unchanged regardless of which function a user chooses. Only the central area changes its content according to the function chosen by users.

In page-based navigation, each function is put into a separated page and we need to have a consistent style. One way is to copy the duplicated part from one zul to another, but it is hard to maintain. Fortunately, ZK provides a Templating technique that lets you define a template zul and apply it to multiple zul pages afterwards. All zul pages that apply the same template zul have the same layout, so changing the template zul can change the layout of all pages once.

The steps to use templating are:

  1. Create a template zul and define anchors
  2. Apply the template in the target zul and define zul fragments for anchors

Then when you visit the target zul page, ZK will insert those fragments to corresponding anchors upon anchor names and combine them as one page.

Create a Template ZUL File

Creating a template is nothing different from creating a normal zul, but you should define one or more anchors by specifying annotation @insert() at self. You can give any name to identify an anchor that will be used to insert a zul fragment with the same anchor name later.

chapter7/pagebased/layout/template.zul

<zk>
	<borderlayout hflex="1" vflex="1">
		<north height="100px" border="none" >
			<include src="/chapter3/banner.zul"/>
		</north>
		<west width="260px" border="none" collapsible="true" 
				splittable="true" minsize="300">
			<include src="/chapter7/pagebased/layout/sidebar.zul"/>
		</west>
		<center id="mainContent" autoscroll="true" border="none" 
				self="@insert(content)">
		</center>
		<south height="50px" border="none">
			<include src="/chapter3/footer.zul"/>
		</south>
	</borderlayout>
</zk>
  • Line 10,11: Define an anchor with name content

Apply the Template

After creating a template zul, we can apply it in another zul with directive

<?init class="org.zkoss.zk.ui.util.Composition" arg0="template_path"?>

This directive tells ZK that this page uses the specified template. Then we also have to define zul fragments that are inserted to the anchor in the template zul with annotation @define(anchorName) at self attribute. The anchorName you specify should correspond to one of anchors defined in the template zul.

chapter7/pagebased/index.zul

<?link rel="stylesheet" type="text/css" href="/style.css"?>
<?init class="org.zkoss.zk.ui.util.Composition" 
		arg0="/chapter7/pagebased/layout/template.zul"?>
<zk>
	<include self="@define(content)" src="/chapter7/pagebased/home.zul"/>
</zk>
  • Line 2,3: Tell ZK that we want to use a template zul for current page and give the path of template zul.
  • Line 5: The anchor name content correspond to the anchor name defined in the template zul in previous section.


After above steps, when you visit http://localhost:8080/essentials/chapter7/pagebased/index.zul, ZK will render the page based on template.zul and attach the Include component to <center>.

Page-based Navigation

Traditional web applications are usually designed with page-based navigation. Each function corresponds to an independent page with independent URL. The navigation is very clear for users as they know where they are from the URL and they can press "go back" button on their browser to go back to previous pages in history. But the drawback is users have to wait whole page reloading every time they switch to a function. Additionally, developers also have to maintain multiple pages that have similar contents but applying a template zul can reduce this problem.

Tutorial-ch7-page-based-navigation.png
Page-based Navigation


To build a page-based navigation, first you should prepare pages for those items in the sidebar.

Tutorial-ch7-pagebased.png

From above image, you can see there are four zul pages which correspond to items in the sidebar under "chpater7\pagebased" (index-profile-mvc.zul, index-profile-mvvm.zul, index-todolist-mvc.zul, index-todolist-mvvm.zul). Then we can link four items of the sidebar to these zul pages by redirecting a browser.

Next, we apply the template zul created before on those 4 pages.

/chapter7/pagebased/index-profile-mvc.zul

<?link rel="stylesheet" type="text/css" href="/style.css"?>
<?init class="org.zkoss.zk.ui.util.Composition" 
		arg0="/chapter7/pagebased/layout/template.zul"?>
<zk>
	<include self="@define(content)" src="/chapter5/profile-mvc.zul"/>
</zk>
  • Line 2,3: Apply the template in /chapter7/pagebased/layout/template.zul.
  • Line 5: Define a zul fragment for the anchor content.


Our example application creates those menu items dynamically upon a configuration, so we should initialize configuration.

Page-based navigation's sidebar configuration

public class SidebarPageConfigPagebasedImpl implements SidebarPageConfig{
	
	HashMap<String,SidebarPage> pageMap = new LinkedHashMap<String,SidebarPage>();
	public SidebarPageConfigPagebasedImpl(){		
		pageMap.put("zk",new SidebarPage("zk","www.zkoss.org","/imgs/site.png","http://www.zkoss.org/"));
		pageMap.put("demo",new SidebarPage("demo","ZK Demo","/imgs/demo.png","http://www.zkoss.org/zkdemo"));
		pageMap.put("devref",new SidebarPage("devref","ZK Developer Reference","/imgs/doc.png"
				,"http://books.zkoss.org/wiki/ZK_Developer's_Reference"));
		
		pageMap.put("fn1",new SidebarPage("fn1","Profile (MVC)","/imgs/fn.png"
				,"/chapter7/pagebased/index-profile-mvc.zul"));
		pageMap.put("fn2",new SidebarPage("fn2","Profile (MVVM)","/imgs/fn.png"
				,"/chapter7/pagebased/index-profile-mvvm.zul"));
		pageMap.put("fn3",new SidebarPage("fn3","Todo List (MVC)","/imgs/fn.png"
				,"/chapter7/pagebased/index-todolist-mvc.zul"));
		pageMap.put("fn4",new SidebarPage("fn4","Todo List (MVVM)","/imgs/fn.png"
				,"/chapter7/pagebased/index-todolist-mvvm.zul"));
	}
	
	...
	
}
  • Line 10~17: Specify URL and related data for each menu item's configuration.

The following codes show how to redirect a user to an independent page when they click a menu item in the sidebar.

Controller for page-based navigation

public class SidebarPagebasedController extends SelectorComposer<Component>{

	...

	//wire service
	SidebarPageConfig pageConfig = new SidebarPageConfigPagebasedImpl();
	
	@Override
	public void doAfterCompose(Component comp) throws Exception{
		super.doAfterCompose(comp);
		
		//to initial view after view constructed.
		Rows rows = fnList.getRows();
		
		for(SidebarPage page:pageConfig.getPages()){
			Row row = constructSidebarRow(page.getName(),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");
			
		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 15: Create menu items in the sidebar upon configurations with Rows.
  • Line 39: Add a event listener to redirect a browser to the URL specified in the menu item a user clicks.


Visit http://localhost:8080/essentials/chapter7/pagebased/index.zul. You will see the URL changes and whole page reloads each time you click a different menu item.

AJAX-based Navigation

When switching between different functions in paged-based navigation, you find that only central area's content is different among those pages and other three areas (header, sidebar, and footer) contain identical content. But in page-based navigation, a browser has to reload all contents no matter they are identical to previous page when switching to another function. With AJAX's help, ZK allows you to implement another navigation way that only updates necessary part of a page instead of reloading the whole page.


Tutorial-ch7-ajax-based-navigation.png
AJAX-based Navigation


The easiest way to implement AJAX-based navigation is changing src attribute of Include component. It can change only partial content of an page instead of redirecting to another page to achieve the navigation purpose. This navigation way switches functions by only replacing a group of components instead of whole page and therefore has faster response than page-based one. But it doesn't change a browser's URL when each time switching to a different function. However, if you want users can keep track of different functions with bookmark, please refer to Browser History Management.


We will use the same layout example to demonstrate AJAX-based navigation.

Below is the index page, its content is nearly the same as the index page of page based example except it includes a different zul.

chapter7/ajaxbased/index.zul

<?link rel="stylesheet" type="text/css" href="/style.css"?>
<?init class="org.zkoss.zk.ui.util.Composition" 
		arg0="/chapter7/ajaxbased/layout/template.zul"?>
<zk>
	<include id="mainInclude" self="@define(content)" 
		src="/chapter7/ajaxbased/home.zul"/>
</zk>
  • Line 5,6: We give the component id for we can find it later with ZK selector.

This navigation is mainly implemented by changing the src attribute of the Include component to switch between different zul pages so that it only reloads included components without affecting other areas . We still need to initialize sidebar configuration:

AJAX-based navigation's sidebar configuration

public class SidebarPageConfigAjaxbasedImpl implements SidebarPageConfig{
	
	HashMap<String,SidebarPage> pageMap = new LinkedHashMap<String,SidebarPage>();
	public SidebarPageConfigAjaxbasedImpl(){		
		pageMap.put("zk",new SidebarPage("zk","www.zkoss.org","/imgs/site.png","http://www.zkoss.org/"));
		pageMap.put("demo",new SidebarPage("demo","ZK Demo","/imgs/demo.png","http://www.zkoss.org/zkdemo"));
		pageMap.put("devref",new SidebarPage("devref","ZK Developer Reference","/imgs/doc.png","http://books.zkoss.org/wiki/ZK_Developer's_Reference"));
		
		pageMap.put("fn1",new SidebarPage("fn1","Profile (MVC)","/imgs/fn.png","/chapter5/profile-mvc.zul"));
		pageMap.put("fn2",new SidebarPage("fn2","Profile (MVVM)","/imgs/fn.png","/chapter5/profile-mvvm.zul"));
		pageMap.put("fn3",new SidebarPage("fn3","Todo List (MVC)","/imgs/fn.png","/chapter6/todolist-mvc.zul"));
		pageMap.put("fn4",new SidebarPage("fn4","Todo List (MVVM)","/imgs/fn.png","/chapter6/todolist-mvvm.zul"));
	}
	...
}
  • Line 9 ~ 12: Because we only need those pages that doesn't have header, sidebar, and footer, we can re-use those pages written in previous chapters.


In the sidebar controller, we get the Include and change its src according to the menu item's URL.

Controller for AJAX-based navigation

public class SidebarAjaxbasedController extends SelectorComposer<Component>{

	@Wire
	Grid fnList;
	
	//wire service
	SidebarPageConfig pageConfig = new SidebarPageConfigAjaxbasedImpl();
	
	@Override
	public void doAfterCompose(Component comp) throws Exception{
		super.doAfterCompose(comp);
		
		//to initial view after view constructed.
		Rows rows = fnList.getRows();
		
		for(SidebarPage page:pageConfig.getPages()){
			Row row = constructSidebarRow(page.getName(),page.getLabel(),page.getIconUri(),page.getUri());
			rows.appendChild(row);
		}		
	}

	private Row constructSidebarRow(final 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");
		
		//new and register listener for events
		EventListener<Event> onActionListener = new SerializableEventListener<Event>(){
			private static final long serialVersionUID = 1L;

			public void onEvent(Event event) throws Exception {
				//redirect current url to new location
				if(locationUri.startsWith("http")){
					//open a new browser tab
					Executions.getCurrent().sendRedirect(locationUri);
				}else{
					//use iterable to find the first include only
					Include include = (Include)Selectors.iterable(fnList.getPage(), "#mainInclude")
							.iterator().next();
					include.setSrc(locationUri);
					
					...
				}
			}
		};		
		row.addEventListener(Events.ON_CLICK, onActionListener);

		return row;
	}
	
}
  • Line 46,47: Since Include is not a child component of the component that SidebarAjaxbasedController applies to, we cannot use @Wire to retrieve it. Therefore, we use Selectors.iterable() to get components with the id selector from the page. Because ZK combines included components and its parent into one ZK page.
  • Line 48: Change the src to the corresponding URL that belongs to the clicked menu item.


Visit the http://localhost:8080/essentials/chapter7/ajaxbased/index.zul to see the result.

Source Code




Last Update : 2022/01/19

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