It is common for an application to provide different themes for different users based on their preferences. However, the difference among themes is limited to the power of CSS. If the difference can not be done by CSS, an application has to adopt some UI glue logic and code to handle it. Fortunately, it is no longer a limit for ZK applications. By using the so-called mold or widget classes, a ZK application could provide a totally different look without modifying a single line of the application code.

For example, let us assume we’d like to place the first child of hbox in the first row and the rest in the second row as shown below.

A hbox that spans two rows

Adding a New DOM Structure with a Mold

To support this, we have to use a different DOM structure rather than customize CSS. For ZK, it means we have to introduce a new mold. For example,

function (out) {
  out.push('<table', this.domattrs_(),="" zutl.cellps0,="" '="">');
  var child = this.firstChild;
  if (child) //1st row
    child.redraw(out);
  out.push('');

  while (child = child.nextSibling) { //2nd row
    out.push('');
    child.redraw(out);
    out.push('');
  }
  out.push('');
}
</table',>
  • The code will be more complicated if we want to support all hbox features such as pack and align.
  • Refer to ZK Component Development Essentials for details of implementing a mold.

Adding a New Widget Class for an Additional Look

By writing a new mold, we introduce a new look. However, to have two or more looks coexist in the same application (and therefore selectable by users), we have to implement a new widget class to have the new mold, rather than override an old mold, or add a new mold to an existent widget class. For the sake of description, let us call the new widget class as foo.TwoRowHbox. For example,

foo.TwoRowHbox = zk.$extends(zul.box.Box, {
  //we can override any method we want (optional)
});
foo.TwoRowHbox.molds = {
  horizontal: function (out) {
    //put the mold function here, described in the previous section
  }
};

In additions, we have to specify an EL expression in a language addon (lang-addon.xml) that selects the correct widget class based on the preference. For example, if we store the name of the widget class in a session attribute called hbox.widget, we can specify the following in the lanuage addon.

  hbox
  hbox
  
  ${empty sessionScope["hbox.widget"] ? 'zul.box.Box': sessionScope["hbox.widget"]}
  

Then, the user will see a different hbox depending on the value of the session attribute.

Sample Code

Here is a sample code to select a different look dynamically.

	
	void setLook(String name) {
		if ("two-rows".equals(name)) {
			session.setAttribute("hbox.widget", "foo.TwoRowHbox");
			session.setAttribute("hbox.widget.name", "two-rows");
		} else { 
			session.setAttribute("hbox.widget", "zul.box.Box");
			session.setAttribute("hbox.widget.name", "default");
		}
	}

	if (session.getAttribute("hbox.widget.name") == null)
		setLook("default");
	

	Select a look:
	
		
		
	

	
		

More Information

  • The support of specifying EL expressions in the widget class is allowed since ZK 5.0.4.
  • If the JavaScript files (zk.wpd) are hosted at the Web application (rather than a JAR file), we have to specify org.zkoss.web.util.resource.dir in WEB-INF/zk.xml.
  • If the language addon is hosted at the Web application (rather than a JAR file), we have to specify language-addon in WEB-INF/zk.xml.
  • ZK Component Development Essentials
If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

Leave a Reply