ZK8 Features for MVC - Shadow Elements - Part 1
Robert Wenzel, Engineer, Potix Corporation
August 2016
ZK 8.0
Introduction
ZK 8 added several new Features mainly improving Component control when using the MVVM design pattern. The question I sometimes heard since was: (How) Can we benefit from these features in our MVC application ?
To answer the question I'll start with the most prominent features which are:
- shadow elements
- form proxies
Let's have a look where they can be used in the MVC world.
If you are missing a feature let us know in the comments section below.
Shadow Elements in MVC
Some of the Shadow Elements LINK ME are more/less beneficial in MVC than others here a short summary
- <if>
- most useful when used with dynamic @load expressions in MVVM
- in MVC you can attach/detach components directly (so this is nothing new for MVC)
- used statically you can render parts of your UI based on a conditional EL expression
<if test="${some.condition}">...</if>
- <choose>, <when>, <otherwise>
- similar to <if> no real benefit for MVC
- <forEach>
- very useful this will be the topic in next smalltalk
- <apply>
- I'll show a few usage examples today ... just read on.
<apply> for static layout composition
Why a new shadow element? Let's compare what's there previously.
For static UI composition you'd classically use an <include> component such as this:
<include src="customerDetails.zul" customerId="123"/>
Or a macro component:
<?component name="my-macro" macroURI="customerDetails.zul"?>
<my-macro customerId="123"/>
These 2 examples roughly will give the same output.
You might ask: "What's the difference?" "Which is better?" - The answer is often not that trivial and depends on what you need - and sometimes neither option fulfills all requirements.
Comparing the generated DOM we'll see that the <include> creates a
...
while the Macro generates a
...
So far so good.
If you don't want the additional
(e.g. since it breaks your layout, or you are inside a component hierarchy that only allows certain child components) the case is clear, you need to use an inline macro LINK ME. Inline Macros in return have their own technical limitations. Main impact is the root component of the macro is detached from your component tree with all its properties/custom-attributes lost, which you might want to use inside the included zul template.
If you need a different DOM element other than you can use the enclosingTag property LINK ME of the macro component or implement a custom mold for <include/> LINK ME.
Another common requirement is often to use a dynamic zul URI to include, which works better with <include/>, but include has no inline option.
Sometimes even more surprising are the side effects when using an <include> component with a url containing a query string ... while Macros don't support query string (or do they? I'd need to try or look into the source).
<include src="customerDetails.zul?customerId=123"/>
Suddenly the <include> component changes to "defer" mode LINK ME resulting in a different component creation life cycle.
I assume you see where I am getting ... these grown components/features diverged over time, causing quite some confusion and controversies about which to use in which scenario, and how to work around the limitations in either case.
Apply to the rescue:
... can be replaced by an <apply> element:
<apply templateURI="customerDetails.zul" customerId="123"/>
The difference is that an <apply> does less things "under the hood" compared to <include>:
- it does not create an additional
in the resulting DOM
- -> in fact it does not exist at the client side at all
- it does not create a new namespace (you have to take care of that yourself if you need)
- it does not have this automatic behavior of switching between "instant" and "defer" mode like an include
- it does not create a nested Page object
- it only supports zul/zhtml templates
- parameters in the URI (e.g. some.zul?id=abc) are accessible via ${arg.id} variable instead of ${param.id}
Simpy spoken behaves like a call to Executions.createComponents(...).
To save memory the <apply> shadow element is even removed completely from the server side component (shadow) tree when used purely statically.
The shadow elements are not removed when used with dynamic parameters. E.g.:
- in MVVM using the ZKBIND anotations (@load/@init/@ref)
- in MVC by adding the special attribute dynamicValue="true"
This is required if you want to wire the shadow element into your composer, e.g. to set the templateURI or parameters dynamically. LINK EXAMPLE BELOW
<div>
<apply id="panelContent"/>
</div>
- Line 2: assign an id in the zul file
@Wire("::shadow#panelContent")
private Apply panelContent;
- Line 1: wire by id using a shadow selector
Still in pure java you could just call Executions.createComponents(...) LINK ME
helps to compose the UI from ZUL fragments (more flexible than <include> or Macro components)
<apply> with Templates
On top of the above <apply> can be used to insert templates at any point of your page. This can be useful when refactoring larger zul files in order to remove duplicate layout structures.
Here a repeated layout only differing by customer object and a label.
<zk>
<div>
<div sclass="customerBox">
<div>Current Customer id: ${sessionScope.currentCustomer.id}</div>
<div>Current Customer name: ${sessionScope.currentCustomer.name}</div>
</div>
<div sclass="customerBox">
<div>Last Customer id: ${sessionScope.lastCustomer.id}</div>
<div>Last Customer name: ${sessionScope.lastCustomer.name}</div>
</div>
</div>
</zk>
The repeated layout can be avoided by defining a template (customerBox) and apply the same template using different parameters.
<zk>
<div>
<apply template="customerBox" customer="${sessionScope.currentCustomer}" label="Current Customer"/>
<apply template="customerBox" customer="${sessionScope.lastCustomer}" label="Last Customer"/>
</div>
<template name="customerBox">
<div sclass="customerBox">
<div>${label} id: ${customer.id}</div>
<div>${label} name: ${customer.name}</div>
</div>
</template>
<!-- alternatively define the template content in a separate file
<template name="customerBox" src="/WEB-INF/templates/customerBox.zul"/>
-->
</zk>
As described above you can also apply the template directly using the templateURI.
<apply templateURI="/WEB-INF/templates/customerBox.zul" customer="${sessionScope.currentCustomer}" label="Current Customer"/>
Or as a syntax sugar you can define a custom tag inside the page.
<?component name="customerBox" templateURI="/WEB-INF/templates/customerBox.zul"?>
<zk>
<div>
<customerBox customer="${sessionScope.currentCustomer}" label="Current Customer"/>
<customerBox customer="${sessionScope.lastCustomer}" label="Last Customer"/>
</div>
</zk>
Finally you can declare the custom element globally in a lang-addon.xml, and omit the <?component ... ?> directive in the page.
<component>
<component-name>customerBox</component-name>
<template-uri>/WEB-INF/templates/customerBox.zul</template-uri>
</component>
ForEach
https://www.zkoss.org/wiki/ZK_Developer's_Reference/MVC/Controller/Wire_Components#Shadow_Selectors
CollectionTemplate
ZUL Template
Java Based Template
Using ZK8 Form Proxies in MVC
Creating a Simple CRUD Template
Summary
Download
- The source code for this article can be found on github.
Running the Example
Clone the repository
git clone [email protected]:zkoss-demo/mvc-shadow.git
cd mvc-shadow
git checkout master
The example war file can be built with maven:
mvn clean package
Execute using jetty:
mvn jetty:run
Then access the overview page http://localhost:8080/mvc-shadow/
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.