Client-side UI Composing"

From Documentation
m ((via JWB))
(22 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
{{ZKDevelopersReferencePageHeader}}
 
{{ZKDevelopersReferencePageHeader}}
  
=Overview=
+
Though optional, you could have the total control of the client's functionality without the assistance of server-side coding. Generally, you don't need to do it. You don't even need to know how ZK Client Engine and client-side widgets communicate with the server. Their states can be synchronized automatically with ZK. However, you can still control this type of synchronization if you want. It is the so-called Server-client fusion.
  
[[File:ZKComDevEss_widget_component_application.png]]
+
A good rule of thumb is that you should handle events and manipulate UI mostly, if not all, on the server, since it is more productive. Then, you could improve the responsiveness and visual effects, and/or reduce server loading by handling them at the client, when it is appropriate. Notice that JavaScript is readable by any user, so be careful not to expose sensitive data or business logic when migrating some code from server to client.  
  
A UI object visible to a user at the client is hosted by a JavaScript object<ref>It actually depends on the device. For Ajax, it is a JavaScript object. For Android devices, it is a Java object.</ref> called a widget (<javadoc directory="jsdoc">zk.Widget</javadoc>). On the other hand, a component is a Java object (<javadoc>org.zkoss.zk.ui.Component</javadoc>) representing the UI object at the server that an application manipulates directly. Once a component is attached to a page, a widget is created at client automatically. Furthermore, any state change of the component at the server will be updated to the widget at the client.
+
* About client-side UI composing, please refer to [[ZK Client-side Reference/General Control/UI Composing|ZK Client-side Reference: UI Composing]].
 +
* About customizing client-side widget's behavior, please refer to [[ZK Client-side Reference/General Control/Widget Customization|ZK Client-side Reference: Widget Customization]].
 +
* About client-side event handling, please refer to [[ZK Client-side Reference/General Control/Event Listening|ZK Client-side Reference: Event Listening]]
  
Generally, you need not to know the existence of widgets. Ajax requests and the state synchronization are handled automatically by ZK and the components automatically. However, you are allowed to instantiate or alert any client-side widgets directly at the client (in JavaScript). It is the so-called Server+client fusion.
+
=Version History=
 
 
The rule of thumb is to compose and manipulate UI at the server first since it is easier. Then, you could reduce the load of the server by composing some UI at the client when it is appropriate. Notice that JavaScript is readable by any user, so be careful not to expose sensitive data or business logic when migrating some code from server to client.
 
 
 
Here we describe how to compose UI in JavaScript at the client. For client-side event handling, please refer to the [[ZK Developer's Reference/Event Handling/Client-side Event Listening|Client-side Event Handling]] section. For XML-based UI composing at the client, please refer to the [[ZK Developer's Reference/UI Composing/iZUML|iZUML]] section.
 
 
 
<blockquote>
 
----
 
<references/>
 
</blockquote>
 
 
 
=Modify Widget's State at Client=
 
 
 
While the states of a widget is maintained automatically if you update the corresponding component at the server, you could modify the widget state directly at the server. The modification is straightforward: call the correct method with the arguments you want. Notice that it is JavaScript for Ajax browsers.
 
 
 
<source lang="JavaScript">
 
var foo = zk.Widget.$('foo');
 
foo.setValue("What's Up?");
 
</source>
 
 
 
For a complete API available to the client-side fusion, please refer to [http://www.zkoss.org/javadoc/latest/jsdoc/ JavaScript API].
 
 
 
==Fusion with Server-side ZUML and Java==
 
It is suggested that the client-side UI composing is better designed to minimize the network round-trip, provide effects and other enhancement, while the most, if not all, of the application is better to be done at the server. Thus, here we only discuss this kind of addon, aka., fusion. For pure-client approach, please refer to [[Small Talks/2009/July/ZK 5.0 and Client-centric Approach|Small Talk: ZK 5.0 and Client-centric Approach]].
 
 
 
Depending on your requirement, there are typically two situations we could ''fuse'' the client-side code:
 
 
 
#Register a client-side event listener.
 
#Override widget's default behavior
 
 
 
For example, suppose we want to open the drop down when a commbox gains the focus, then we register a client-side event listener for the onFocus event as follows.
 
 
 
<source lang="xml">
 
<div>
 
  <combobox xmlns:w="client" w:onFocus="this.open()"/>
 
</div>
 
</source>
 
 
 
As shown, we have to use the [[ZUML Reference/ZUML/Namespaces/Client|client namespace]] to indicate the onFocus attribute is for the client-side event listener. It is done by apply [http://www.w3schools.com/xml/xml_namespaces.asp XML namespace]:
 
 
 
*Add the <code>xmlns:w="client"</code> attribute
 
*Prefix <code>w:</code> before onFocus
 
 
 
For more information about the client-side event listener, please refer to the [[ZK Developer's Reference/Event Handling/Client-side Event Listening|Client-side Event Listening]] section.
 
 
 
The other typical situation to fuse the client-side code is to override the default behavior of a widget. We will discuss it later.
 
 
 
==Identify Widget at Client==
 
 
 
When the client event is invoked, you can reference the widget using <tt>this</tt> and the event using <tt>event</tt>. In the following example, <tt>this</tt> refers to the label.
 
 
 
<source lang="xml">
 
<window xmlns:w="client">
 
  <label value="change me by click" w:onClick="this.setValue('clicked');"/>
 
</window>
 
</source>
 
 
 
To retrieve a fellow<ref>A widget in the same [[ZK Developer's Reference/UI Composing/Component-based UI#ID_Space|ID space]].</ref>, you could use <javadoc directory="jsdoc" method="$f(_global_.String)">zk.Widget</javadoc>. It works in a similar manner as <javadoc method="getFellow(java.lang.String)">org.zkoss.zk.ui.Component</javadoc>.  For example,
 
 
 
<source lang="JavaScript">
 
this.$f('foo').setValue('found');
 
this.$().foo.setValue('found'); //equivalent to the above statement
 
</source>
 
  
If you don't have a widget as a reference, you could use <javadoc directory="jsdoc" method="$(zk.Object, _global_.Map)">zk.Widget</javadoc>. Notice it assumes there is only one widget with the given ID in all ID spaces of the desktop. For example,
+
{| class='wikitable' | width="100%"
 
 
<source lang="JavaScript">
 
zk.Widget.$('foo').setValue('found');
 
</source>
 
 
 
In additions, you can use jQuery to select a DOM element of a widget<ref>Since ZK 5.0.2</ref>. For example <code>jq("@window")</code> will select DOM elements of all window widget. And, <code>jq("$win1")</code> will select DOM elements of all widgets whose ID is <code>win1</code>. (see <javadoc directory="jsdoc">_global_.jq</javadoc>).
 
 
 
<source lang="xml">
 
<window xmlns:w="http://www.zkoss.org/2005/zk/client">
 
<vbox>
 
<label id="labelone" value="click to change"
 
w:onClick="this.setValue('changed by click label');" />
 
 
 
<button label="button"
 
w:onClick="this.$f('labelone').setValue('changed by button');" />
 
 
 
<html><![CDATA[
 
  <a href="javascript:;" onclick="zk.Widget.$(jq('$labelone')[0]).setValue('changed with jq');">not widget</a>
 
]]></html>
 
 
</vbox>
 
</window>
 
</source>
 
 
 
<blockquote>
 
----
 
<references/>
 
</blockquote>
 
 
 
=Instantiate Widget at Client=
 
 
 
A widget has to be create to make a component visible at the client (once it has been attached to a page). However, you could instantiate a widget at client, without the corresponding component at the server. To extreme extent, you could create all widgets at client (of course, it is costly and less secure).
 
 
 
To instantiate a widget is similar to a widget, except we can pass all initial values into the constructor. For example,
 
 
 
<source lang="JavaScript">
 
new zul.wnd.Window({
 
    title: 'Hello, World',
 
    border: 'normal',
 
    children: [
 
        new zul.wgt.Label({value: 'Hi, '}),
 
        new zul.wgt.Button({
 
            label: 'Click Me!',
 
            listeners:  {
 
                onClick: function (evt) {
 
                    alert('Hi, you clicked me');
 
                }
 
            }
 
        })
 
    ]
 
});
 
</source>
 
 
 
As shown, the initial values can be passed as a map. In additions, the <code>children</code> property could be used to specify an array of child widgets, and the <code>listeners</code> property to specify a map of listeners.
 
 
 
In additions to instantiate widgets in JavaScript, you could use a markup language called iZUML. Please refer to the [[ZK Developer's Reference/UI Composing/iZUML|iZUML]] section for more information.
 
 
 
== Attach Widget to DOM ==
 
 
 
Once a widget is instantiated, you could attach it to the browser's DOM tree to make it visible to users<ref>Notice that a widget is not visible to users unless it is attached to the browser's DOM tree.</ref>. It can be done in one of two ways:
 
 
 
#Make it as a child of another widget that already being attached
 
#Replace or insert it to a DOM element
 
 
 
You could use <javadoc directory="jsdoc" method="appendChild(zk.Widget)">zk.Widget</javadoc> or <javadoc directory="jsdoc" method="insertBefore(zk.Widget, zk.Widget)">zk.Widget</javadoc>. For example,
 
 
 
<source lang="JavaScript">
 
<vlayout>
 
  <button label="Click Me" xmlns:w="client"
 
    w:onClick="this.parent.appendChild(new zul.wgt.Label({value: 'Clicked'}))"/>
 
</vlayout>
 
</source>
 
 
 
In additions, we could replace an existent DOM element with a widget (not attached yet). For example,
 
 
 
<source lang="JavaScript"><zk>
 
  <n:div id="anchor" xmlns:n="native"/>
 
  <button label="Click Me" xmlns:w="client"
 
    w:onClick="new zul.wgt.Label({value: 'Clicked'}).replaceHTML('#anchor')"/>
 
</zk>
 
</source>
 
 
 
where we use the [[ZUML Reference/ZUML/Namespaces/Native|native namespace]] to create a DOM element and then replace it with the label widgt.
 
 
 
<blockquote>
 
----
 
<references/>
 
</blockquote>
 
 
 
==When to Run Your JavaScript Code==
 
 
 
ZK Client Engine loads a JavaScript package only when it is required. It minimizes the memory footprint at the client. However, it also mean that you cannot run your JavaScript code until the required packages have been loaded. It can be done by use of <javadoc directory="jsdoc" method="load(_global_.String, _global_.Function)">_global_.zk</javadoc>. For example, suppose you're not sure if the <code>zul.wnd</code> and <code>zul.grid</code> package has been loaded, when you are going to instantiate <javadoc directory="jsdoc">zul.wnd.Window</javadoc> and <javadoc directory="jsdoc">zul.grid.Grid</javadoc>, you could do as follows.
 
 
 
<source lang="JavaScript">
 
zk.load("zul.wnd,zul.grid", function () { //load zul.wnd and zul.grid if they aren't loaded yet
 
    //In this function, you could access zul.wnd.Window and zul.grid.Grid whatever you want
 
    new zul.wnd.Window({children: [new zul.grid.Grid()]});
 
});
 
</source>
 
 
 
where <javadoc directory="jsdoc" method="load(_global_.String, _global_.Function)">_global_.zk</javadoc> loads the <code>zul.wnd</code> and <code>zul.grid</code> packages and then invokes the function when they have been loaded.
 
 
 
Notice that there is another method for similar purpose called <javadoc directory="jsdoc" method="aferLoad(_global_.String, _global_.Function)">_global_.zk</javadoc>. Unlike <javadoc directory="jsdoc" method="load(_global_.String, _global_.Function)">_global_.zk</javadoc>, <javadoc directory="jsdoc" method="afterLoad(_global_.String, _global_.Function)">_global_.zk</javadoc> won't load the packages. Rather, it queues the given function and invokes it when the packages have been loaded. It is useful when you want to override the default behavior of a widget. We will discuss it later.
 
 
 
=Override Widget's Default Behavior=
 
 
 
There are many ways to override the default behavior of widgets and even ZK Client Engine. JavaScript is a dynamic language and you could override almost any method you want.
 
 
 
For example, suppose we want to change the CSS style of a label when its value is changed, then we might have the code as follows.
 
 
 
<source lang="xml">
 
<window xmlns:w="http://www.zkoss.org/2005/zk/client">
 
      <label>
 
            <attribute w:name="setValue">
 
            function (value) {
 
                  this.$setValue(value); //call the original method
 
                  if (this.desktop) {
 
                        this._flag = !this._flag;
 
                        this.setStyle('background:'+(this._flag ? 'red':'green'));
 
                  }
 
            }
 
            </attribute>
 
      </label>
 
</window>
 
</source>
 
 
 
where
 
 
 
* We specify [[ZUML Reference/ZUML/Namespaces/Client|client namespace]] to the <code>setValue</code> attribute to indicate it is the method to override
 
* The content of the attribute is a complete function definition of the method, including <code>function ()</code>
 
* You can access the widget by <code>this</code> in the function
 
* You can access the original method by <code>this.$xxx</code>, where xxx is the method name being overridden. If the method doesn't exist, it is null.
 
* To retrieve another widget, use <code>this.$f('anotherWidgetId')</code> or other methods as described in the previous section
 
* You can specify EL expressions<ref>EL expressions are allowed since ZK 5.0.2</ref> in the content of the attribute, such as
 
 
 
<source lang="javascript">
 
w:setValue='function (value) { this.$setValue(value + "${whatever}")}';
 
</source>
 
 
 
Notice EL expressions are evaluated at the server before sent back to the client. Thus, you could any Java class or variables in EL expressions.
 
 
 
<blockquote>
 
----
 
<references/>
 
</blockquote>
 
 
 
=Version History=
 
Last Update : {{REVISIONYEAR}}/{{REVISIONMONTH}}/{{REVISIONDAY}}
 
{| border='1px' | width="100%"
 
 
! Version !! Date !! Content
 
! Version !! Date !! Content
 
|-
 
|-

Revision as of 07:37, 8 July 2022


Client-side UI Composing


Though optional, you could have the total control of the client's functionality without the assistance of server-side coding. Generally, you don't need to do it. You don't even need to know how ZK Client Engine and client-side widgets communicate with the server. Their states can be synchronized automatically with ZK. However, you can still control this type of synchronization if you want. It is the so-called Server-client fusion.

A good rule of thumb is that you should handle events and manipulate UI mostly, if not all, on the server, since it is more productive. Then, you could improve the responsiveness and visual effects, and/or reduce server loading by handling them at the client, when it is appropriate. Notice that JavaScript is readable by any user, so be careful not to expose sensitive data or business logic when migrating some code from server to client.

Version History

Version Date Content
     



Last Update : 2022/07/08

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