Friday, December 10, 2010

GWT CellTable with sorting extended version

In my last article I was talking about how to create sortable cell table using the latest version of the Google Web Toolkit framework. In the last week I work to extended this example and make it more generic. I think the example now is also much more easier to understand and also gives you a much more easier possibility to add every data model to the table and make it sortable. Let’s see how the new example works.

The first big change is now I just have one class which I called CellTableSorting which extends the GWT CellTable. Into this extended class I add the all needed functionality. This is maybe not really the best practice to do this but work very well for me so fill free to use it as well if you like it.

The new class looks like this:

1 package com.mvp.client.ui;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.List;
7
8 import com.google.gwt.cell.client.Cell;
9 import com.google.gwt.cell.client.ValueUpdater;
10 import com.google.gwt.user.cellview.client.CellTable;
11 import com.google.gwt.user.cellview.client.Column;
12 import com.google.gwt.view.client.ListDataProvider;
13 import com.google.gwt.view.client.ProvidesKey;
14 import com.mvp.client.ui.CellTableViewImpl.GetValue;
15
16 /**
17 * Provide table with sorting
18 *
19 * @author L.Pelov
20 *
21 * @param <T>
22 * - custom object you want to expose to the table
23 */
24 public class CellTableSorting<T> extends CellTable<T> {
25
26 private final List<SortableHeader> allHeaders = new ArrayList<SortableHeader>();
27 private ListDataProvider<T> dataProvider = new ListDataProvider<T>();
28
29 public CellTableSorting(ProvidesKey<T> keyProvider) {
30 super(keyProvider);
31 }
32
33 /**
34 * Add a column of a {@link Comparable} type using default comparators.
35 *
36 * @param <C>
37 * the column type
38 * @param table
39 * the table
40 * @param text
41 * the header text
42 * @param cell
43 * the cell used to render values
44 * @param getter
45 * the {@link GetValue} used to retrieve cell values
46 * @return the new column
47 */
48 public <C extends Comparable<C>> Column<T, C> addColumn(final String text,
49 final Cell<C> cell, final GetValue<T, C> getter) {
50
51 return addColumn(text, cell, getter,
52 createColumnComparator(getter, false),
53 createColumnComparator(getter, true));
54 }
55
56 /**
57 * Add a sortable column to the table.
58 *
59 * @param <C>
60 * the data type for the column
61 * @param text
62 * the header text
63 * @param cell
64 * the cell used to render the column
65 * @param getter
66 * the getter to retrieve the value for the column
67 * @param property
68 * the property to sort by
69 * @param ascComparator
70 * ascendent comparator
71 * @param descComparator
72 * descendant comparator
73 * @return the column
74 */
75 private <C> Column<T, C> addColumn(final String text, final Cell<C> cell,
76 final GetValue<T, C> getter, final Comparator<T> ascComparator,
77 final Comparator<T> descComparator) {
78
79 // gets the cell value
80 final Column<T, C> column = new Column<T, C>(cell) {
81 @Override
82 public C getValue(T object) {
83 return getter.getValue(object);
84 }
85 };
86
87 final SortableHeader header = new SortableHeader(text);
88 allHeaders.add(header);
89
90 // call this every time headers is clicked
91 header.setUpdater(new ValueUpdater<String>() {
92 public void update(String value) {
93 header.setSorted(true);
94 header.toggleReverseSort();
95
96 for (SortableHeader otherHeader : allHeaders) {
97 if (otherHeader != header) {
98 otherHeader.setSorted(false);
99 otherHeader.setReverseSort(true);
100 }
101 }
102
103 // sort the clicked column
104 sortExpenses(dataProvider.getList(),
105 header.getReverseSort() ? descComparator
106 : ascComparator);
107
108 // cellTable.redrawHeaders();
109 redrawHeaders();
110
111 // Go to the first page of the newly-sorted results, if wished
112 // pager.firstPage();
113 }
114 });
115
116 addColumn(column, header);
117 return column;
118 }
119
120 /**
121 * Proceed with the sorting operation
122 *
123 * @param list
124 * data to sort
125 * @param comparator
126 * the {@link Comparator} used for the sorting
127 */
128 private void sortExpenses(List<T> list, final Comparator<T> comparator) {
129 Collections.sort(list, comparator);
130 }
131
132 /**
133 * Implements the comparator for our ContactInfo object.
134 *
135 * @param <C>
136 * the data type from the column
137 * @param getter
138 * the {@link GetValue} used to retrieve cell values
139 * @param descending
140 * way of sorting
141 * @return
142 */
143 private <C extends Comparable<C>> Comparator<T> createColumnComparator(
144 final GetValue<T, C> getter, final boolean descending) {
145
146 return new Comparator<T>() {
147 public int compare(T o1, T o2) {
148 // Null check the row object.
149 if (o1 == null && o2 == null) {
150 return 0;
151 } else if (o1 == null) {
152 return descending ? 1 : -1;
153 } else if (o2 == null) {
154 return descending ? -1 : 1;
155 }
156
157 // Compare the column value.
158 C c1 = getter.getValue(o1);
159 C c2 = getter.getValue(o2);
160 if (c1 == null && c2 == null) {
161 return 0;
162 } else if (c1 == null) {
163 return descending ? 1 : -1;
164 } else if (c2 == null) {
165 return descending ? -1 : 1;
166 }
167 int comparison = c1.compareTo(c2);
168 return descending ? -comparison : comparison;
169 }
170 };
171 }
172
173 public void setData(List<T> data) {
174
175 if (dataProvider.getDataDisplays() != null
176 && dataProvider.getDataDisplays().size() > 0) {
177 dataProvider.removeDataDisplay(this);
178 dataProvider.setList(data);
179
180 // cellTable.setRowData(0, items);
181 dataProvider.addDataDisplay(this);
182 } else {
183 dataProvider.setList(data);
184
185 // cellTable.setRowData(0, items);
186 dataProvider.addDataDisplay(this);
187 }
188 }
189
190 }
191

For my needs I do have only one constructor which provides the KeyProvider. Also the createColumnComparator function looks now completely different and gives you the possibility to pass every object which implements Comparable interface.


The ListDataProvider is also inside the class. It is used for the sorting functionality.


The function addColumn is also changed and like the comparator function, you can now also past here your object without to care about the rest of the functionality. This is also the function which you will call when you inherit the CellTableSorting class.


There is also a one new function I called setData, where you can pass the data to table. What is interesting here you can call the function as much time as you wish and pass your up to date data to the table. For example if you have a timer which has to update the data in the table every let’s say 10 minutes, you can call this function and pass the new data to the table. 


I make also new view in my MVP project which shows you how to use the new sortable table class, check out the code here.


cheers!

2 comments:

  1. HI, I was looking into your sample view. I didn't see any place where you are adding a sortable header. Am i missing something.
    -Ahmed

    ReplyDelete
  2. It depends which class you look, check this one here:
    http://code.google.com/p/wcinteractions/source/browse/trunk/MVP2.2/src/com/mvp/client/ui/CellTableSortingViewImpl.java

    on the line 143.

    cheers

    ReplyDelete