ZK8 Features for MVC - Shadow Elements - Part 1"

From Documentation
Line 182: Line 182:
 
</source>
 
</source>
  
 +
=== Reuse recurring elements ===
 +
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.
  
'''RESUME HERE''' '''RESUME HERE''' '''RESUME HERE''' '''RESUME HERE'''
+
Here a repeated layout only differing by customer object and a label.
 +
<source lang="xml">
 +
<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>
 +
</source>
 +
 
 +
The repeated layout can be avoided by defining a template (customerBox) and apply the same template using different parameters.
 +
<source lang="xml">
 +
<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>
 +
</source>
 +
 
 +
As described above you can also apply the template directly using the templateURI.
 +
 
 +
<source lang="xml">
 +
        <apply templateURI="/WEB-INF/templates/customerBox.zul" customer="${sessionScope.currentCustomer}" label="Current Customer"/>
 +
</source>
 +
 
 +
Or as a syntax sugar you can define a custom tag inside the page.
 +
 
 +
<source lang="xml">
 +
<?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>
 +
</source>
 +
 
 +
Finally you can declare the custom element globally in a lang-addon.xml, and omit the <?component ... ?> directive in the page.
 +
 
 +
<source lang="xml">
 +
    <component>
 +
        <component-name>customerBox</component-name>
 +
        <template-uri>/WEB-INF/templates/customerBox.zul</template-uri>
 +
    </component>
 +
</source>
 +
 
 +
 
 +
== RESUME HERE''' '''RESUME HERE''' '''RESUME HERE''' '''RESUME HERE ==
  
  

Revision as of 06:43, 21 September 2016

DocumentationSmall Talks2016OctoberZK8 Features for MVC - Shadow Elements - Part 1
ZK8 Features for MVC - Shadow Elements - Part 1

Author
Robert Wenzel, Engineer, Potix Corporation
Date
August 2016
Version
ZK 8.0

WarningTriangle-32x32.png This page is under construction, so we cannot guarantee the accuracy of the content!


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 introduce the most prominent features which are:

  • shadow elements
  • form proxies (later article)

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 scenarios today ... just read on.

Some background

If you already know the details and differences between <include> and Macro, you can just skip this paragraph.

Why a new shadow element? Let's compare what's been 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. Comparing the generated DOM we'll see that the <include> creates:

<div class="z-include">...</div>

and the Macro generates:

<div class="z-macro">...</div>

So far so good. 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.

If you don't need/want the additional <div> (e.g. because it breaks your layout, or you are inside a component hierarchy that only allows certain child components e.g. grid>rows>row) the case seems clear: use an inline macro LINK ME (which in return has its own technical limitations). Main impact is the root component of the macro being detached from the component tree - losing all its properties/custom-attributes, which you might want to refer to inside the included zul template (this gets additionally painful when using MVVM and dynamic bind annotations - not the topic in this MVC dedicated article). At the same time include doesn't have an inline mode.

If you need a different DOM element other than <div> you can use the enclosingTag property LINK ME of the macro (since 5.0.3) or <include/> since (7.0.4) LINK ME.

Often surprising (even if documented) are the side effects when using an <include> component with a url containing a query string ... while Macros simply ignore the query string. The query string with a changing parameter is often used as a workaround to reload the same included page.

<include src="customerDetails.zul?customerId=123"/>

Suddenly the <include> component changes to "defer" mode LINK ME resulting in a different component creation life cycle and a nested page. While necessary in certain cases this is usually more than you expect/need if you simply want to insert a zul fragment.

Last but not least there is a Template Composition LINK ME pattern which works again in a very unique way (... let's not go into details right now, we can do the same with <apply> without learning a separate syntax)

I assume you see where I am getting ... these grown components/features have diverged over time, causing quite some confusion and discussion about which to use, in which scenario and how to work around the limitations in various cases.

<apply> for static layout composition

In order to unify the approaches described above and to reduce the unexpected side effects and limitations the <apply> shadow element was introduced in ZK 8.

To enable it you have to add the zuti.jar to your project LINK ME.

Why "Shadow"? - Because it works behind the scenes (similar to a Shadow-DOM):

  • it represents a position inside the component tree used as an injection point for a <template>
  • it does not affect the parent-child-sibling relations between the real components LINK TO JUMPER'S article
  • it does not create any DOM element by itself - it is totally transparent to the client side (no more unwanted <div>)
  • it will even disappear automatically if not needed anymore to save memory (this can be disabled on demand)

Static usage examples

Like an include / macro to insert a zul file (optionally with parameters)

    <apply templateURI="customerDetails.zul" customerId="123"/>

Insert inline templates

    <apply template="${currentUser.hasEditPermission ? 'editable' : 'readonly'}>
        <template name="readonly">
           <label value="${person.name}"/>
        </template>
        <template name="editable">
           <textbox value="${person.name}"/>
        </template>
    </apply>

Insert external templates

    <apply template="${currentUser.hasEditPermission ? 'editable' : 'readonly'}>
        <template name="readonly" src="personView.zul"/>
        <template name="editable" src="personEdit.zul"/>
    </apply>

Inject contents templates into a layout template (example of a nested usage)

Define a layout template (e.g. pageLayout.zul ) containing any reusable layout structure with nested <apply> elements to inject the actual content where needed.

    <div sclass="wrapper">
        ...
        <div sclass="header">
            <apply template="headerContent"/>
        </div>
        <div sclass="content">
            <apply template="pageContent"/>
        </div>
        ...
    </div>

Use the pageLayout.zul anywhere and define the templates to be injected (inline or external zul file)

    <apply templateURI="pageLayout.zul">
        <template name="headerContent">
            My Application Overview <button label="logout"/>
        </template>
        <template name="pageContent" src="applicationOverviewPage.zul"/>
    </apply>

This greatly helps to reduce the size of your zul pages, and split them into smaller maintainable fragments.

As a syntax sugar you can define your own tags in a way very similar to macro components (inline or inside a lang-addon.zul). Internally the <apply> tag is extended adding a default templateURI (also other properties can be predefined).

Inline definition using the component processing-instruction LINK ME

    <?component name="sidebar" templateURI="/WEB-INF/zul/layout/sidebar.zul"/>
    <?component name="footer" templateURI="/WEB-INF/zul/layout/footer.zul"/>
    <zk>
        <div sclass="wrapper">
            <sidebar/>
            <apply template="someContent.zul" />
            <footer/>
        </div>
    </zk>

or make the tags globally available via lang-addon.zul LINK ME

 	<component>
 		<component-name>sidebar</component-name>
 		<template-uri>/WEB-INF/zul/layout/sidebar.zul</template-uri>
 	</component>
 	<component>
 		<component-name>footer</component-name>
 		<template-uri>/WEB-INF/zul/layout/footer.zul</template-uri>
 	</component>

Reuse recurring elements

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>


RESUME HERE RESUME HERE RESUME HERE RESUME HERE

... 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 idspace (you can control that yourself as needed)
  • 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;



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)

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.