Composite Components in the MVC
This article is out of date, please refer to http://books.zkoss.org/zkessentials-book/master/ for more up to date information.
First the syntax of the template will be dealt with. It looks incredibly similar to what one observed in the databinding chapter except using a $ instead of an @ and a slightly different syntax. This is because the markup below makes use of EL. EL expressions differ from databinding in the fact that they are only interpreted once, whereas databidning will be re-interpreted whenever it is notified a value has changed. Seeing as the product list will remain relatively constant we use EL.
For more information on EL please visit link needed here
<template name="model">
<row value="${each}">
<image height="70px" width="70px" src="${each.imgPath}" />
<label value="${each.name}" />
<label value="${each.price}" />
<label value="${each.quantity}" />
<label value="${each.createDate}" />
<productOrder maximumQuantity="${each.quantity}" product="${each}" />
</row>
</template>
The above snipped makes use of template (as spoken about briefly in the previous section) as well as EL
The template
A template is a ZUML fragment that defines how to create components and can contain any ZUML element you would like including other templates. In this case the template defines the layout of each row. The row value is assigned as "each" where each refers to a bean provided by the model.
A template is generally used within a Listbox, Grid, Tree and Combobox to describe how repeating items are displayed. For example take the application's case the template is contained within a Grid:
<grid id="prodGrid">
<columns sizable="true">
...
</columns>
<template name="model">
<row value="${each}">
<image height="70px" width="70px"
src="${each.imgPath}" />
<label value="${each.name}" />
<label value="${each.price}" />
<label value="${each.quantity}" />
<label value="${each.createDate}" />
<productOrder maximumQuantity="${each.quantity}" product="${each}" />
</row>
</template>
</grid>
Thus the template is outputted for every bean contained in the grid's model. In this case the output is just relatively simple, however, one interesting item in the template is the productOrder control. This is in fact a composite control which is created by developers.
Composite component
A composite component is an advanced template that affords developers the ability to extend from any component. A composite component consists of a Java class which extends from a component, in this case the ProductOrder component extends from Cell as it will be placed into a grid row. The Cell will therefore contain multiple components.
The product order control extends form Cell and implements IdSpace and AfterCompose. Implementing these two interfaces enables the component to take on certain behaviours, by implementing the AfterCompose when the composite component is created it enbables developers to perform other actions. In the case of the ProductOrder the snippet below shows the afterCompose method:
public class ProductOrder extends Cell implements IdSpace, AfterCompose {
...
public void afterCompose() {
// 1. Render the ZUML fragment
Executions.createComponents("/WEB-INF/composite/productorder.zul",
this, new HashMap<String, Object>() {
private static final long serialVersionUID = 7141348964577773718L;
{
put("maximumQuantity", getMaximumQuantity());
}
});
// 2. Wire variables, components and event listeners (optional)
Selectors.wireVariables(this, this, null);
Selectors.wireComponents(this, this, false);
Selectors.wireEventListeners(this, this);
}
...
}
The snippet above shows two things happening, firstly the composite component renders a ZUML fragment which will be contained within itself. Taking a look at the Executions.createComponents function call on line 7 the first parameter is the location of the ZUML fragment, the second parameter is the component which will become the root, in this case itself. The third component is a HashMap containing the parameters to send to the fragment, in this case the maximumQuantity, which is just a simple bean.
The fragment is shown below:
<zk>
<spinner id="spnQuantity" constraint="min 1 max ${arg.maximumQuantity}" value="1"/>
<button id="btnAdd" label="add" image="/image/ShoppingCart-16x16.png" />
<label id="lblError" />
</zk>
In the above fragment one can see how the maximum quantity, which was passed to the fragment on creation, is accessed and used using EL expressions.
Finally, in the composite component needs to be wired, this includes the variables, components and event listeners. This is easy to do using the following method calls in the afterCompose function.
Selectors.wireVariables(this, this, null);
Selectors.wireComponents(this, this, false);
Selectors.wireEventListeners(this, this);
<source>
Having built the component it now needs to be registered with ZK, this is easily done by putting a directive at the top of the ZUL file you want to use it in. In the case of this application it is present in index.zul.
<source lang="xml">
<?component name="productOrder" class="demo.web.ui.ctrl.ProductOrder" ?>
The directive specifies a name for the component which can be referenced in the ZUL file and the appropriate class which drives the functionality. The component can now be used as follows:
<grid id="prodGrid">
<columns sizable="true">
...
</columns>
<template name="model">
<row value="${each}">
<image height="70px" width="70px"
src="${each.imgPath}" />
<label value="${each.name}" />
<label value="${each.price}" />
<label value="${each.quantity}" />
<label value="${each.createDate}" />
<productOrder maximumQuantity="${each.quantity}" product="${each}" />
</row>
</template>
</grid>
For more information on the composite component please consult the Developer's reference
This concludes this section of the document, the data is displayed correctly. The adding of products is discussed in the last chapter as it deals with communication between MVC and MVVM thus is a topic best introduced after MVVM. If you are interested you can take a look now.