Groups Model"
Line 79: | Line 79: | ||
=Example: Grouping Tabular Data= | =Example: Grouping Tabular Data= | ||
− | Suppose you have the data in a two-dimensional array (see below), and you want to allow the user to group them based on a field selected by the user (such as food's name or food's calories) | + | Suppose you have the data in a two-dimensional array (see below), and you want to allow the user to group them based on a field selected by the user (such as food's name or food's calories). |
<source lang="java"> | <source lang="java"> | ||
//Data | //Data | ||
− | + | Object[][] _foods = new Object[][] { | |
new Object[] { "Vegetables", "Asparagus", "Vitamin K", 115, 43}, | new Object[] { "Vegetables", "Asparagus", "Vitamin K", 115, 43}, | ||
new Object[] { "Vegetables", "Beets", "Folate", 33, 74}, | new Object[] { "Vegetables", "Beets", "Folate", 33, 74}, | ||
Line 112: | Line 112: | ||
}; | }; | ||
</source> | </source> | ||
+ | |||
+ | Then, we can make it a groups model by extending from <javadoc>org.zkoss.zul.GroupsModelArray</javadoc>: | ||
<source lang="java"> | <source lang="java"> | ||
Line 118: | Line 120: | ||
public class FoodGroupsModel extends GroupsModelArray { | public class FoodGroupsModel extends GroupsModelArray { | ||
public FoodGroupsModel(java.util.Comparator cmpr) { | public FoodGroupsModel(java.util.Comparator cmpr) { | ||
− | super(_foods, cmpr); | + | super(_foods, cmpr); //assume we |
+ | //cmpr is used to group | ||
} | } | ||
protected Object createGroupHead(Object[] groupdata, int index, int col) { | protected Object createGroupHead(Object[] groupdata, int index, int col) { | ||
return groupdata[0][col]; | return groupdata[0][col]; | ||
− | //groupdata | + | //groupdata is one of groups after GroupsModelArray groups _foods |
+ | ///here we pick the first element and use the col-th column as the group head | ||
} | } | ||
+ | private static Object[][] _foods = new Object[][] { | ||
+ | ...tabular data as shown above | ||
+ | }; | ||
}; | }; | ||
</source> | </source> | ||
− | + | In additions, we have to implement a comparator to group the data based on the given column as follows. | |
<source lang="java"> | <source lang="java"> | ||
Line 147: | Line 154: | ||
</source> | </source> | ||
− | Since the data will be displayed in a multiple column, we have to implement a renderer | + | Since the data will be displayed in a multiple column, we have to implement a renderer. Here is an example. |
<source lang="java"> | <source lang="java"> | ||
Line 153: | Line 160: | ||
public void render(Row row, java.lang.Object obj) { | public void render(Row row, java.lang.Object obj) { | ||
if (row instanceof Group) { | if (row instanceof Group) { | ||
+ | //display the group head | ||
row.appendChild(new Label(data.toString())); | row.appendChild(new Label(data.toString())); | ||
} else { | } else { | ||
+ | //display an element | ||
Object[] data = (Object[]) obj; | Object[] data = (Object[]) obj; | ||
row.appendChild(new Label(data[0].toString())); | row.appendChild(new Label(data[0].toString())); | ||
Line 166: | Line 175: | ||
</source> | </source> | ||
− | |||
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. | ||
Revision as of 01:44, 31 December 2010
- Available for ZK:
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-level tree of data: a list of grouped data and each grouped data is a list of elements to display. Here is a live demo. 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 as following:
SimpleGroupsModel | GroupsModelArray | |
---|---|---|
Usage | The grouping is immutable, i.e., re-grouping is not allowed | Grouping is based on a comparator (java.util.Comparator) |
Constructor | The data must be grouped, i.e., data[0] is the first group, data[1] the second, etc. | The data is not grouped, i.e., data[0] is the first element. The constructor requires a comparator that will be used to group them. |
Version |
Since 3.5.0 |
Since 5.0.5; For 5.0.4 or prior, please use ArrayGroupsModel (the same). |
Example: Immutable Grouping Data
If your data is already grouped and the grouping won't be changed, then you could use SimpleGroupsModel as follows:
<zk>
<zscript>
String[][] datas = new String[][] {
new String[] { //group 1
// Today
"RE: Bandbox Autocomplete Problem",
"RE: It's not possible to navigate a listbox' ite",
"RE: FileUpload"
},
new String[] { //group 2
// Yesterday
"RE: Opening more than one new browser window",
"RE: SelectedItemConverter Question"
},
new String[] { //group 3
"RE: Times_Series Chart help",
"RE: SelectedItemConverter Question"
}
};
GroupsModel model = new SimpleGroupsModel(datas,
new String[]{"Date: Today", "Date: Yesterday", "Date: Last Week"});
//the 2nd argument is a list of group head
</zscript>
<grid model="${model}">
<columns sizable="true">
<column label="Subject"/>
</columns>
</grid>
</zk>
Then, the result
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:
- Sorting: GroupsModelArray sorts each group separately by using the specified comparator (java.util.Comparator).
- Re-grouping: GroupsModelArray re-groups by assuming two data belong to the same group if the compared result is the same (i.e., the given java.util.Comparator returns 0).
- For better control, you could implement GroupComparator, and pass an instance to, say, Column.setSortAscending(Comparator) and Column.setSortDescending(Comparator).
Example: Grouping Tabular Data
Suppose you have the data in a two-dimensional array (see below), and you want to allow the user to group them based on a field selected by the user (such as food's name or food's calories).
//Data
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 can make it a groups model by extending from GroupsModelArray:
//GroupsModel
package foo;
public class FoodGroupsModel extends GroupsModelArray {
public FoodGroupsModel(java.util.Comparator cmpr) {
super(_foods, cmpr); //assume we
//cmpr is used to group
}
protected Object createGroupHead(Object[] groupdata, int index, int col) {
return groupdata[0][col];
//groupdata is one of groups after GroupsModelArray groups _foods
///here we pick the first element and use the col-th column as the group head
}
private static Object[][] _foods = new Object[][] {
...tabular data as shown above
};
};
In additions, we have to implement a comparator to group the data based on 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. Here is an example.
public class FoodGroupRenderer implements RowRenderer {
public void render(Row row, java.lang.Object obj) {
if (row instanceof Group) {
//display the group head
row.appendChild(new Label(data.toString()));
} else {
//display an element
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()));
}
}
};
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.
Example: Grouping an Array of JavaBean
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
Version | Date | Content |
---|---|---|