ZK8 Features for MVC - Shadow Elements - Part 1"

From Documentation
(47 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{Template:Smalltalk_Author|
 
{{Template:Smalltalk_Author|
 
|author=Robert Wenzel, Engineer, Potix Corporation
 
|author=Robert Wenzel, Engineer, Potix Corporation
|date=August 2016
+
|date=October 2016
 
|version=ZK 8.0
 
|version=ZK 8.0
 
}}
 
}}
 
{{Template:UnderConstruction}}
 
 
  
 
= Introduction =
 
= Introduction =
  
ZK 8 added several new Features mainly improving Component control when using the MVVM design pattern. The question I sometimes heard since was:
+
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 ?'''
+
'''(How) Can our project benefit from these features when using (''only'') MVC?'''
  
 
To answer the question I'll introduce the most prominent features which are:
 
To answer the question I'll introduce the most prominent features which are:
  
* shadow elements
+
* [http://books.zkoss.org/zk-mvvm-book/8.0/shadow_elements/shadow_elements.html shadow elements]
 
* form proxies (later article)
 
* form proxies (later article)
  
 
Let's have a look where they can be used in the MVC world.
 
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.
+
If you are missing a feature, please let us know in the Comments section below.
 +
 
 +
The text below will contain several links pointing to the technical documentation. Hence another goal for this article is to put these sometimes unrelated articles into a common context rather than being a complete documentation.
  
 
= Shadow Elements in MVC =
 
= Shadow Elements in MVC =
 
+
{{Template:ZK EE}}
Some of the Shadow Elements '''LINK ME''' are more/less beneficial in MVC than others here a short summary
+
Some of the [http://books.zkoss.org/zk-mvvm-book/8.0/shadow_elements/shadow_elements.html Shadow Elements] are more/less beneficial in MVC than others, here's a short summary:
  
 
* '''<if>'''
 
* '''<if>'''
Line 31: Line 30:
 
: used statically you can render parts of your UI based on a conditional EL expression <code><if test="${some.condition}">...</if></code>
 
: used statically you can render parts of your UI based on a conditional EL expression <code><if test="${some.condition}">...</if></code>
 
* '''<choose>, <when>, <otherwise>'''
 
* '''<choose>, <when>, <otherwise>'''
: similar to <if> no real benefit for MVC
+
: similar to <if>, no real benefit for MVC
 
* '''<forEach>'''
 
* '''<forEach>'''
: '''very useful''' this will be the topic in next smalltalk
+
: '''very useful''', this will be the topic in [[Small_Talks/2017/March/ZK8_Features_for_MVC_-_Shadow_Elements_-_Part_2|Part 2]]
 
* '''<apply>'''
 
* '''<apply>'''
 
: I'll show a few usage scenarios today ... just read on.
 
: I'll show a few usage scenarios today ... just read on.
Line 39: Line 38:
 
== Some background ==
 
== Some background ==
  
If you already know the details and differences between <include> and Macro, you can just skip this paragraph.
+
If you already know the details and differences between [[ZK_Component_Reference/Essential_Components/Include | <include>]] and [[ZK_Developer%27s_Reference/UI_Composing/Macro_Component | Macro component]], you can just skip this paragraph.
  
 
Why a new ''shadow'' element? Let's compare what's been there previously.  
 
Why a new ''shadow'' element? Let's compare what's been there previously.  
Line 56: Line 55:
 
</source>
 
</source>
  
These 2 examples roughly will give the same output. Comparing the generated DOM we'll see that the <include> creates:
+
These 2 examples will roughly give the same output. Comparing the generated DOM, we'll see that the <include> creates:
  
 
<pre><div class="z-include">...</div></pre>  
 
<pre><div class="z-include">...</div></pre>  
Line 66: Line 65:
 
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.
 
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 &lt;div&gt; (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).  
+
If you don't need/want the additional &lt;div&gt; (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 [[ZK_Developer%27s_Reference/UI_Composing/Macro_Component/Inline_Macros | inline macro]] (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.
+
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 &lt;div&gt; you can use the '''enclosingTag''' property '''LINK ME''' of the macro (since 5.0.3) or <include/> since (7.0.4) '''LINK ME'''.
+
If you need a different DOM element other than &lt;div&gt; you can use the '''enclosingTag''' property of [https://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/HtmlMacroComponent.html#setEnclosingTag(java.lang.String) the macro (since 5.0.3)] or [https://www.zkoss.org/javadoc/latest/zk/org/zkoss/zul/Include.html#setEnclosingTag(java.lang.String) <include/> since (7.0.4)].
  
 
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.
 
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.
Line 77: Line 76:
 
</source>
 
</source>
  
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.
+
After adding the url parameter the <include> component changes to [[ZK_Component_Reference/Essential_Components/Include#Defer | "defer" mode]] 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 compose a page from smaller zul fragments.
  
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)
+
Last but not least there is a [[ZK_Developer's_Reference/UI_Patterns/Templating/Composition | Template Composition]] 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 while gaining flexibility).
  
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.
+
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 ==
 
== <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.
+
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 [http://books.zkoss.org/zk-mvvm-book/8.0/shadow_elements/shadow_elements.html#setup add the zuti.jar] to your project.
  
To enable it you have to add the zuti.jar to your project '''LINK ME'''.
+
[[File:Shadow_Diagram.PNG|right|200px|link=http://books.zkoss.org/zk-mvvm-book/8.0/shadow_elements/shadow_elements.html]]
  
 
Why "Shadow"? - Because it works behind the scenes (similar to a Shadow-DOM):
 
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 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 affect the parent-child-sibling relations between the ''real'' components  
 
* it does not create any DOM element by itself - it is totally transparent to the client side (no more unwanted &lt;div&gt;)
 
* it does not create any DOM element by itself - it is totally transparent to the client side (no more unwanted &lt;div&gt;)
 
* it will even disappear automatically if not needed anymore to save memory (this can be disabled on demand)
 
* it will even disappear automatically if not needed anymore to save memory (this can be disabled on demand)
Line 103: Line 103:
 
</source>
 
</source>
  
'''Insert inline templates'''
+
==== Insert inline templates ====
  
 
<source lang="xml">
 
<source lang="xml">
     <apply template="${currentUser.hasEditPermission ? 'editable' : 'readonly'}>
+
     <apply template="${currentUser.hasEditPermission ? 'editable' : 'readonly'}">
 
         <template name="readonly">
 
         <template name="readonly">
 
           <label value="${person.name}"/>
 
           <label value="${person.name}"/>
Line 116: Line 116:
 
</source>
 
</source>
  
'''Insert external templates'''
+
==== Insert external templates ====
  
 
<source lang="xml">
 
<source lang="xml">
     <apply template="${currentUser.hasEditPermission ? 'editable' : 'readonly'}>
+
     <apply template="${currentUser.hasEditPermission ? 'editable' : 'readonly'}">
 
         <template name="readonly" src="personView.zul"/>
 
         <template name="readonly" src="personView.zul"/>
 
         <template name="editable" src="personEdit.zul"/>
 
         <template name="editable" src="personEdit.zul"/>
Line 125: Line 125:
 
</source>
 
</source>
  
'''Inject contents templates into a layout template''' (example of a nested usage)
+
==== Inject content(s) into a layout template ====
 +
This is an example of a nested usage, to replace the '''template composition pattern''' mentioned above
  
Define a layout template (e.g. pageLayout.zul ) containing any reusable layout structure with nested <apply> elements to inject the actual content where needed.
+
1. Define a layout template (e.g. pageLayout.zul ) containing any reusable layout structure with nested <apply> elements to inject the actual content where needed.
 
<source lang="xml">
 
<source lang="xml">
 
     <div sclass="wrapper">
 
     <div sclass="wrapper">
Line 141: Line 142:
 
</source>
 
</source>
  
Use the pageLayout.zul anywhere and define the templates to be injected (inline or external zul file)
+
2. Use the pageLayout.zul anywhere and define the templates to be injected (inline or external zul file)
 
<source lang="xml">
 
<source lang="xml">
 
     <apply templateURI="pageLayout.zul">
 
     <apply templateURI="pageLayout.zul">
Line 153: Line 154:
 
This greatly helps to reduce the size of your zul pages, and split them into smaller maintainable fragments.
 
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).
+
==== Named <apply> ====
 +
As a '''syntax sugar''' you can [[ZK_Developer%27s_Reference/UI_Composing/ZUML/Include_a_Page#Application-wide_Named_.3CApply.3E|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).
 
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'''
+
'''Inline definition''' using the [[ZUML_Reference/ZUML/Processing_Instructions/component#The_by-template_Format | component processing-instruction]]
 
<source lang="xml">
 
<source lang="xml">
 
     <?component name="sidebar" templateURI="/WEB-INF/zul/layout/sidebar.zul"/>
 
     <?component name="sidebar" templateURI="/WEB-INF/zul/layout/sidebar.zul"/>
Line 169: Line 171:
 
</source>
 
</source>
  
or make the tags globally available via lang-addon.zul '''LINK ME'''
+
Or make the tags globally available via [[ZK_Client-side_Reference/Language_Definition | lang-addon.zul]] ('''Tip:''' using this method the zul fragments and configuration can also be packaged into a separate jar file)
  
 
<source lang="xml">
 
<source lang="xml">
 +
<language-addon>
 +
<addon-name>my-addon</addon-name>
 +
<version>0.1</version>
 +
<language-name>xul/html</language-name>
 +
<depends>zuti</depends>
 
  <component>
 
  <component>
 
  <component-name>sidebar</component-name>
 
  <component-name>sidebar</component-name>
Line 180: Line 187:
 
  <template-uri>/WEB-INF/zul/layout/footer.zul</template-uri>
 
  <template-uri>/WEB-INF/zul/layout/footer.zul</template-uri>
 
  </component>
 
  </component>
 +
</language-addon>
 
</source>
 
</source>
  
=== Reuse recurring elements ===
+
=== Refactor repeated markup ===
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.
+
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.
  
Here a repeated layout only differing by customer object and a label.
 
 
<source lang="xml">
 
<source lang="xml">
 
<zk>
 
<zk>
Line 201: Line 213:
 
</source>
 
</source>
  
The repeated layout can be avoided by defining a template (customerBox) and apply the same template using different parameters.
+
The repeated layout can be avoided by defining a template (customerBox) and apply the same template using the varying parameters.
 
<source lang="xml">
 
<source lang="xml">
 
<zk>
 
<zk>
Line 222: Line 234:
 
</source>
 
</source>
  
As described above you can also apply the template directly using the templateURI.
+
As described above you can also apply the templateURI directly and start using the customerBox in multiple pages.
  
 
<source lang="xml">
 
<source lang="xml">
         <apply templateURI="/WEB-INF/templates/customerBox.zul" customer="${sessionScope.currentCustomer}" label="Current Customer"/>
+
         <apply templateURI="/WEB-INF/templates/customerBox.zul"  
 +
                  customer="${sessionScope.currentCustomer}" label="Current Customer"/>
 
</source>
 
</source>
  
Or as a syntax sugar you can define a custom tag inside the page.
+
Finally if that looks too long - define a custom tag inline or in a lang-addon.xml.
  
 +
lang-addon.xml
 
<source lang="xml">
 
<source lang="xml">
<?component name="customerBox" templateURI="/WEB-INF/templates/customerBox.zul"?>
+
<language-addon>
<zk>
+
     <addon-name>my-shadow-elements</addon-name>
     <div>
+
    <version>1.0</version>
        <customerBox customer="${sessionScope.currentCustomer}" label="Current Customer"/>
+
     <language-name>xul/html</language-name>
        <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.
+
    <depends>zuti</depends>
  
<source lang="xml">
 
 
     <component>
 
     <component>
 
         <component-name>customerBox</component-name>
 
         <component-name>customerBox</component-name>
 
         <template-uri>/WEB-INF/templates/customerBox.zul</template-uri>
 
         <template-uri>/WEB-INF/templates/customerBox.zul</template-uri>
 
     </component>
 
     </component>
 +
</language-addon>
 
</source>
 
</source>
  
 
+
resulting zul file
== RESUME HERE''' '''RESUME HERE''' '''RESUME HERE''' '''RESUME HERE ==
 
 
 
 
 
... can be replaced by an <apply> element:
 
 
 
 
<source lang="xml">
 
<source lang="xml">
<apply templateURI="customerDetails.zul" customerId="123"/>
+
<zk>
 +
    <div>
 +
        <customerBox customer="${sessionScope.currentCustomer}" label="Current Customer"/>
 +
        <customerBox customer="${sessionScope.lastCustomer}"  label="Last Customer"/>
 +
    </div>
 +
</zk>
 
</source>
 
</source>
  
The difference is that an <apply> does less things "under the hood" compared to <include>:
+
Now whenever the customerBox layout needs to change only one template is affected.
* it does not create an additional <code><div class="z-include"/></code> 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(...).
+
== Dynamic usage ==
 +
Of course in your dynamic application parameters or templateURIs need to change from time to time. This means you'll need to control them in your composer.
  
To save memory the '''<apply>''' shadow element is even removed completely from the server side component (shadow) tree when used purely statically.
+
'''Remember:''' To save memory the '''<apply>''' shadow element is removed completely from the server side component/shadow tree when used in a purely static way - (i.e. only raw string properties or EL ${...} parameters)
The shadow elements are not removed when used with dynamic parameters. E.g.:
+
The shadow elements are not removed when used with dynamic parameters:
* in MVVM using the ZKBIND anotations (@load/@init/@ref)
+
* in MVVM ZKBIND annotations (@load/@init/@ref) are automatically considered dynamic
* in MVC by adding the special attribute '''dynamicValue="true"'''
+
* in MVC the special attribute '''dynamicValue="true"''' needs to be added
  
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'''
 
  
 +
Examples for a pure static <apply>:
 
<source lang="xml" high="2">
 
<source lang="xml" high="2">
   <div>
+
   <apply templateURI="statusBox.zul" status="${c:l('statusBox.OK')}" />
    <apply id="panelContent"/>
+
   <apply templateURI="home.zul" />
   </div>
 
 
</source>
 
</source>
* '''Line 2:''' assign an id in the zul file
 
  
<source lang="java" high="1">
+
If we now want to update the '''status''' or the '''templateURI''' we need to change the property, and re-render the template.
   @Wire("::shadow#panelContent")
+
To avoid automatic removal and gain access to the <apply> element in a composer we have to indicate to the zul processor we want to keep the shadow element for later updates.
   private Apply panelContent;
+
 
 +
Dynamic <apply> via dynamicValue:
 +
<source lang="xml" high="1,3">
 +
   <apply id="statusBox" dynamicValue="true"
 +
            templateURI="statusBox.zul" status="${c:l('statusBox.OK')}" />
 +
   <apply id="pageContent" dynamicValue="true"
 +
            templateURI="home.zul" />
 
</source>
 
</source>
* '''Line 1:''' wire by id using a [https://www.zkoss.org/wiki/ZK_Developer's_Reference/MVC/Controller/Wire_Components#Shadow_Selectors shadow selector]
 
  
 +
In our SelectorComposer we can now wire the <apply> elemets by ID using the [[ZK_Developer's_Reference/MVC/Controller/Wire_Components#Shadow_Selectors | shadow selector]] syntax.
  
 +
<source lang="java" high="1,3">
 +
  @Wire("::shadow#statusBox")
 +
  private Apply errorBox;
 +
  @Wire("::shadow#homeContent")
 +
  private Apply homeContent;
 +
</source>
  
 +
To re-render the content of an <apply> element - ''re-apply the template'' (with or without updated properties) - you need to call the <javadoc class="false" method="recreate()">org.zkoss.zk.ui.HtmlShadowElement</javadoc> method.
 +
Parameters can be set using <javadoc class="false" method="setDynamicProperty(java.lang.String, java.lang.Object)">org.zkoss.zuti.zul.Apply</javadoc>.
  
Still in pure java you could just call Executions.createComponents(...) '''LINK ME'''
+
<source lang="java" high="4,5,11,12">
 +
  @Listen("onClick=#settingsLink")
 +
  public void navigateToSettings() {
 +
    //some additional navigation code
 +
    homeContent.setTemplateURI("settings.zul"); //update the template url
 +
    homeContent.recreate(); //trigger the update - re-renders also when the templateURI was not changed
 +
  }
  
helps to compose the UI from ZUL fragments (more flexible than <include> or Macro components)
+
  @Listen("onTimer=#updateStatus")
 +
  public void updateStatus() {
 +
    String newStatus = calculateCurrentStatus();
 +
    statusBox.setDynamicProperty("status", newStatus);
 +
    statusBox.recreate(); //re-apply template with updated status-property
 +
  }
 +
</source>
  
==  ForEach ==
+
Of course you can update multiple properties and template(URI/name) in any order before finally calling recreate(). This separation between changing the properties and calling recreate() gives your more render control. (It also solves the issue with <include> when trying to re-render the content without changing the include source).
https://www.zkoss.org/wiki/ZK_Developer's_Reference/MVC/Controller/Wire_Components#Shadow_Selectors
+
Also if you prefer real setters/getters for your dynamic properties you can subclass the Apply class and configure a custom implementation class in the zul file directly or in a lang-addon.xml.
  
CollectionTemplate
+
inline in a zul file
 +
<source lang="xml">
 +
  <apply use="my.package.MyExtendedApply" .../>
 +
</source>
  
== ZUL Template ==
+
define as custom tag <my-apply> in lang addon
 
+
<source lang="xml">
== Java Based Template ==
+
    <component>
 
+
        <component-name>myApply</component-name>
= Using ZK8 Form Proxies in MVC =
+
        <extends>apply</extends>
 
+
        <component-class>my.package.MyExtendedApply</component-class>
== Creating a Simple CRUD Template ==
+
    </component>
 +
</source>
  
 
= Summary =
 
= Summary =
  
 +
As shown above the <apply> element has multiple usages inside an MVC application, it doesn't depend on MVVM at all (still it matches the MVVM pattern since it allows view model based control over it's properties). It reduces the HTML markup rendered by avoiding unwanted <div> elements and it saves memory by destroying itself if used purely static. Besides it allows dynamic control and component re-creation on demand.
  
 +
This time there's no runnable example code as the code fragments above are meant to illustrate the idea and the reasons when to use either approach. You'll know better which templates you want to <apply> in your application ;)
  
== Download ==
+
Let me know your experiences and feedback in the '''Comments''' section below.
* The source code for this article can be found on [https://github.com/zkoss-demo/mvc-shadow 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/
+
The next article will show how the <forEach> element is equally useful in an MVC application in reducing the effort to render collection based markup.
  
 
{{Template:CommentedSmalltalk_Footer_new|
 
{{Template:CommentedSmalltalk_Footer_new|

Revision as of 10:06, 30 March 2017

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

Author
Robert Wenzel, Engineer, Potix Corporation
Date
October 2016
Version
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 our project benefit from these features when using (only) MVC?

To answer the question I'll introduce the most prominent features which are:

Let's have a look where they can be used in the MVC world.

If you are missing a feature, please let us know in the Comments section below.

The text below will contain several links pointing to the technical documentation. Hence another goal for this article is to put these sometimes unrelated articles into a common context rather than being a complete documentation.

Shadow Elements in MVC

  • Available for ZK:
  • http://www.zkoss.org/product/zkhttp://www.zkoss.org/whyzk/zkeeVersion ee.png

Some of the Shadow Elements are more/less beneficial in MVC than others, here's 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 Part 2
  • <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 component, 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 will roughly 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 (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 of the macro (since 5.0.3) or <include/> since (7.0.4).

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"/>

After adding the url parameter the <include> component changes to "defer" mode 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 compose a page from smaller zul fragments.

Last but not least there is a Template Composition 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 while gaining flexibility).

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.

Shadow Diagram.PNG

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
  • 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 content(s) into a layout template

This is an example of a nested usage, to replace the template composition pattern mentioned above

1. 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>

2. 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.

Named <apply>

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

    <?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 (Tip: using this method the zul fragments and configuration can also be packaged into a separate jar file)

<language-addon>
	<addon-name>my-addon</addon-name>
	<version>0.1</version>
	<language-name>xul/html</language-name>
	<depends>zuti</depends>
 	<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>
</language-addon>

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

<language-addon>
    <addon-name>my-shadow-elements</addon-name>
    <version>1.0</version>
    <language-name>xul/html</language-name>

    <depends>zuti</depends>

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

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.

Dynamic usage

Of course in your dynamic application parameters or templateURIs need to change from time to time. This means you'll need to control them in your composer.

Remember: To save memory the <apply> shadow element is removed completely from the server side component/shadow tree when used in a purely static way - (i.e. only raw string properties or EL ${...} parameters) The shadow elements are not removed when used with dynamic parameters:

  • in MVVM ZKBIND annotations (@load/@init/@ref) are automatically considered dynamic
  • in MVC the special attribute dynamicValue="true" needs to be added


Examples for a pure static <apply>:

  <apply templateURI="statusBox.zul" status="${c:l('statusBox.OK')}" />
  <apply templateURI="home.zul" />

If we now want to update the status or the templateURI we need to change the property, and re-render the template. To avoid automatic removal and gain access to the <apply> element in a composer we have to indicate to the zul processor we want to keep the shadow element for later updates.

Dynamic <apply> via dynamicValue:

  <apply id="statusBox" dynamicValue="true"
            templateURI="statusBox.zul" status="${c:l('statusBox.OK')}" />
  <apply id="pageContent" dynamicValue="true"
            templateURI="home.zul" />

In our SelectorComposer we can now wire the <apply> elemets by ID using the shadow selector syntax.

  @Wire("::shadow#statusBox")
  private Apply errorBox;
  @Wire("::shadow#homeContent")
  private Apply homeContent;

To re-render the content of an <apply> element - re-apply the template (with or without updated properties) - you need to call the recreate() method. Parameters can be set using setDynamicProperty(String, Object).

  @Listen("onClick=#settingsLink")
  public void navigateToSettings() {
    //some additional navigation code
    homeContent.setTemplateURI("settings.zul"); //update the template url
    homeContent.recreate(); //trigger the update - re-renders also when the templateURI was not changed
  }

  @Listen("onTimer=#updateStatus")
  public void updateStatus() {
    String newStatus = calculateCurrentStatus();
    statusBox.setDynamicProperty("status", newStatus);
    statusBox.recreate(); //re-apply template with updated status-property
  }

Of course you can update multiple properties and template(URI/name) in any order before finally calling recreate(). This separation between changing the properties and calling recreate() gives your more render control. (It also solves the issue with <include> when trying to re-render the content without changing the include source). Also if you prefer real setters/getters for your dynamic properties you can subclass the Apply class and configure a custom implementation class in the zul file directly or in a lang-addon.xml.

inline in a zul file

  <apply use="my.package.MyExtendedApply" .../>

define as custom tag <my-apply> in lang addon

    <component>
        <component-name>myApply</component-name>
        <extends>apply</extends>
        <component-class>my.package.MyExtendedApply</component-class>
    </component>

Summary

As shown above the <apply> element has multiple usages inside an MVC application, it doesn't depend on MVVM at all (still it matches the MVVM pattern since it allows view model based control over it's properties). It reduces the HTML markup rendered by avoiding unwanted

elements and it saves memory by destroying itself if used purely static. Besides it allows dynamic control and component re-creation on demand.

This time there's no runnable example code as the code fragments above are meant to illustrate the idea and the reasons when to use either approach. You'll know better which templates you want to <apply> in your application ;)

Let me know your experiences and feedback in the Comments section below.

The next article will show how the <forEach> element is equally useful in an MVC application in reducing the effort to render collection based markup.


Comments



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