ZK/How-Tos/Concepts and Tricks

= Concepts and Tricks =

Parameter and Event Listener
It is simple to access a parameter by use of Execution.getParameter or ${param[key]} in EL as follows.



However, you cannot access it in an event listener. For example, the button's label becomes empty once it is pressed in the following example.

 void doIt { self.label= Executions.getCurrent.getParameter("what"); }

Why?

The parameter map is available only when the page is evaluated. And, when user clicks the button, another URL is generated and the previous parameters was gone.

Solution:

 tmp = Executions.getCurrent.getParameter("what"); void doIt { self.label= tmp; }

Grid versus Listbox
Both grid and listbox support multiple columns and rows. They looks similar. Which one to use? also work combobox in listbox and grid.onopen are used to run time create database record in listbox and combobox;

Grid

 * Main use: table-like presentation of data.
 * Each cell could be dramatically different from each other
 * To implement selections, developers have to add a checkbox to each row. There is no easy way to implement single-selection (with radio button).

Listbox

 * Main use: selectable data
 * Selections are supported directly (by use of getSelectedItem and others)
 * If textbox is contained in a cell, users cannot select its content.

Why alert instead of an error box when access the value property in BeanShell
In the following code snippet, the error box is not shown. Rather, ZK considered it as programming error (by showing the exception with JavaScript's alert)

   

Why? BeanShell intercepts all exceptions and uses its own exception to represent them. Thus, ZK doesn't know it is actually caused by WrongValueException.

Solution: Use getValue and setValue instead of accessing the property name directly.

   

How to implement paging for listboxes with many items
If you have a listbox with many (say 1,000,000 items), you cannot use the internal paging mechanism of the listbox (in other words, you cannot set the 'paging' mold.) The reason is that the internal paging mechanism will create as many listitems as there are items.

A listbox that is controlled by a paginal will always display listitems starting from offset "pagesize * active page count" to "pagesize * (active page count + 1)". Consequently, you must *not* associate a listbox for which the number of listitems does not match the actual number of elements with a paginal. In other words, do not let the listbox be the controllee of the paging element. Instead, create a separate paging element like so:

Set the number of actual items as the "totalSize" of the paging controller:

paging.setTotalSize(1000000);

Now, you have update intercept the "onPaging" event:

paging.addEventListener("onPaging", new EventListener {   public void onEvent(Event e) {       PagingEvent pe = (PagingEvent)e;       int desiredPage = pe.getActivePage;       // now retrieve items desiredPage * pagesize .. (desiredPage+1) * pagesize       // and assign their content to listitem 0 .. pagesize    } });

Consequently, there are always only as many listitems as are currently displayed.

Note that a drawback of this technique is that you have to place the paging controller outside the listbox. You cannot add this paging controller to the listbox, since the listbox checks that at most one child that is an instance of org.zkoss.zul.Paging is added, lest it conflict with the internal paging component. A second drawback is that you cannot use the "box.getPageSize" methods, which rely on internal paging. You must use "paging.getPageSize" instead. Also, make sure you do *not* call box.setPaginal(paging) if you use this technique.

On Width and Percentages
Using the width= attribute with percentages in ZK requires careful attention because its meaning is different from other frameworks. On one hand, ZK strives to be a browser-independent framework. On the other hand, when rendering HTML, ZK often passes the width= attribute on to the HTML elements it uses, which leads to unexpected effects. The reason is that the HTML elements to which the width= attribute is being passed form a different hierarchy than the ZUL elements from which they originate.

To understand this, consider the following scenario:

 Left Right Here, the intention is for the parent hbox to occupy 100% of its parent (maybe a window), and for each 'div' child to occupy 50% of its parent. In CSS, [W3C], the width attribute refers to the containing block.

However, ZK renders the above roughly as follows:

Users familiar with CSS will note that the width:50% attribute now no longer refers to the 'table' element that was used to render the 'hbox', but to the 'td' element ZK inserted! The default width of the td element is 50% of the table (since there are two 'td' elements), hence the effective width of the 'div' element is 25% of the table. It is apparent that in order to use width= properly, one must understand how ZK translates between ZUL and HTML (contrary to the intentions of ZK's developers.)

To address this problem, ZK provides a "widths" attribute for box elements. The 'widths' attribute allows the assignment of 'width:' attributes to the 'td' elements that make up a box. To achieve the desired effect, you would have to use:

 Left Right which will be rendered to

Thus, the table takes up 100% of its parent, each 'td' elements takes up 50% of the table's width, and the 'div' elements occupying each 'td' take up the entire space (100%).

The 'widths' attribute provides a work-around for boxes, but, unfortunately, it does not provide a general solution. ''By passing on 'width=' attributes to HTML elements, ZK puts the rendering at the whim of the browser being used, therefore exposing the ZK user to all width-related rendering bugs. Moreover, these bugs are hard to debug because they require the ZK user to understand ZK's rendering process, before they can begin to address the browser peculiarities causing them.''

For instance, specifying a width of 100% to a will result in a style of "width:100%" applied to a element, which triggers bugs in different browsers - for instance, IE ignores the width restriction when the value placed in the textbox is larger than the box. In this case, IE stretches the box. Workaround

Change or customize the Toolbars in FCKeditor
Since March 13, 2007, the FCKeditor has supported the new method setCustomConfigurationsPath(String url) that you can specify the url of the custom configuration .js file. You do not need to modify the fckconfig.js inside the fckez.jar any more. Please see FCKeditor Configurations File for details on what can be customized.

So here is the new way to create a new toolbar set for the FCKeditor.

1. Create a JavaScript .js custom configuration file, say, myconfig.js under somewhere of your web path. For description convenience, this example would put it under root path; i.e. "/myconfig.js".

2. Create a new toolbar set "Simple" inside the file (myconfig.js). e.g. FCKConfig.ToolbarSets["Simple"] = [ ['Bold','Italic','Underline','StrikeThrough','-','TextColor','BGColor'] ]; 3. To use this /myconfig.js, you have to specify it in the tag  or by Java methods FCKeditor myFCKeditor = new FCKeditor; myFCKeditor.setCustomConfigurationsPath("/myconfig.js"); myFCKeditor.setToolbarSet("Simple"); 4. OK. You are done.

The following is the old way that needs you to modify the fckconfig.js inside the fckez.jar

To change a toolbar in FCKeditor or create a new one, the fckconfig.js file in the fckez.jar file will have to be modified.

1. make a backup copy of the fckez.jar file in case anything goes wrong.

2. rename the fckez.jar file to fckez.zip (jar files are just zip files, so this works)

3. unzip the fckez.zip file somewhere. A new directory is suggested.

4. find the fckconfig.js, and open it for editing. It should be at \web\js\ext\FCKeditor\fckconfig.js

5. find the toolbars section. The following is the Basic one. FCKConfig.ToolbarSets["Basic"] = [ ['Bold','Italic','-','OrderedList','UnorderedList','-','Link','Unlink','-','About'] ] ;

6. To create a new ToolbarSet, copy everything between the FCKConfig.ToolbarSets... to the semicolon ;

7. Paste it in following the semicolon, and change the name. FCKConfig.ToolbarSets["Simple"] = [ ['Bold','Italic','-','OrderedList','UnorderedList','-','Link','Unlink','-','About'] ] ;

8. Change the Toolset as you see fit. Buttons are defined in the single quotes. the '-' is a toolbar separator. Lets remove the link and unlink buttons. FCKConfig.ToolbarSets["Simple"] = [ ['Bold','Italic','-','OrderedList','UnorderedList','-','About'] ] ; 9. Save the file and exit the editor program.

10. navigate up the dir tree till you get to web folder level, and place the whole tree into the zip file. (this will preserve the location of the modified file in the dir structure.)

11. rename the zip file back to fckez.jar, and put the jar file into the tomcat shared/lib directory with the other zk jar files.

12. change your code that calls the FCKeditor in you zk application to use the 'Simple' toolbarset. FCKeditor myFCKeditor = new FCKeditor; myFCKeditor.setToolbarSet("Simple");

13. start Tomcat, and use a browser to view your application. You should see your modified toolbar set in FCKeditor.

How to open treeitem at start
Please try this with the newest version as a work-round. ... int[] path ={0}; tree.renderItemByPath(path); int[] path ={1}; tree.renderItemByPath(path); ... And, please refer to javadoc- treemodel for detail on path and please feel free to post further question to ZK forum on sourceforge

Due to some spec change, after 3.0.3, please refer the following: ... int[] path ={0.0}; tree.renderItemByPath(path); int[] path ={1.0}; tree.renderItemByPath(path); ... Here is a demo code to play around 

 <window title="Dynamically Change by Model"> <![CDATA[ class MySimpleTreeNode extends SimpleTreeNode { private String myData = null; public MySimpleTreeNode(String data, List children) { super(data, children); myData = data.toString; }               public String toString { return "Node: " + myData; }               public void append(String data) { myData = myData + data; }               public Object getData { return myData; }       }       List aChildren = new ArrayList; List empty = new ArrayList; List a2Children = new ArrayList; MySimpleTreeNode a20 = new MySimpleTreeNode("A2-0", empty); MySimpleTreeNode a21 = new MySimpleTreeNode("A2-1", empty); MySimpleTreeNode a22 = new MySimpleTreeNode("A2-2", empty); a2Children.add(a20); a2Children.add(a21); a2Children.add(a22); MySimpleTreeNode a0 = new MySimpleTreeNode("A0", empty); MySimpleTreeNode a1 = new MySimpleTreeNode("A1", empty); MySimpleTreeNode a2 = new MySimpleTreeNode("A2", a2Children); aChildren.add(a0); aChildren.add(a1); aChildren.add(a2); List children = new ArrayList; MySimpleTreeNode a = new MySimpleTreeNode("A", aChildren); children.add(a); List bbChildren = new ArrayList; MySimpleTreeNode b00 = new MySimpleTreeNode("B0-0", empty); bbChildren.add(b00); List bChildren = new ArrayList; MySimpleTreeNode b0 = new MySimpleTreeNode("B0", bbChildren); MySimpleTreeNode b1 = new MySimpleTreeNode("B1", empty); MySimpleTreeNode b2 = new MySimpleTreeNode("B2", empty); bChildren.add(b0); bChildren.add(b1); bChildren.add(b2); MySimpleTreeNode b = new MySimpleTreeNode("B", bChildren); children.add(b); List rList = new ArrayList; rList.add(a); rList.add(b); MySimpleTreeNode r = new MySimpleTreeNode("Root", rList); List rootList = new ArrayList; rootList.add(r); MySimpleTreeNode root = new MySimpleTreeNode("Root", rootList); SimpleTreeModel stm = new SimpleTreeModel(root); public void renderByPath(Object obj){ int[] result = stm.getPath(root,obj); for(int i =0; i < result.length;i++) {      			System.out.println(result[i]); }      		tree.renderItemByPath(result); }      public void renderByPathMul{ int l = tree.getTreechildren.getChildren.size; System.out.println(l); for(int i=0; i<l; i++) { int[] path ={0,i}; tree.renderItemByPath(path); }      }        ]]>             <tree model="${stm}" id="tree" width="700PX"> <button label='renderByPath A2' onClick='renderByPath(a2)' /> <button label='renderByPath B0-0' onClick='renderByPath(b00)' /> <button label='renderByPath A2-1' onClick='renderByPath(a21)' /> <button label='renderByPath Root' onClick='renderByPath(r)' /> <button label='renderByPath A' onClick='renderByPath(a)' /> </zk>

How do I programmatically create a Button that has an onClick zscript?
This piece of code does not work with ZK 3.5.1!!!

Click the "Try Me!" button on the demo page to see the source panel. Cut-n-paste in the following example then lick "Try Me!" again to load the example. Then click on the button to see the code in action.

<window title="My First Window" border="normal" width="200px"> <vbox id="myVbox"> <button label="create new button"> <attribute name="onClick"> <![CDATA[ deleteButton = new Button("Click me!"); // Store an arbitary piece of data in my button (e.g. some data primary key of the some object to delete) deleteButton.setAttribute("myDeleteButtonAttribute", "mary had a little lamb"); cond = new org.zkoss.zk.ui.util.Condition{ boolean 	isEffective(Component comp) {return true;} boolean 	isEffective(Page page){return true;} }; // here the script just does a serverside alert showing the silly text but you could call a // business delegate function to delete something from the database. zscript = new org.zkoss.zk.ui.metainfo.ZScript("java", "alert(self.getAttribute(\"myDeleteButtonAttribute\"));", cond); eventHandler = new org.zkoss.zk.ui.metainfo.EventHandler( zscript, cond); deleteButton.addEventHandler("onClick", eventHandler); myVbox.appendChild(deleteButton); ]]>

How do I use JavaScript in zscripts?
It is important to note that the event handlers and functions that you write with zscript run on the server not in the browser. There is a feature called "client side action" where you can have the browser run some JavaScript function in parallel to your server-side event handlers. Client-side actions are well suited for graphical effects in the browser. JavaScript client actions running in the browser do not have access to your server-side business objects, the whole Java JDK, and the server-side ZK Desktop containing rich XUL objects. Server-side zscript does have direct access to all of these things and you can use any of a number of languages including JavaScript.

This code shows how you can opt to use JavaScript rather than interpreted Java (BeanShell) in your server-side event zscripts handlers:

<?page zscript-language="javascript"?> // this is a JavaScript function that creates a JavaScript object literal // on the server and runs the server-side alert function function hello{ var now = new java.util.Date; // you can script pure Java objects! var myMessage = {msg:'hello world '+now}; // pure JavaScript alert(myMessage.msg); // JavaScript calling a Java method // Rhino = JavaScript on Java (http://www.mozilla.org/rhino/doc.html) }	<button label="Say hello world"> <attribute name="onClick"> hello;

You can run that example on the demo page of the zkoss.org site. Simply press the "Try Me!" button to show the editor, paste in the example, then press "Try Me!" again to load the example and press the "Say hello world" button to see it work.

What you cannot expect to do is write something like window.setTimeout("alert('hello');", 1000);  in that serverside JavaScript function. Why? Because 'window' is only an implicit object reference in the browser's JavaScript runtime. As the event handler is running on the server you do not have direct access to objects in the browser. There is no implicit object reference called 'window' in server-side ZK. We could have given the xul window tag and id of "window" and then we could script it. Cut and paste the following example into a "Try Me!" code editor of the zkoss.org demo then click "Try Me!" again to load it:

<?page zscript-language="javascript"?> <window id="window" title="This is my window title"> // this is a JavaScript function that manipulates the // desktop object that is assigned the ID "window" function changeWindowTitle(title){ window.title = title; }       <label value="New title:"/> <textbox id="new_title" value="edit me..."/> <button label="Change the window title."> <attribute name="onClick"> changeWindowTitle(new_title.value);

There we gave the window object an ID of 'window' so that we can refer to it in a zscript by that name and change its title. We could still not call "window.setTimeout" as there is no method called setTimeout on a ZK Window Component. The JavaDoc on the zkoss site to see what methods Window and other Components have. We implicitly used setTitle in that last example when we set a new value to window.title in the zscript.

One browser method that people use all the time is: document.getElementById('some_id');  That is not going to work as 'document' is an implicit object in the browser and it does not exist in server-side ZK. Do not dispare though! ZK has 86 different types of XHTML objects in addition to the 76 XUL object types. It will build up a mixed XHTML and XUL "DOM" on the server known as the Desktop. The following example manipulates some XHTML with a JavaScript zscript:

<?page zscript-language="javascript"?> <zk xmlns:h="http://www.w3.org/1999/xhtml"> function changeParagraph{ // this script code changes the xhtml objects on the server myParagraph.children.get(1).value = 'goodbye world'; }       <h:p id="myParagraph"> <h:img src="http://en.wikibooks.org/skins-1.5/common/images/button_bold.png"></h:img> hello world </h:p>

<button label="Change hello to goodbye"> <attribute name="onClick"> changeParagraph; </zk>

You cannot change the src attribute of the XHTML IMG element. Instead use the ZUL IMAGE element. Likewise if you want to set the source on an IFRAME use the ZUL element not an XHTML element. Why? Because then when you change their state on the server in zscripts after directly accessing your business logic ZK will automatically update the corresponding DOM elements in the browser. The ZUL elements are documented here and the ZHTML elements documented here.

Pass JavaScript variable value to ZK Server
Sometimes we need to write some browser side JavaScript function to do something. We can use CSA (Client Side Action) to trigger the operation without problem. However, after the JavaScript execution completes, we might need the value from the JavaScript variable to be accessed from within a zscript function (the code running at server side). Here is a trick to do it. Basically, we prepare a "proxy" textbox. Copy the JavaScript value to textbox's value and fake an "onChange" event to really send the value back to Server. Following is the example codes:

<zk> <script type="text/JavaScript"> <![CDATA[ function test(tbxsss) { var sssval = "Time: "+(new Date); tbxsss.value = sssval; if (document.createEvent) { var evt = document.createEvent('HTMLEvents'); evt.initEvent( 'blur', false, false); tbxsss.dispatchEvent(evt); var evt2 = document.createEvent('HTMLEvents'); evt2.initEvent( 'change', false, false); tbxsss.dispatchEvent(evt2); } else if (document.createEventObject) { tbxsss.fireEvent('onblur'); tbxsss.fireEvent('onchange'); }     }    ]]>   <window id="win" title="My First Window" border="normal" width="200px"> <textbox id="sss" value="test" onChange="alert(self.getValue);" visible="false"/> <button id="btn" label="Invoke JS" action="onclick:test(#{sss})"/> </zk>

When you click the button, the JavaScript function test is executed. It then copys the new String "abc" into the textbox's value and fake onblur and onchange events. This will cause the ZK JavaScript engine to pass the new value "abc" back to server side, update the Textbox object's value property, and fire the "onChange" ZK event. And now you can access the value from this "proxy" textbox. And if you read the example code carefully, you should notice that the textbox is "invisible" on the browser (visible = "false") so it will not interfere your layout. Have fun!

Use 'self' In Event Handlers Within 'forEach'
It is very useful to use forEach to dynamically build a user interface. If you have an event handler within a forEach you can use 'self' to refer to the component that the event happened upon. You can also use 'self' to walk through the component scope starting at the component that the event happened upon. <![CDATA[ /**        * in a real application we would use something like *        List iterateOverMe = sessionScope.get("searchResults"); */       String[][] iterateOverMe = { {"Fred","Flintstone"} ,{"Wilma","Flintstone"} ,{"Barney","Rubble"} ,{"Betty","Rubble"} };       void doRespondToClick{ String message = "I am "+self.id; List people = self.parent.children; for( Iterator i = people.iterator; i.hasNext; ){ Object person = i.next; if( person.id != self.id ) message += "\nI am next to "+person.id; }        alert(message); } ]]> <button forEach="${iterateOverMe}" id="${each[0]} ${each[1]}" label="Click Me ${each[0]} ${each[1]}" onClick="doRespondToClick"/>

IE Overflow Scrolling of Relative Positioned DIV
The overflow bug is documented well and exists in IE6 or IE7 as well. Thus, in this case we have to specify "position:relative" to the outer tag. For example, <window title="tree demo" border="normal"> <div style="overflow:auto;position:relative" width="300px" height="100px"> <tree id="tree" width="90%" rows="5"> <treecol label="Name" /> <treecol label="Description" /> <treecell label="Item 1" /> <treecell label="Item 1 description" /> <treecell label="Item 2" /> <treecell label="Item 2 description" /> <treecell label="Item 2.1" /> <treecell label="Item 2.1.1" /> <treecell label="Item 2.1.2" /> <treecell label="Item 2.2" /> <treecell label="Item 2.2 is something who cares" /> <treeitem label="Item 3" />

How to Keep The Current Focused Component
We need to catch the onFocus events to book keeping who is getting the focus right now. For example, <zk> Component current; void changeFocus(Component t){ current = t; } <window title="My First Window" border="normal" width="300px"> Window 1 : <textbox id="First" onFocus="changeFocus(self)"/> <window title="My Second Window" border="normal" width="300px"> Window 2 : <textbox id="Second" onFocus="changeFocus(self)"/> <window title="My Third Window" border="normal" width="300px"> Window 3 : <textbox id="Third" onFocus="changeFocus(self)"/> <window title="My Fourth Window" border="normal" width="300px"> Window 4 : <textbox id="Fourth" onFocus="changeFocus(self)"/> <button label="Show" onClick="alert(current.id)"/> </zk>

How to Disable The Progress Bar of ZK
Add the following code into your zul page. <script type="text/javascript"><![CDATA[ window.Boot_progressbox = function {} ]]>

How to access static member field of a class in zul without zscript
The EL of ZUL support tag lib, so we can write a util tag lib function to access static member field from zul.(or any other static function)

Note: ZK 3.0 introduced the xel-method directive to declare a static method directly in a ZUL page (without TLD).

<?xel-method prefix="c" name="forName" class="java.lang.Class" signature="java.lang.Class forName(java.lang.String)"?>

Use TLD as described below if you want to manage the declarations in a single TLD file (rather than scattering around ZUL files), or you want to be compatible with ZK 2.x.

step1. write a tag lib, I place this field in src/metainfo/tld/myutil.tld

<?xml version="1.0" encoding="ISO-8859-1" ?> http://mytld/util sf            <function-class>mytld.Util</function-class> <function-signature> java.lang.String getStaticField(java.lang.String name) </function-signature>

step2. write a tag lib configuration, this file must be placed in src/metainfo/tld/config.xml

<?xml version="1.0" encoding="UTF-8"?> <version-class>org.zkoss.zk.Version</version-class> <version-uid>3.0.1</version-uid> <taglib-uri>http://mytld/util</taglib-uri> <taglib-location>/metainfo/tld/myutil.tld</taglib-location>

step3. taglib implementation

public class Util { static public String getStaticField(String name){ try{ int i = name.lastIndexOf("."); String field = name.substring(i+1,name.length); name = name.substring(0,i); Class clz = Class.forName(name); Object obj = clz.getField(field).get(null); if(obj!=null){ return obj.toString; }               return null; }catch(Exception x){ throw new RuntimeException(x); }       }    }

step4. access taglib in the zul.(sf is function name declared in step1

<?taglib uri="http://mytld/util" prefix="t" ?> <window id="${t:sf('mytld.CompIds.ID1')}" title="title"> <button label="click" onClick="alert(self.parent.id)"/>

How to detect Firebug from browser
Firebug is an accepted cause of reducing performance regarding Javascript. This is a way to detect whether user enables the Firebug. Here is a Javascript code. if(window.console && window.console.firebug){ // Do something Javascript code. }

How to resolve the issue of CSS not loaded in IE6&7 while integrating ZK and JSP.
As so far as I know, the ZK CSS file fails to load in JSP on IE6&7, so you must add the following page definition to your JSP file.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ... //Your JSP content ...

How to pass Arguments to a Macro Component.
If you have a macro component and you are using that macro in a window, the following shows the exact syntax on how to pass arguments (which is in the developers guide but there isn't an actual example) to the macro and use them inside the macro: Here is the macro component :

<button id="pmminusButton"/> <progressmeter width="${arg.width}" value="${arg.initialValue}" id="dhProgressMeter" /> <button id="pmplusButton"/>

And here is the include statement at the top of my window file where I specify the arguments to get passed :

<?component name="dhProgressMeter" inline="true" macro-uri="/macros/dhProgressMeter.zul" width="90px" initialValue="20" ?>

The important items here are that I am declaring 2 variables to be passed to the macro component, 'width' and 'initialValue'. In the macro itself you can see that I obtain the value of these arguments using ${arg.width} and ${arg.initialValue} This is straightforward and I guess could have gone into a how-to but I thought I would put it in the forum so if someone does a search for "how to pass arguments to a macro" it will come up.