ZK8 Features for MVC - Shadow Elements - Part 1

From Documentation
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>

Refactor repeated markup

Besides the larger page layout the <apply> element is also useful when refactoring otherwise repeated markup.

Here a simple example of the repeated layout for a customerBox - only differing by customer object and a label. (It doesn't really matter where the customer object is coming from - here I get it from a session attribute - any other EL resolving to this kind of object will work equivalently - the existing variable resolvers will also work here - so nothing really new here). Imagine you have used the same customerBox in various pages and you need to update the layout or add a new field. Search and replace will be difficult, as the fragment is obviously not the same for all usages.

<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 the varying 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 templateURI directly and start using the customerBox in multiple pages.

        <apply templateURI="/WEB-INF/templates/customerBox.zul" 
                  customer="${sessionScope.currentCustomer}" label="Current Customer"/>

Finally if that looks too long - define a custom tag inline or in a lang-addon.xml.

lang-addon.xml

    <component>
        <component-name>customerBox</component-name>
        <template-uri>/WEB-INF/templates/customerBox.zul</template-uri>
    </component>

resulting zul file

<zk>
    <div>
        <customerBox customer="${sessionScope.currentCustomer}" label="Current Customer"/>
        <customerBox customer="${sessionScope.lastCustomer}"  label="Last Customer"/>
    </div>
</zk>

Now whenever the customerBox layout needs to change only one template is affected.

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.