Processing...
Description & Source Code

ZK Tabbox is an useful component. But sometimes the default behavior needs to be customized. This demo shows two ways to set up a "Plus Tab", and how to use client side widget overrides to improve the visual appearance - without changing the server side code.

The top example uses plain server-side event forwarding, to translate the onClick into a custom onTabAdd-event. The tabbox content switches temporarily to the empty content of the (fake) "Plus Tab", causing unwanted flickering of the tabbox content, before the actual content of the new tab is loaded from the server.

To avoid this flickering the client side behavior of the "Plus Tab" can be changed by using a client side widget override. This will prevent the default onClick-handling for the tab, and directly send the onTabAdd-event to the server. Like this the "Plus Tab" never gets selected effectively.

plus_tab.zul
<zk xmlns:w="client">
	<groupbox closable="false" sclass="z-demo-config" style="padding: 10px;">
		<caption label="Using server side Event-Forwarding (flickering)"/>
		<tabbox id="tabx" hflex="1" height="70px" apply="demo.client_side.plus_tab.PlusTabComposer">
			<!-- Server side custom event (onTabAdd) listener -->
			<tabs>
				<forEach items="${tabsModel}">
					<tab label="${each}"/>
				</forEach>
				<!-- Plus Tab - used to trigger onAddTab event -->
				<tab iconSclass="z-icon-plus" label=" " forward="onClick=tabx.onTabAdd"/>
			</tabs>
			<tabpanels>
				<forEach items="${tabsModel}">
					<tabpanel>${each} Content</tabpanel>
				</forEach>
			</tabpanels>
		</tabbox>
	</groupbox>

	<separator height="15px"/>

	<groupbox closable="false" sclass="z-demo-config" style="padding: 10px;">
		<caption label="With Client Override (no flickering)"/>
		<tabbox hflex="1" height="70px" apply="demo.client_side.plus_tab.PlusTabComposer">
			<tabs>
				<forEach items="${tabsModel}">
					<tab label="${each}"/>
				</forEach>
				<!-- Plus Tab - used to trigger onAddTab event -->
				<tab iconSclass="z-icon-plus" label=" ">
					<!-- Overwrite default client side click handing function 'doClick_' at widget level -->
					<attribute w:name="doClick_"><![CDATA[
						 function(event) {
						 	//don't call overridden function 'this.$doClick_' to prevent tab selection
						 	//instead fire the custom event to the tabbox component
						 	this.getTabbox().fire('onTabAdd', {}, {toServer: true});
						 }
					]]></attribute>
				</tab>
			</tabs>
			<tabpanels>
				<forEach items="${tabsModel}">
					<tabpanel>${each} Content</tabpanel>
				</forEach>
			</tabpanels>
		</tabbox>
	</groupbox>

</zk>
PlusTabComposer.java
package demo.client_side.plus_tab;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.metainfo.ComponentInfo;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zk.ui.util.ComposerExt;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Tabbox;

public class PlusTabComposer implements Composer<Tabbox>, ComposerExt<Tabbox> {

	private ListModelList tabsModel;
	private int counter = 0;

	@Override
	public void doBeforeComposeChildren(Tabbox tabbox) throws Exception {
		tabsModel = new ListModelList();
		tabsModel.add("Tab A");
		tabsModel.add("Tab B");
		tabsModel.add("Tab C");
		tabbox.setAttribute("tabsModel", tabsModel);
	}

	@Override
	public void doAfterCompose(Tabbox tabbox) throws Exception {
		tabbox.addEventListener("onTabAdd", event -> {
			counter++;
			tabsModel.add("New Tab " + counter);
			tabbox.setSelectedIndex(tabsModel.size() - 1);
		});
	}

	public ListModelList getTabsModel() {
		return tabsModel;
	}

	@Override
	public ComponentInfo doBeforeCompose(Page page, Component component, ComponentInfo componentInfo) throws Exception {
		return componentInfo;
	}

	@Override
	public boolean doCatch(Throwable throwable) throws Exception {
		return false;
	}

	@Override
	public void doFinally() throws Exception { }
}