Skip to content

Commit c766694

Browse files
tsuoanttilahesara
authored andcommitted
Allow changing renderers after column creation (vaadin#8841)
Closes vaadin#8250
1 parent 0fe0e72 commit c766694

File tree

8 files changed

+296
-26
lines changed

8 files changed

+296
-26
lines changed

client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ public Object getValue(JsonObject row) {
7373
return null;
7474
}
7575
};
76-
column.setRenderer(getRendererConnector().getRenderer());
7776
getParent().addColumn(column, getState().internalId);
7877
}
7978

@@ -92,6 +91,11 @@ void updateSortable() {
9291
column.setSortable(getState().sortable);
9392
}
9493

94+
@OnStateChange("renderer")
95+
void updateRenderer() {
96+
column.setRenderer(getRendererConnector().getRenderer());
97+
}
98+
9599
@OnStateChange("hidingToggleCaption")
96100
void updateHidingToggleCaption() {
97101
column.setHidingToggleCaption(getState().hidingToggleCaption);

client/src/main/java/com/vaadin/client/connectors/treegrid/TreeGridConnector.java

+29-18
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
import java.util.Collection;
1919
import java.util.logging.Logger;
2020

21+
import com.google.gwt.core.client.Scheduler;
2122
import com.google.gwt.dom.client.BrowserEvents;
2223
import com.google.gwt.dom.client.Element;
2324
import com.google.gwt.event.dom.client.KeyCodes;
2425
import com.google.gwt.user.client.Event;
2526
import com.google.web.bindery.event.shared.HandlerRegistration;
26-
import com.vaadin.client.communication.StateChangeEvent;
27+
import com.vaadin.client.annotations.OnStateChange;
2728
import com.vaadin.client.connectors.grid.GridConnector;
2829
import com.vaadin.client.renderers.ClickableRenderer;
2930
import com.vaadin.client.renderers.HierarchyRenderer;
@@ -43,7 +44,7 @@
4344

4445
/**
4546
* A connector class for the TreeGrid component.
46-
*
47+
*
4748
* @author Vaadin Ltd
4849
* @since 8.1
4950
*/
@@ -67,13 +68,18 @@ public TreeGridState getState() {
6768
return (TreeGridState) super.getState();
6869
}
6970

70-
@Override
71-
public void onStateChanged(StateChangeEvent stateChangeEvent) {
72-
super.onStateChanged(stateChangeEvent);
73-
74-
if (stateChangeEvent.hasPropertyChanged("hierarchyColumnId")
75-
|| stateChangeEvent.hasPropertyChanged("columns")) {
76-
71+
/**
72+
* This method has been scheduled finally to avoid possible race conditions
73+
* between state change handling for the Grid and its columns. The renderer
74+
* of the column is set in a state change handler, and might not be
75+
* available when this method is executed.
76+
* <p>
77+
* TODO: This might need some clean up if we decide to allow setting a new
78+
* renderer for hierarchy columns.
79+
*/
80+
@OnStateChange("hierarchyColumnId")
81+
void updateHierarchyColumn() {
82+
Scheduler.get().scheduleFinally(() -> {
7783
// Id of old hierarchy column
7884
String oldHierarchyColumnId = this.hierarchyColumnId;
7985

@@ -111,7 +117,7 @@ public void onStateChanged(StateChangeEvent stateChangeEvent) {
111117
Logger.getLogger(TreeGridConnector.class.getName()).warning(
112118
"Couldn't find column: " + newHierarchyColumnId);
113119
}
114-
}
120+
});
115121
}
116122

117123
private HierarchyRenderer getHierarchyRenderer() {
@@ -159,18 +165,21 @@ public void onUnregister() {
159165
}
160166

161167
private native void replaceCellFocusEventHandler(Grid<?> grid,
162-
GridEventHandler<?> eventHandler)/*-{
168+
GridEventHandler<?> eventHandler)
169+
/*-{
163170
var browserEventHandlers = [email protected]::browserEventHandlers;
164-
171+
165172
// FocusEventHandler is initially 5th in the list of browser event handlers
166173
[email protected]::set(*)(5, eventHandler);
167174
}-*/;
168175

169-
private native void replaceClickEvent(Grid<?> grid, GridClickEvent event)/*-{
176+
private native void replaceClickEvent(Grid<?> grid, GridClickEvent event)
177+
/*-{
170178
[email protected]::clickEvent = event;
171179
}-*/;
172180

173-
private native EventCellReference<?> getEventCell(Grid<?> grid)/*-{
181+
private native EventCellReference<?> getEventCell(Grid<?> grid)
182+
/*-{
174183
return [email protected]::eventCell;
175184
}-*/;
176185

@@ -213,13 +222,15 @@ public void onEvent(Grid.GridEvent<JsonObject> event) {
213222
}
214223
}
215224

216-
private native Collection<String> getNavigationEvents(Grid<?> grid)/*-{
217-
return [email protected]::cellFocusHandler
218-
[email protected]::getNavigationEvents()();
225+
private native Collection<String> getNavigationEvents(Grid<?> grid)
226+
/*-{
227+
return [email protected]::cellFocusHandler
228+
[email protected]::getNavigationEvents()();
219229
}-*/;
220230

221231
private native void handleNavigationEvent(Grid<?> grid,
222-
Grid.GridEvent<JsonObject> event)/*-{
232+
Grid.GridEvent<JsonObject> event)
233+
/*-{
223234
[email protected]::cellFocusHandler
224235
[email protected]::handleNavigationEvent(*)(
225236
[email protected]::getDomEvent()(),

documentation/components/components-grid.asciidoc

+15-1
Original file line numberDiff line numberDiff line change
@@ -403,9 +403,23 @@ You set the column renderer in the [classname]#Grid.Column# object as follows:
403403
[source, java]
404404
----
405405
// the type of birthYear is a number
406-
Column<Integer> bornColumn = grid.addColumn(Person:getBirthYear,
406+
Column<Person, Integer> bornColumn = grid.addColumn(Person:getBirthYear,
407407
new NumberRenderer("born in %d AD"));
408408
----
409+
410+
Changing the renderer during runtime is also possible, but for type safety
411+
you should store the column reference with data types for doing this.
412+
When you change the renderer, the content of Grid is refreshed.
413+
414+
[source, java]
415+
----
416+
Column<Person, Integer> ageColumn = grid.addColumn(Person::getBirthYear);
417+
// The default renderer is TextRenderer
418+
addComponent(new Button("Change renderer",
419+
clickEvent -> ageColumn.setRenderer(new NumberRenderer())
420+
));
421+
----
422+
409423
The following renderers are available, as defined in the server-side
410424
[package]#com.vaadin.ui.renderers# package:
411425

server/src/main/java/com/vaadin/ui/Grid.java

+44-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import com.vaadin.server.SerializableSupplier;
7474
import com.vaadin.server.Setter;
7575
import com.vaadin.server.VaadinServiceClassLoaderUtil;
76+
import com.vaadin.shared.Connector;
7677
import com.vaadin.shared.MouseEventDetails;
7778
import com.vaadin.shared.Registration;
7879
import com.vaadin.shared.data.DataCommunicatorConstants;
@@ -1813,6 +1814,34 @@ public <F, C extends HasValue<F> & Component> Column<T, V> setEditorComponent(
18131814
return setEditorBinding(binding);
18141815
}
18151816

1817+
/**
1818+
* Sets the Renderer for this Column. Setting the renderer will cause
1819+
* all currently available row data to be recreated and sent to the
1820+
* client.
1821+
*
1822+
* @param renderer
1823+
* the new renderer
1824+
* @return this column
1825+
*/
1826+
public Column<T, V> setRenderer(Renderer<? super V> renderer) {
1827+
Objects.requireNonNull(renderer, "Renderer can't be null");
1828+
1829+
// Remove old renderer
1830+
Connector oldRenderer = getState().renderer;
1831+
if (oldRenderer != null && oldRenderer instanceof Extension) {
1832+
removeExtension((Extension) oldRenderer);
1833+
}
1834+
1835+
// Set new renderer
1836+
getState().renderer = renderer;
1837+
addExtension(renderer);
1838+
1839+
// Trigger redraw
1840+
getParent().getDataCommunicator().reset();
1841+
1842+
return this;
1843+
}
1844+
18161845
/**
18171846
* Gets the grid that this column belongs to.
18181847
*
@@ -2341,11 +2370,25 @@ public <V> Column<T, V> addColumn(ValueProvider<T, V> valueProvider) {
23412370
public <V> Column<T, V> addColumn(ValueProvider<T, V> valueProvider,
23422371
AbstractRenderer<? super T, ? super V> renderer) {
23432372
String generatedIdentifier = getGeneratedIdentifier();
2344-
Column<T, V> column = new Column<>(valueProvider, renderer);
2373+
Column<T, V> column = createColumn(valueProvider, renderer);
23452374
addColumn(generatedIdentifier, column);
23462375
return column;
23472376
}
23482377

2378+
/**
2379+
* Creates a column instance from a value provider and a renderer.
2380+
*
2381+
* @param valueProvider
2382+
* the value provider
2383+
* @param renderer
2384+
* the renderer
2385+
* @return a new column instance
2386+
*/
2387+
protected <V> Column<T, V> createColumn(ValueProvider<T, V> valueProvider,
2388+
AbstractRenderer<? super T, ? super V> renderer) {
2389+
return new Column<>(valueProvider, renderer);
2390+
}
2391+
23492392
private void addColumn(String identifier, Column<T, ?> column) {
23502393
if (getColumns().contains(column)) {
23512394
return;

server/src/main/java/com/vaadin/ui/TreeGrid.java

+31-5
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,24 @@
1919
import java.util.Objects;
2020
import java.util.stream.Stream;
2121

22+
import com.vaadin.data.ValueProvider;
2223
import com.vaadin.data.provider.DataProvider;
2324
import com.vaadin.data.provider.HierarchicalDataProvider;
2425
import com.vaadin.shared.ui.treegrid.NodeCollapseRpc;
2526
import com.vaadin.shared.ui.treegrid.TreeGridCommunicationConstants;
2627
import com.vaadin.shared.ui.treegrid.TreeGridState;
28+
import com.vaadin.ui.renderers.AbstractRenderer;
29+
import com.vaadin.ui.renderers.Renderer;
2730

2831
import elemental.json.Json;
2932
import elemental.json.JsonObject;
3033

3134
/**
3235
* A grid component for displaying hierarchical tabular data.
33-
*
36+
*
3437
* @author Vaadin Ltd
3538
* @since 8.1
36-
*
39+
*
3740
* @param <T>
3841
* the grid bean type
3942
*/
@@ -106,9 +109,12 @@ public void setDataProvider(DataProvider<T, ?> dataProvider) {
106109
* <p>
107110
* Setting a hierarchy column by calling this method also sets the column to
108111
* be visible and not hidable.
109-
*
112+
* <p>
113+
* <strong>Note:</strong> Changing the Renderer of the hierarchy column is
114+
* not supported.
115+
*
110116
* @see Column#setId(String)
111-
*
117+
*
112118
* @param id
113119
* id of the column to use for displaying hierarchy
114120
*/
@@ -137,7 +143,7 @@ protected TreeGridState getState(boolean markAsDirty) {
137143
* expanded, it will be collapsed.
138144
* <p>
139145
* Toggling expansion on a leaf item in the hierarchy will have no effect.
140-
*
146+
*
141147
* @param item
142148
* the item to toggle expansion for
143149
*/
@@ -161,4 +167,24 @@ public void toggleCollapse(T item) {
161167
}
162168
return (HierarchicalDataProvider<T, ?>) dataProvider;
163169
}
170+
171+
@Override
172+
protected <V> Column<T, V> createColumn(ValueProvider<T, V> valueProvider,
173+
AbstractRenderer<? super T, ? super V> renderer) {
174+
return new Column<T, V>(valueProvider, renderer) {
175+
176+
@Override
177+
public com.vaadin.ui.Grid.Column<T, V> setRenderer(
178+
Renderer<? super V> renderer) {
179+
// Disallow changing renderer for the hierarchy column
180+
if (getInternalIdForColumn(this).equals(
181+
TreeGrid.this.getState(false).hierarchyColumnId)) {
182+
throw new IllegalStateException(
183+
"Changing the renderer of the hierarchy column is not allowed.");
184+
}
185+
186+
return super.setRenderer(renderer);
187+
}
188+
};
189+
}
164190
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.vaadin.tests.components.treegrid;
2+
3+
import org.junit.Test;
4+
5+
import com.vaadin.ui.TreeGrid;
6+
import com.vaadin.ui.renderers.TextRenderer;
7+
8+
public class TreeGridColumnTest {
9+
10+
private TreeGrid<String> treeGrid = new TreeGrid<>();
11+
12+
@Test(expected = IllegalStateException.class)
13+
public void testChangeRendererOfHierarchyColumn() {
14+
treeGrid.addColumn(Object::toString).setId("foo");
15+
treeGrid.setHierarchyColumn("foo");
16+
// This should not be allowed.
17+
treeGrid.getColumn("foo").setRenderer(new TextRenderer());
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.vaadin.tests.components.grid;
2+
3+
import java.util.stream.IntStream;
4+
5+
import com.vaadin.annotations.Widgetset;
6+
import com.vaadin.server.VaadinRequest;
7+
import com.vaadin.tests.components.AbstractTestUI;
8+
import com.vaadin.ui.Button;
9+
import com.vaadin.ui.Grid;
10+
import com.vaadin.ui.Grid.Column;
11+
import com.vaadin.ui.Notification;
12+
import com.vaadin.ui.renderers.ButtonRenderer;
13+
import com.vaadin.ui.renderers.TextRenderer;
14+
15+
@Widgetset("com.vaadin.DefaultWidgetSet")
16+
public class GridRendererSwitch extends AbstractTestUI {
17+
18+
private boolean textRenderer = true;
19+
private boolean reverse = false;
20+
21+
@Override
22+
protected void setup(VaadinRequest request) {
23+
Grid<Integer> grid = new Grid<>();
24+
Column<Integer, String> column = grid.addColumn(i -> "Foo " + i)
25+
.setCaption("Foo");
26+
Column<Integer, String> secondColumn = grid.addColumn(i -> "Bar " + i)
27+
.setCaption("Bar");
28+
29+
addComponent(grid);
30+
addComponent(new Button("Switch", e -> {
31+
if (textRenderer) {
32+
ButtonRenderer<Integer> renderer = new ButtonRenderer<>();
33+
renderer.addClickListener(event -> Notification
34+
.show("Click on row: " + event.getItem()));
35+
column.setRenderer(renderer);
36+
} else {
37+
column.setRenderer(new TextRenderer());
38+
}
39+
textRenderer = !textRenderer;
40+
}));
41+
addComponent(new Button("Reverse", e -> {
42+
if (reverse) {
43+
grid.setColumnOrder(column, secondColumn);
44+
} else {
45+
grid.setColumnOrder(secondColumn, column);
46+
}
47+
reverse = !reverse;
48+
}));
49+
50+
grid.setItems(IntStream.range(0, 10).boxed());
51+
}
52+
53+
}

0 commit comments

Comments
 (0)