Groups Model"

From Documentation
Line 9: Line 9:
 
Instead of implementing <javadoc type="interface">org.zkoss.zul.GroupsModel</javadoc>, it is suggested to extend from <javadoc>org.zkoss.zul.AbstractGroupsModel</javadoc>, or to use one of the default implementations: <javadoc>org.zkoss.zul.GroupsModelArray</javadoc> and <javadoc>org.zkoss.zul.SimpleGroupsModel</javadoc>. <javadoc>org.zkoss.zul.GroupsModelArray</javadoc> supports regrouping, while <javadoc>org.zkoss.zul.SimpleGroupsModel</javadoc> is simpler but no regrouping is allowed (i.e., immutable).
 
Instead of implementing <javadoc type="interface">org.zkoss.zul.GroupsModel</javadoc>, it is suggested to extend from <javadoc>org.zkoss.zul.AbstractGroupsModel</javadoc>, or to use one of the default implementations: <javadoc>org.zkoss.zul.GroupsModelArray</javadoc> and <javadoc>org.zkoss.zul.SimpleGroupsModel</javadoc>. <javadoc>org.zkoss.zul.GroupsModelArray</javadoc> supports regrouping, while <javadoc>org.zkoss.zul.SimpleGroupsModel</javadoc> is simpler but no regrouping is allowed (i.e., immutable).
  
 +
= An Array of Data Example =
 
For example, suppose you have a two-dimensional array, then you could use <javadoc>org.zkoss.zul.SimpleGroupsModel</javadoc> as follows (simple but not regrouping):
 
For example, suppose you have a two-dimensional array, then you could use <javadoc>org.zkoss.zul.SimpleGroupsModel</javadoc> as follows (simple but not regrouping):
  
Line 58: Line 59:
 
[[Image:Grouping_model_explain.png]]
 
[[Image:Grouping_model_explain.png]]
  
For example, suppose you have the data as follows, and you want to allow the user to group them by a given field (such as food's name or food's calories):
+
For example, suppose you have the data in a two-dimensional array (see below), and you want to allow the user to group them by a given field (such as food's name or food's calories). Then, we can make it a groups model by extending from <javadoc>org.zkoss.zul.GroupsModelArray</javadoc> (since we want to support re-grouping):
  
''under construction''
+
<source lang="java">
 +
//GroupsModel
 +
package foo;
 +
public class FoodGroupsModel extends GroupsModelArray {
 +
    public FoodGroupsModel(java.util.Comparator cmpr) {
 +
        super(_foods, cmpr);
 +
    }
 +
    protected Object createGroupHead(Object[] groupdata, int index, int col) {
 +
        return groupdata[0][col];
 +
        //groupdata will _foods[n], and we retrieve the first element
 +
    }
 +
    private static Object[][] _foods = new Object[][] {
 +
        new Object[] { "Vegetables", "Asparagus", "Vitamin K", 115, 43},
 +
        new Object[] { "Vegetables", "Beets", "Folate", 33, 74},
 +
        new Object[] { "Vegetables", "Bell peppers", "Vitamin C", 291, 24},
 +
        new Object[] { "Vegetables", "Cauliflower", "Vitamin C", 92, 28},
 +
        new Object[] { "Vegetables", "Eggplant", "Dietary Fiber", 10, 27},
 +
        new Object[] { "Vegetables", "Onions", "Chromium", 21, 60},
 +
        new Object[] { "Vegetables", "Potatoes", "Vitamin C", 26, 132},
 +
        new Object[] { "Vegetables", "Spinach", "Vitamin K", 1110, 41},
 +
        new Object[] { "Vegetables", "Tomatoes", "Vitamin C", 57, 37},
 +
        new Object[] { "Seafood", "Salmon", "Tryptophan", 103, 261},
 +
        new Object[] { "Seafood", "Shrimp", "Tryptophan", 103, 112},
 +
        new Object[] { "Seafood", "Scallops", "Tryptophan", 81, 151},
 +
        new Object[] { "Seafood", "Cod", "Tryptophan", 90, 119},
 +
        new Object[] { "Fruits", "Apples", "Manganese", 33, 61},
 +
        new Object[] { "Fruits", "Cantaloupe", "Vitamin C", 112, 56},
 +
        new Object[] { "Fruits", "Grapes", "Manganese", 33, 61},
 +
        new Object[] { "Fruits", "Pineapple", "Manganese", 128, 75},
 +
        new Object[] { "Fruits", "Strawberries", "Vitamin C", 24, 48},
 +
        new Object[] { "Fruits", "Watermelon", "Vitamin C", 24, 48},
 +
        new Object[] { "Poultry & Lean Meats", "Beef, lean organic", "Tryptophan", 112, 240},
 +
        new Object[] { "Poultry & Lean Meats", "Lamb", "Tryptophan", 109, 229},
 +
        new Object[] { "Poultry & Lean Meats", "Chicken", "Tryptophan", 121, 223},
 +
        new Object[] { "Poultry & Lean Meats", "Venison ", "Protein", 69, 179},
 +
        new Object[] { "Grains", "Corn ", "Vatamin B1", 24, 177},
 +
        new Object[] { "Grains", "Oats ", "Manganese", 69, 147},
 +
        new Object[] { "Grains", "Barley ", "Dietary Fiber", 54, 270}
 +
    };
 +
};
 +
</source>
 +
 
 +
Then, we have to implement a comparator to compare the given column as follows.
 +
 
 +
<source lang>
 +
package foo;
 +
public class FoodComparator implements java.util.Comparator {
 +
int _col;
 +
boolean _asc;
 +
public FoodComparator(int col, boolean asc) {
 +
    _col = col; //which column to compare
 +
    _asc = asc; //ascending or descending
 +
}
 +
public int compare(Object o1, Object o2) {
 +
            Object[] data = (Object[]) o1;
 +
            Object[] data2 = (Object[]) o2;
 +
            int v = data[_col].compareTo(data2[_col]);
 +
return _asc ? v: -v;
 +
}
 +
}
 +
</source>
 +
 
 +
Since the data will be displayed in a multiple column, we have to implement a renderer as follows.
 +
 
 +
<source lang="java">
 +
public class FoodGroupRenderer implements RowRenderer {
 +
    public void render(Row row, java.lang.Object obj) {
 +
        if (row instanceof Group) {
 +
            row.appendChild(new Label(data.toString()));
 +
        } else {
 +
            Object[] data = (Object[]) obj;
 +
            row.appendChild(new Label(data[0].toString()));
 +
            row.appendChild(new Label(data[1].toString()));
 +
            row.appendChild(new Label(data[2].toString()));
 +
            row.appendChild(new Label(data[3].toString()));
 +
            row.appendChild(new Label(data[4].toString()));
 +
        }
 +
    }
 +
};
 +
</source>
  
 +
As shown, there are two cases: group and data of a group.
 
If it is not the behavior you want, you could override <javadoc method="sortGroupData(java.lang.Object, java.lang.Object[], java.util.Comparator, boolean, int)">org.zkoss.zul.GroupsModelArray</javadoc>. Of course, you could extend from <javadoc>org.zkoss.zul.AbstractGroupsModel</javadoc> to have total control.
 
If it is not the behavior you want, you could override <javadoc method="sortGroupData(java.lang.Object, java.lang.Object[], java.util.Comparator, boolean, int)">org.zkoss.zul.GroupsModelArray</javadoc>. Of course, you could extend from <javadoc>org.zkoss.zul.AbstractGroupsModel</javadoc> to have total control.
 +
 +
=An JavaBean Example =
  
 
=Group Foot =
 
=Group Foot =

Revision as of 12:24, 30 December 2010

  • Available for ZK:
  • http://www.zkoss.org/product/zkhttp://www.zkoss.org/whyzk/zkeeVersion pe-ee.png

Here we describe how to implement a groups model (GroupsModel). For the concept about component, model and render, please refer to the Model-driven Display section.

A groups model is used to drive components that support groups of data. The groups of data is a two dimensional data: a list of grouped data and each grouped data is a list of data to display. Here is an example. Currently, Listbox and Grid both support a list of grouped data.

Instead of implementing GroupsModel, it is suggested to extend from AbstractGroupsModel, or to use one of the default implementations: GroupsModelArray and SimpleGroupsModel. GroupsModelArray supports regrouping, while SimpleGroupsModel is simpler but no regrouping is allowed (i.e., immutable).

An Array of Data Example

For example, suppose you have a two-dimensional array, then you could use SimpleGroupsModel as follows (simple but not regrouping):

<zk>
	<zscript>
		String[][] datas = new String[][] {
			new String[] {
				// Today
				"RE: Bandbox Autocomplete Problem",
				"RE: It's not possible to navigate a listbox' ite",
				"RE: FileUpload"
			},
			new String[] {
				// Yesterday
				"RE: Opening more than one new browser window",
				"RE: SelectedItemConverter Question"
			},
			new String[] {
				"RE: Times_Series Chart help",
				"RE: SelectedItemConverter Question"
			}			
		};
		GroupsModel model = new SimpleGroupsModel(datas,
			new String[]{"Date: Today", "Date: Yesterday", "Date: Last Week"});
	</zscript>
	<grid model="${model}">
	 	<columns sizable="true">
	       <column label="Subject"/>
	     </columns>
	</grid>
</zk>

Then, the result

DrGroupsModel.png

Sorting and Regrouping

If your groups model allows the end user to sort and/or to re-group (i.e., grouping data based on different criteria), you have to implement GroupsModelExt too. Then, GroupsModelExt.group(Comparator, boolean, int) will be called if the user requests to sort the data based on particular column. And, GroupsModelExt.sort(Comparator, boolean, int) will be called if the user requests to re-group the data based on particular column.

GroupsModelArray support both sorting and re-grouping as described below:

Grouping model explain.png

For example, suppose you have the data in a two-dimensional array (see below), and you want to allow the user to group them by a given field (such as food's name or food's calories). Then, we can make it a groups model by extending from GroupsModelArray (since we want to support re-grouping):

//GroupsModel
package foo;
public class FoodGroupsModel extends GroupsModelArray {
    public FoodGroupsModel(java.util.Comparator cmpr) {
        super(_foods, cmpr);
    }
    protected Object createGroupHead(Object[] groupdata, int index, int col) {
        return groupdata[0][col];
        //groupdata will _foods[n], and we retrieve the first element
    }
    private static Object[][] _foods = new Object[][] {
        new Object[] { "Vegetables", "Asparagus", "Vitamin K", 115, 43},
        new Object[] { "Vegetables", "Beets", "Folate", 33, 74},
        new Object[] { "Vegetables", "Bell peppers", "Vitamin C", 291, 24},
        new Object[] { "Vegetables", "Cauliflower", "Vitamin C", 92, 28},
        new Object[] { "Vegetables", "Eggplant", "Dietary Fiber", 10, 27},
        new Object[] { "Vegetables", "Onions", "Chromium", 21, 60},
        new Object[] { "Vegetables", "Potatoes", "Vitamin C", 26, 132},
        new Object[] { "Vegetables", "Spinach", "Vitamin K", 1110, 41},
        new Object[] { "Vegetables", "Tomatoes", "Vitamin C", 57, 37},
        new Object[] { "Seafood", "Salmon", "Tryptophan", 103, 261},
        new Object[] { "Seafood", "Shrimp", "Tryptophan", 103, 112},
        new Object[] { "Seafood", "Scallops", "Tryptophan", 81, 151},
        new Object[] { "Seafood", "Cod", "Tryptophan", 90, 119},
        new Object[] { "Fruits", "Apples", "Manganese", 33, 61},
        new Object[] { "Fruits", "Cantaloupe", "Vitamin C", 112, 56},
        new Object[] { "Fruits", "Grapes", "Manganese", 33, 61},
        new Object[] { "Fruits", "Pineapple", "Manganese", 128, 75},
        new Object[] { "Fruits", "Strawberries", "Vitamin C", 24, 48},
        new Object[] { "Fruits", "Watermelon", "Vitamin C", 24, 48},
        new Object[] { "Poultry & Lean Meats", "Beef, lean organic", "Tryptophan", 112, 240},
        new Object[] { "Poultry & Lean Meats", "Lamb", "Tryptophan", 109, 229},
        new Object[] { "Poultry & Lean Meats", "Chicken", "Tryptophan", 121, 223},
        new Object[] { "Poultry & Lean Meats", "Venison ", "Protein", 69, 179},
        new Object[] { "Grains", "Corn ", "Vatamin B1", 24, 177},
        new Object[] { "Grains", "Oats ", "Manganese", 69, 147},
        new Object[] { "Grains", "Barley ", "Dietary Fiber", 54, 270}
    };
};

Then, we have to implement a comparator to compare the given column as follows.

package foo;
public class FoodComparator implements java.util.Comparator {
	int _col;
	boolean _asc;
	public FoodComparator(int col, boolean asc) {
	    _col = col; //which column to compare
	    _asc = asc; //ascending or descending
	}
	public int compare(Object o1, Object o2) {
            Object[] data = (Object[]) o1;
            Object[] data2 = (Object[]) o2;
            int v = data[_col].compareTo(data2[_col]);
		return _asc ? v: -v;
	}
}

Since the data will be displayed in a multiple column, we have to implement a renderer as follows.

public class FoodGroupRenderer implements RowRenderer {
    public void render(Row row, java.lang.Object obj) {
        if (row instanceof Group) {
            row.appendChild(new Label(data.toString()));
        } else {
            Object[] data = (Object[]) obj;
            row.appendChild(new Label(data[0].toString()));
            row.appendChild(new Label(data[1].toString()));
            row.appendChild(new Label(data[2].toString()));
            row.appendChild(new Label(data[3].toString()));
            row.appendChild(new Label(data[4].toString()));
        }
    }
};

As shown, there are two cases: group and data of a group. If it is not the behavior you want, you could override GroupsModelArray.sortGroupData(Object, Object[], Comparator, boolean, int). Of course, you could extend from AbstractGroupsModel to have total control.

An JavaBean Example

Group Foot

If the groups model supports a foot (such as a summary of all data in the same group), you could return an object to represent the footer when GroupsModel.getGroupfoot(int) is called (similar to GroupsModel.getGroup(int) shall return an object representing the group).

Version History

Last Update : 2010/12/30


Version Date Content
     



Last Update : 2010/12/30

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