Listbox Template"

From Documentation
m (replace tt with code (via JWB))
(16 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
{{ZKDevelopersReferencePageHeader}}
 
{{ZKDevelopersReferencePageHeader}}
 +
 +
__TOC__
  
 
The template used to control the rendering of each item must be named <code>model</code> and declared right inside the <code>listbox</code> element. For example,
 
The template used to control the rendering of each item must be named <code>model</code> and declared right inside the <code>listbox</code> element. For example,
  
<source lang="xml" high="6,7,8,9,10,11">
+
<source lang="xml" highlight="7,8,9,10,11,12">
<listbox model="${$composer.fruits}" apply="foo.FruitProvider">
+
<div apply="foo.FruitProvider">
 +
    <listbox model="${$composer.fruits}">
 
<listhead>
 
<listhead>
 
<listheader label="Name" sort="auto"/>
 
<listheader label="Name" sort="auto"/>
Line 15: Line 18:
 
</listitem>
 
</listitem>
 
</template>
 
</template>
</listbox>
+
    </listbox>
 +
</div>
 
</source>
 
</source>
  
As illustrated above, custom rendering is defined by a template called <code>model</code>. The template's name is important because users are allowed to associate multiple templates to one component, and <code>listbox</code>'s default renderer looks only for the template called <code>model</code>.
+
The template's name is important because users are allowed to associate multiple templates to one component, and <code>listbox</code>'s default renderer looks only for the template called <code>model</code>.
  
When the template is rendered, a variable called <code>each</code> is assigned with the data being rendered. Thus, you could retrieve the information to render with EL expressions, such as <code>${each[0]}</code> or <code>${each.name}</code>, if it is a bean with a setter called <code>name</code>.
+
When the template is rendered, a variable called [[ZUML Reference/EL Expressions/Implicit Objects/each|each]] is assigned with the data being rendered. Thus, you could retrieve the information to render with EL expressions, such as <code>${each[0]}</code>, if it is an array, or <code>${each.name}</code>, if it is a bean with a getter called <code>name</code>.
  
Notice that, if <code>value="${each}"</code> is not specified to the <code>listitem</code> element (<javadoc method="setValue(java.lang.Object)">org.zkoss.zul.Listitem</javadoc>) in the template, <code>listbox</code> will automatically assign the data being rendered to the <code>listitem</code>'s value, such that you could conveniently retrieve back the data when necessary.
+
In this example, we assume the <code>$composer.fruits</code> expression returns a two-dimensional array<ref>Of course, it can be anything you like. Just make sure it matches the EL expressions specified in the template.</ref>, and is provided by the <code>foo.FruitProvider</code> composer such as follows<ref>Here we use <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc> for simplicity. There are several ways to implement a composer, such as wiring a Spring-managed bean. For more information, please refer to [[ZK Developer's Reference/MVC/Controller/Composer|the Composer section]]</ref>.
 
 
The <code>$composer.fruits</code> expression, in this example, is expected to return a two-dimensional array<ref>Of course, it can be anything you like. Just make sure it matches the EL expressions specified in the template.</ref>. It is provided by the <code>foo.FruitProvider</code> composer such as follows<ref>Here we use <javadoc>org.zkoss.zk.ui.select.SelectorComposer</javadoc> for simplicity. There are several ways to implement a composer, such as wiring a Spring-managed bean. For more information, please refer to [[ZK Developer's Reference/MVC/Controller/Composer|the Composer section]]</ref>.
 
  
 
<source lang="java">
 
<source lang="java">
 
public class FruitProvider extends org.zkoss.zk.ui.select.SelectorComposer {
 
public class FruitProvider extends org.zkoss.zk.ui.select.SelectorComposer {
     public String[][] fruits = new ListModelArray(
+
     public ListModelArray fruits = new ListModelArray(
 
             new String[][] {
 
             new String[][] {
 
                 {"Apple", "10kg"},
 
                 {"Apple", "10kg"},
Line 35: Line 37:
 
             });
 
             });
  
     public String[][] getFruits() {
+
     public ListModelArray getFruits() {
 
         return fruits;
 
         return fruits;
 
     }
 
     }
Line 41: Line 43:
 
</source>
 
</source>
 
[[File:St201107-listbox.png‎]]
 
[[File:St201107-listbox.png‎]]
 
  
 
<blockquote>
 
<blockquote>
Line 47: Line 48:
 
<references/>
 
<references/>
 
</blockquote>
 
</blockquote>
 +
 +
==Component's Value==
 +
By default, the data used to render a component will be stored to the component's value property automatically. For listitem, it is <javadoc method="setValue(T)">org.zkoss.zul.Listitem</javadoc>. Thus, you retrieve it back easily by invoking <javadoc method="getValue()">org.zkoss.zul.Listitem</javadoc>.
 +
 +
Of course, if you prefer to store other values, you can simply specify <code>value="${whatever}"</code> to the <code>listitem</code> element in the template.
 +
 +
==The forEachStatus Variable==
 +
 +
There is a variable called [[ZUML Reference/EL Expressions/Implicit Objects/forEachStatus|forEachStatus]] providing the information of the iteration. It is an instance of <javadoc type="interface">org.zkoss.zk.ui.util.ForEachStatus</javadoc>. For example, you could retrieve the iteration's index by use of <code>${forEachStatus.index}</code>.
 +
 +
==Lifecycle and the arg Variable==
 +
 +
When using the template, it is important to remember that the template is rendered on demand. It means the template can be rendered very late, after the page is rendered, after the user scrolls down to make an item visible, and so on. Thus, in the template, you ''cannot'' reference anything that is available only in the page rendering phase. For example, you can't reference [[ZUML Reference/EL Expressions/Implicit Objects/arg|the arg variable]] in a template:
 +
 +
<source lang="xml" highlight="4">
 +
<listbox model="${$composer.fruits}" apply="foo.FruitProvider">
 +
    <template name="model">
 +
        <listitem>
 +
            <listcell label="${arg.foo}"/> <!-- Wrong! it is always empty -->
 +
            <listcell label="${each}"/>
 +
        </listitem>
 +
    </template>
 +
</listbox>
 +
</source>
 +
 +
To work around, you have to store the value in, say, component's custom attributes (<javadoc method="getAttributes()" type="interface">org.zkoss.zul.Component</javadoc>. For example,
 +
 +
<source lang="xml" highlight="2,5">
 +
<listbox model="${$composer.fruits}" apply="foo.FruitProvider">
 +
    <custom-attributes foo="${arg.foo}"/><!-- store it for later use -->
 +
    <template name="model">
 +
        <listitem>
 +
            <listcell label="${foo}"/> <!-- Correct! Use the stored copy. -->
 +
            <listcell label="${each}"/>
 +
        </listitem>
 +
    </template>
 +
</listbox>
 +
</source>
  
 
==Nested Listboxes==
 
==Nested Listboxes==
 
The template can be applied recursively. Here is an example of a listbox-in-listbox:
 
The template can be applied recursively. Here is an example of a listbox-in-listbox:
  
<source lang="xml" high="12, 17">
+
<source lang="xml" highlight="12, 17">
 
<zk>
 
<zk>
 
<zscript><![CDATA[
 
<zscript><![CDATA[
Line 81: Line 120:
 
[[File:St201107-listbox-in-listbox.png‎]]
 
[[File:St201107-listbox-in-listbox.png‎]]
  
===Data in the outer template variable===
+
===How to retrieve the outer template's data in the inner template===
To access the data of the outer template from the inner template, you could use the parent listitem's <javadoc method="getValue()">org.zkoss.zul.Listitem</javadoc> where the data is stored by default. Please look at line 9 of the following example, data can be retrieved from the outer model by travellng the component tree
+
 
 +
Although [[ZUML Reference/EL Expressions/Implicit Objects/forEachStatus|forEachStatus]] has an API called <javadoc method="getPrevious()" type="interface">org.zkoss.zk.ui.util.ForEachStatus</javadoc>, it always returns null<ref>On the other hand, it returns the previous iteration information when using with [[ZUML Reference/ZUML/Attributes/forEach|the forEach attribute]]</ref>. It is because the template is rendered on demand. When ZK is rendering the inner template, the previous iteration has already gone. There is no way to retrieve the iteration information of the outer template.
  
<source lang="xml" high="9">
+
Rather, you have to traverse the component tree or use [[ZUML Reference/ZUML/Elements/custom-attributes|the custom-attributes element]].
 +
 
 +
Here is an example of traversing the component tree to retrieve the data in the outer template, as shown at line 9 below. Notice that, each data is, as described before, stored in the component's value property.
 +
 
 +
<source lang="xml" highlight="9">
 
<listbox model="${quarters}">
 
<listbox model="${quarters}">
 
<template name="model">
 
<template name="model">
Line 104: Line 148:
 
</source>
 
</source>
  
Notice that [[ZUML Reference/EL Expressions/Implicit Objects/forEachStatus|forEachStatus]] (line 8) doesn't have any information of the outer template<ref>It is because the inner template is ''not'' rendered at the same time as the outer's.</ref>. In other words, <code>${forEachStatus.previous}</code> always returns null.
+
If the component tree is deep, It is tedious and somehow error prone. Alternatively, you can store the information into [[ZUML Reference/ZUML/Elements/custom-attributes|a custom attribute]] and then retrieve it later, as shown at line 4 and 10 below.
  
 +
<source lang="xml" highlight="4,10">
 +
<listbox model="${quarters}">
 +
<template name="model">
 +
<listitem>
 +
<custom-attributes master="${each}"/>
 +
<listcell>
 +
<listbox model="${months[each]}">
 +
<template name="model">
 +
<listitem>
 +
<listcell label="${forEachStatus.index}" />
 +
<listcell>${master}</listcell>
 +
<listcell>${each}</listcell>
 +
</listitem>
 +
</template>
 +
</listbox>
 +
</listcell>
 +
</listitem>
 +
</template>
 +
</listbox>
 +
</source>
 +
 
<blockquote>
 
<blockquote>
 
----
 
----
Line 113: Line 178:
 
=Template for GroupsModel=
 
=Template for GroupsModel=
  
When used with <javadoc type="interface">org.zkoss.zul.GroupsModel</javadoc>, listboxes will use the template called <tt>model:grouping</tt> for rendering the grouping object. If it is not defined, it will look for the template called <tt>model</tt> instead (i.e., the same template is used for rendering the grouping and non-grouping objects).
+
When used with <javadoc type="interface">org.zkoss.zul.GroupsModel</javadoc>, listboxes will use the template called <code>model:group</code> for rendering the grouping object. If it is not defined, it will look for the template called <code>model</code> instead (i.e., the same template is used for rendering the grouping and non-grouping objects).
  
 
<source lang="xml">
 
<source lang="xml">
<listbox mode="${fooGroupsModel}">
+
<listbox model="${fooGroupsModel}">
 
   <template name="model:group">
 
   <template name="model:group">
 
       <listgroup open="${groupingInfo.open}" label="${each}"/>
 
       <listgroup open="${groupingInfo.open}" label="${each}"/>
Line 133: Line 198:
 
=Version History=
 
=Version History=
 
{{LastUpdated}}
 
{{LastUpdated}}
{| border='1px' | width="100%"
+
{| class='wikitable' | width="100%"
 
! Version !! Date !! Content
 
! Version !! Date !! Content
 
|-
 
|-

Revision as of 14:13, 12 January 2022

The template used to control the rendering of each item must be named model and declared right inside the listbox element. For example,

<div apply="foo.FruitProvider">
    <listbox model="${$composer.fruits}">
	<listhead>
		<listheader label="Name" sort="auto"/>
		<listheader label="Weight" sort="auto"/>
	</listhead>
	<template name="model">
		<listitem>
			<listcell label="${each[0]}"/>
			<listcell label="${each[1]}"/>
		</listitem>
	</template>
    </listbox>
</div>

The template's name is important because users are allowed to associate multiple templates to one component, and listbox's default renderer looks only for the template called model.

When the template is rendered, a variable called each is assigned with the data being rendered. Thus, you could retrieve the information to render with EL expressions, such as ${each[0]}, if it is an array, or ${each.name}, if it is a bean with a getter called name.

In this example, we assume the $composer.fruits expression returns a two-dimensional array[1], and is provided by the foo.FruitProvider composer such as follows[2].

public class FruitProvider extends org.zkoss.zk.ui.select.SelectorComposer {
    public ListModelArray fruits = new ListModelArray(
            new String[][] {
                {"Apple", "10kg"},
                {"Orange", "20kg"},
                {"Mango", "12kg"}
            });

    public ListModelArray getFruits() {
         return fruits;
    }
}

St201107-listbox.png


  1. Of course, it can be anything you like. Just make sure it matches the EL expressions specified in the template.
  2. Here we use SelectorComposer for simplicity. There are several ways to implement a composer, such as wiring a Spring-managed bean. For more information, please refer to the Composer section

Component's Value

By default, the data used to render a component will be stored to the component's value property automatically. For listitem, it is Listitem.setValue(T). Thus, you retrieve it back easily by invoking Listitem.getValue().

Of course, if you prefer to store other values, you can simply specify value="${whatever}" to the listitem element in the template.

The forEachStatus Variable

There is a variable called forEachStatus providing the information of the iteration. It is an instance of ForEachStatus. For example, you could retrieve the iteration's index by use of ${forEachStatus.index}.

Lifecycle and the arg Variable

When using the template, it is important to remember that the template is rendered on demand. It means the template can be rendered very late, after the page is rendered, after the user scrolls down to make an item visible, and so on. Thus, in the template, you cannot reference anything that is available only in the page rendering phase. For example, you can't reference the arg variable in a template:

<listbox model="${$composer.fruits}" apply="foo.FruitProvider">
    <template name="model">
        <listitem>
            <listcell label="${arg.foo}"/> <!-- Wrong! it is always empty -->
            <listcell label="${each}"/>
        </listitem>
    </template>
</listbox>

To work around, you have to store the value in, say, component's custom attributes (Component.getAttributes(). For example,

<listbox model="${$composer.fruits}" apply="foo.FruitProvider">
    <custom-attributes foo="${arg.foo}"/><!-- store it for later use -->
    <template name="model">
        <listitem>
            <listcell label="${foo}"/> <!-- Correct! Use the stored copy. -->
            <listcell label="${each}"/>
        </listitem>
    </template>
</listbox>

Nested Listboxes

The template can be applied recursively. Here is an example of a listbox-in-listbox:

<zk>
	<zscript><![CDATA[
	ListModel quarters = new ListModelArray(new String[] {"Q1", "Q2", "Q3", "Q4"});
	Map months = new HashMap();
	months.put("Q1", new ListModelArray(new String[] {"Jan", "Feb", "Mar"}));
	months.put("Q2", new ListModelArray(new String[] {"Apr", "May", "Jun"}));
	months.put("Q3", new ListModelArray(new String[] {"Jul", "Aug", "Sep"})); 
	months.put("Q4", new ListModelArray(new String[] {"Oct", "Nov", "Dec"}));
	ListModel qs = (quarters);
	]]></zscript>
	<listbox model="${quarters}">
		<template name="model">
			<listitem>
				<listcell>${each}</listcell>
				<listcell>
					<listbox model="${months[each]}">
						<template name="model">
							<listitem label="${each}"/>
						</template>
					</listbox>
				</listcell>
			</listitem>
		</template>
	</listbox>
</zk>

St201107-listbox-in-listbox.png

How to retrieve the outer template's data in the inner template

Although forEachStatus has an API called ForEachStatus.getPrevious(), it always returns null[1]. It is because the template is rendered on demand. When ZK is rendering the inner template, the previous iteration has already gone. There is no way to retrieve the iteration information of the outer template.

Rather, you have to traverse the component tree or use the custom-attributes element.

Here is an example of traversing the component tree to retrieve the data in the outer template, as shown at line 9 below. Notice that, each data is, as described before, stored in the component's value property.

<listbox model="${quarters}">
	<template name="model">
		<listitem>
			<listcell>
				<listbox model="${months[each]}">
					<template name="model">
						<listitem>
							<listcell label="${forEachStatus.index}" />
							<listcell>${self.parent.parent.parent.parent.parent.value}</listcell>
							<listcell>${each}</listcell>
						</listitem>
					</template>
				</listbox>
			</listcell>
		</listitem>
	</template>
</listbox>

If the component tree is deep, It is tedious and somehow error prone. Alternatively, you can store the information into a custom attribute and then retrieve it later, as shown at line 4 and 10 below.

<listbox model="${quarters}">
	<template name="model">
		<listitem>
			<custom-attributes master="${each}"/>
			<listcell>
				<listbox model="${months[each]}">
					<template name="model">
						<listitem>
							<listcell label="${forEachStatus.index}" />
							<listcell>${master}</listcell>
							<listcell>${each}</listcell>
						</listitem>
					</template>
				</listbox>
			</listcell>
		</listitem>
	</template>
</listbox>

  1. On the other hand, it returns the previous iteration information when using with the forEach attribute

Template for GroupsModel

When used with GroupsModel, listboxes will use the template called model:group for rendering the grouping object. If it is not defined, it will look for the template called model instead (i.e., the same template is used for rendering the grouping and non-grouping objects).

<listbox model="${fooGroupsModel}">
   <template name="model:group">
      <listgroup open="${groupingInfo.open}" label="${each}"/>
   </template>
   <template name="model">
      <listitem>....</listitem>
   </template>
   <template name="model:groupfoot">
      <listgroupfoot>....</listgroupfoot>
   </template>
<listbox>
  • Note the groupingInfo is used to get the information of the grouping data. Please refer to GroupingInfo

Version History

Last Update : 2022/01/12


Version Date Content
6.0.0 July 2011 The template feature was introduced.
6.0.0 January 2012 The GroupingInfo statement was introduced.



Last Update : 2022/01/12

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