Theme:
Description & Source Code
  • Description
  • XML & Java Source
    hierarchy.zul
    season.zul
    month.zul
    HierarchyController.java
    Stock.java
    StockModel.java
    FormatElNumber.java
    Quarter.java
    Month.java
    Style.css

ZK allows components to contain other components to form a hierarchy of components where the inner components can be loaded only when they are needed this is done using ZK's detail tag and the fulfill attribute set to the event "onOpen"

<?xel-method prefix="c" name="formatStock" class="demo.grid.hierarchy.FormatElNumber"
   signature="java.lang.String formatStock(double)"?>
<zk>
	<style src="/widgets/grid/hierarchy/Style.css" />
	<div apply="demo.grid.hierarchy.HierarchyController">
		<grid id="hGrid" model="${$composer.stocks }">
			<columns>
				<column width="40px" />
				<column label="Stock Name" sort="auto(name)" />
				<column label="Avg. High" align="center"
					sort="auto(averageHigh)" />
				<column label="Avg. Low" align="center"
					sort="auto(averageLow)" />
				<column label="Avg. Volume" align="center"
					sort="auto(averageVolume)" />
			</columns>
			<template name="model">
				<row>
					<custom-attributes quarters="${each.quarters }" stock="${each }" />
					<detail open="false" fulfill="onOpen">
						<include 
							src="/widgets/grid/hierarchy/season.zul" stock="${stock }"
							quarters="${quarters}" />
					</detail>
					<label value="${each.name }" />
					<label style="color:red;"
						value="${c:formatStock(each.averageHigh)}" />
					<label style="color:green;"
						value="${c:formatStock(each.averageLow)}" />
					<label style="font-weight:bold;"
						value="${c:formatStock(each.averageVolume)}" />
				</row>
			</template>

		</grid>
	</div>
</zk>

<zk>
	<grid model="${arg.quarters}" sclass="inner-grid">
		<columns>
			<column width="40px" />
			<column label="Season" />
			<column label="Avg. High" align="center"
				sort="auto(averageHigh)" />
			<column label="Avg. Low" align="center"
				sort="auto(averageLow)" />
			<column label="Avg. Volume" align="center"
				sort="auto(averageVolume)" />
		</columns>
		<template name="model">
			<row value="${forEachStatus.index}">
				<custom-attributes quarter="${each }"
					months="${each.months}" />
				<detail open="false" fulfill="onOpen">
					<include mode="instant"
						src="/widgets/grid/hierarchy/month.zul" quarter="${quarter }"
						months="${months }" />
				</detail>
				<label value="Q${each.quarter}" />
				<label value="${c:formatStock(each.averageHigh)}" />
				<label value="${c:formatStock(each.averageLow)}" />
				<label value="${c:formatStock(each.averageVolume)}" />
			</row>
		</template>
		<foot>
			<footer width="40px" />
			<footer label="Avg:" />
			<footer label="${c:formatStock(arg.stock.averageHigh)}" />
			<footer label="${c:formatStock(arg.stock.averageLow)}" />
			<footer label="${c:formatStock(arg.stock.averageVolume)}" />
		</foot>
	</grid>
</zk>
<zk>
	<grid model="${arg.months}">
		<columns>
			<column label="Month" />
			<column label="High" align="center"
				sort="auto(high)" />
			<column label="Low" align="center"
				sort="auto(low)" />
			<column label="Volume" align="center"
				sort="auto(volume)" />
		</columns>
		<template name="model">
			<row sclass="last">
				<label value="${each.month}" />
				<label value="${c:formatStock(each.high)}" />
				<label value="${c:formatStock(each.low)}" />
				<label value="${c:formatStock(each.volume)}" />
			</row>
		</template>
	</grid>
	<chart type="line" width="700px" height="200px" bgColor="#fafafa"
		model="${arg.quarter.chartModel }" />
</zk>
package demo.grid.hierarchy;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;

public class HierarchyController extends SelectorComposer<Component> {
	/**
	 * 
	 */
	private static final long serialVersionUID = -3325569917490393900L;
	
	final StockModel stockData = new StockModel();
	

	public ListModel<Stock> getStocks() {
		return new ListModelList<Stock>(stockData.getStocks());
	}
}
package demo.grid.hierarchy;

import java.util.List;

import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;

public class Stock {
	private String name;
	private List<Quarter> quarters;

	public Stock(String name, double high, double low,
			double volume, List<Quarter> quarters) {
		this.name = name;
		this.quarters = quarters;
	}

	public String getName() {
		return name;
	}

	public double getAverageHigh() {
		double total = 0;

		for (Quarter quarter : quarters) {
			total += quarter.getAverageHigh();
		}

		return total / quarters.size();
	}

	public double getAverageLow() {
		double total = 0;

		for (Quarter quarter : quarters) {
			total += quarter.getAverageLow();
		}

		return total / quarters.size();
	}

	public double getAverageVolume() {
		double total = 0;

		for (Quarter quarter : quarters) {
			total += quarter.getAverageVolume();
		}

		return total / quarters.size();
	}

	public ListModel<Quarter> getQuarters() {
		return new ListModelList<Quarter>(quarters);
	}

}
package demo.grid.hierarchy;

import java.util.ArrayList;
import java.util.List;

public class StockModel {
	
	private List<Stock> stock = new ArrayList<Stock>() {
		/**
		 * 
		 */
		private static final long serialVersionUID = 4154191626685852617L;

		{
			add(generateStock("Csco"));
			add(generateStock("Goog"));
			add(generateStock("Yhoo"));
			add(generateStock("Msft"));
			add(generateStock("Orcl"));
			add(generateStock("Amaz"));
			add(generateStock("Fabc"));
		}
	};
	

	public List<Stock> getStocks() {
		return stock;
	}
	
	//Generation routines start here
	public static Stock generateStock(String stockName) {
		return new Stock(stockName, getValue(), getValue(), getVolume(), generateQuarters());
	}
	
	public static List<Quarter> generateQuarters() {
		List<Quarter> quarters = new ArrayList<Quarter>();
		//generate each quarter
		for(int i =0; i<4; i++) {
			quarters.add(generateQuarter(i * 3));
		}
		
		return quarters;
	}
	
	private static Quarter generateQuarter(int start) {
		List<Month> months = new ArrayList<Month>();
		
		for(int i=0;i<3;i++) {
			months.add(new Month(start + i, getValue(), getValue(), getVolume()));
		}
		
		return new Quarter((start / 3) + 1, months);
	}
	
	private static double getValue() {
		return Math.random() * 50 + 40;
	}
	
	private static double getVolume() {
		return Math.random() * 50000 + 65536;
	}
}
package demo.grid.hierarchy;

import java.text.NumberFormat;

public class FormatElNumber {
	public static String formatStock(double stock) {
		final NumberFormat nf = NumberFormat.getInstance();
		nf.setMaximumFractionDigits(2);
		return nf.format(stock);
	}
}
package demo.grid.hierarchy;

import java.util.List;

import org.zkoss.zul.CategoryModel;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.SimpleCategoryModel;

public class Quarter {

	private List<Month> months;
	private int quarter;
	
	public Quarter(int quarter, List<Month> months) {
		this.months = months;
		this.quarter = quarter;
	}

	public ListModel<Month> getMonths() {
		return new ListModelList<Month>(this.months);
	}
	
	public int getQuarter() {
		return quarter;
	}

	public double getAverageHigh() {
		double total = 0;

		for (Month month : months) {
			total += month.getHigh();
		}

		return total / months.size();
	}

	public double getAverageLow() {
		double total = 0;

		for (Month month : months) {
			total += month.getLow();
		}

		return total / months.size();
	}

	public double getAverageVolume() {
		double total = 0;

		for (Month month : months) {
			total += month.getVolume();
		}

		return total / months.size();
	}

	public CategoryModel getChartModel() {
		
		CategoryModel categoryModel = new SimpleCategoryModel();
		
		for(Month month : months) {
			categoryModel.setValue("Performance", month.getMonth(), month.getVolume());
		}
		
		return categoryModel;
		
	}
}
package demo.grid.hierarchy;

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class Month {
	private final double high, low, volume;
	private final Calendar month = Calendar.getInstance();
	private final SimpleDateFormat monthFormat = new SimpleDateFormat("MMMM");
	private final String stringMonth;

	public Month(int month, double high, double low, double volume) {
		this.high = high;
		this.low = low;
		this.volume = volume;
		this.month.set(Calendar.MONTH, month);
		this.stringMonth = monthFormat.format(this.month.getTime());
	}

	public double getHigh() {
		return high;
	}

	public double getLow() {
		return low;
	}

	public double getVolume() {
		return volume;
	}

	public String getMonth() {
		return stringMonth;
	}
}
tr.z-row .z-detail-outer,tr.z-row .z-row-inner {
	background: none;
}

tr.z-row td.z-row-inner {
	border-left-width: 0;
	border-right-width: 0;
	border-color: gray;
}

.z-column .z-column-cnt {
	padding: 2px;
	font-weight: bold;
}

tr.z-row.last td:first-child span {
	font-weight: bold;
	color: #06488E;
}

tr.z-row td.z-row-inner {
	border: 0;
}

.sapphire .inner-grid tr.z-row .z-row-cnt {
	color: black;
}