summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--all/src/main/templates/release-notes.html2
-rw-r--r--documentation/components/components-grid.asciidoc29
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java188
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnPresentation.java99
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnPresentationTest.java27
5 files changed, 318 insertions, 27 deletions
diff --git a/all/src/main/templates/release-notes.html b/all/src/main/templates/release-notes.html
index 0652dbd58a..46891fe8b2 100644
--- a/all/src/main/templates/release-notes.html
+++ b/all/src/main/templates/release-notes.html
@@ -108,6 +108,8 @@
<li>Tooltip styles for <tt>ContentMode.PREFORMATTED</tt> have been changed in all built-in themes to use the application font and allow long lines to wrap to multiple lines.</li>
<li><tt>Grid.Column</tt> now extends <tt>AbstractExtension</tt> instead of <tt>AbstractGridExtension</tt> to hide data generator specific API.</li>
<li><tt>DataCommunicator</tt>, <tt>DataKeyMapper</tt> and <tt>KeyMapper</tt> public APIs have some minor changes for better bean identification.</li>
+ <li><tt>Grid.createColumn</tt> now has one more parameter <tt>presentationProvider</tt>.</li>
+
<h2>For incompatible or behaviour-altering changes in 8.0, please see <a href="https://vaadin.com/download/release/8.0/8.0.0/release-notes.html#incompatible">8.0 release notes</a></h2>
diff --git a/documentation/components/components-grid.asciidoc b/documentation/components/components-grid.asciidoc
index 5a9cef230b..6d73da72df 100644
--- a/documentation/components/components-grid.asciidoc
+++ b/documentation/components/components-grid.asciidoc
@@ -613,6 +613,7 @@ grid.addColumn(person -> {
}, new ComponentRenderer());
----
+
[[components.grid.renderer.custom]]
=== Custom Renderers
@@ -896,6 +897,34 @@ You can modify the error message by implementing a custom
[interfacename]#EditorErrorGenerator# with for the [classname]#Editor#.
+[[components.grid.presentation.provider]]
+=== Presentation Value Providers
+
+By default, a renderer displays the column value. If you want to edit an
+internal value (such as an address object) but show a simpler representation
+when not editing a row, a presentation value provider can be used.
+
+A presentation value provider converts the value of a cell (obtained with a
+value provider, and used by the editor) to a different representation to be
+shown by renderers when the cell is not being edited. A custom renderer can
+optionally be used for the presentation values.
+
+In the following example, we demonstrate one way to use a simplified
+presentation of an address column while allowing editing the full address:
+
+[source, java]
+----
+Column<Person, Address> column = grid.addColumn(Person::getAddress);
+// alternatively, the presentation provider can be given as an extra parameter
+// to addColumn()
+column.setRenderer(
+ address -> address.getCity() + " " + address.getCountry(),
+ new TextRenderer());
+column.setCaption("Address");
+column.setEditorComponent(new AddressField(), Person::setAddress);
+----
+
+
////
// Not supported in 8
[[components.grid.scrolling]]
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index 58f834cafd..6e11989757 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -822,6 +822,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
public static class Column<T, V> extends AbstractExtension {
private final ValueProvider<T, V> valueProvider;
+ private ValueProvider<V, ?> presentationProvider;
private SortOrderProvider sortOrderProvider = direction -> {
String id = getId();
@@ -846,22 +847,11 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
assert communicationId != null : "No communication ID set for column "
+ state.caption;
- @SuppressWarnings("unchecked")
- Renderer<V> renderer = (Renderer<V>) state.renderer;
-
JsonObject obj = getDataObject(jsonObject,
DataCommunicatorConstants.DATA);
- V providerValue = valueProvider.apply(item);
-
- // Make Grid track components.
- if (renderer instanceof ComponentRenderer
- && providerValue instanceof Component) {
- addComponent(item, (Component) providerValue);
- }
- JsonValue rendererValue = renderer.encode(providerValue);
-
- obj.put(communicationId, rendererValue);
+ obj.put(communicationId, generateRendererValue(item,
+ presentationProvider, state.renderer));
String style = styleGenerator.apply(item);
if (style != null && !style.isEmpty()) {
@@ -906,19 +896,48 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* the function to get values from items, not
* <code>null</code>
* @param renderer
- * the type of value, not <code>null</code>
+ * the value renderer, not <code>null</code>
*/
protected Column(ValueProvider<T, V> valueProvider,
Renderer<? super V> renderer) {
+ this(valueProvider, ValueProvider.identity(), renderer);
+ }
+
+ /**
+ * Constructs a new Column configuration with given renderer and value
+ * provider.
+ * <p>
+ * For a more complete explanation on presentation provider, see
+ * {@link #setRenderer(ValueProvider, Renderer)}.
+ *
+ * @param valueProvider
+ * the function to get values from items, not
+ * <code>null</code>
+ * @param presentationProvider
+ * the function to get presentations from the value of this
+ * column, not <code>null</code>. For more details, see {@link #setRenderer(ValueProvider, Renderer)}
+ * @param renderer
+ * the presentation renderer, not <code>null</code>
+ * @param <P>
+ * the presentation type
+ *
+ * @since 8.1
+ */
+ protected <P> Column(ValueProvider<T, V> valueProvider,
+ ValueProvider<V, P> presentationProvider,
+ Renderer<? super P> renderer) {
Objects.requireNonNull(valueProvider,
"Value provider can't be null");
+ Objects.requireNonNull(presentationProvider,
+ "Presentation provider can't be null");
Objects.requireNonNull(renderer, "Renderer can't be null");
ColumnState state = getState();
this.valueProvider = valueProvider;
- state.renderer = renderer;
+ this.presentationProvider = presentationProvider;
+ state.renderer = renderer;
state.caption = "";
// Add the renderer as a child extension of this extension, thus
@@ -926,7 +945,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
// removed
addExtension(renderer);
- Class<? super V> valueType = renderer.getPresentationType();
+ Class<? super P> valueType = renderer.getPresentationType();
if (Comparable.class.isAssignableFrom(valueType)) {
comparator = (a, b) -> compareComparables(
@@ -1008,6 +1027,20 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
}
}
+ @SuppressWarnings("unchecked")
+ private <P> JsonValue generateRendererValue(T item,
+ ValueProvider<V, P> presentationProvider, Connector renderer) {
+ P presentationValue = presentationProvider
+ .apply(valueProvider.apply(item));
+
+ // Make Grid track components.
+ if (renderer instanceof ComponentRenderer
+ && presentationValue instanceof Component) {
+ addComponent(item, (Component) presentationValue);
+ }
+ return ((Renderer<P>) renderer).encode(presentationValue);
+ }
+
private void addComponent(T item, Component component) {
if (activeComponents.containsKey(item)) {
if (activeComponents.get(item).equals(component)) {
@@ -1887,7 +1920,39 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* @since 8.0.3
*/
public Column<T, V> setRenderer(Renderer<? super V> renderer) {
- Objects.requireNonNull(renderer, "Renderer can't be null");
+ return setRenderer(ValueProvider.identity(), renderer);
+ }
+
+ /**
+ * Sets the Renderer for this Column. Setting the renderer will cause
+ * all currently available row data to be recreated and sent to the
+ * client.
+ * <p>
+ * The presentation provider is a method that takes the value of this
+ * column on a single row, and maps that to a value that the renderer
+ * accepts. This feature can be used for storing a complex value in a
+ * column for editing, but providing a simplified presentation for the
+ * user when not editing.
+ *
+ * @param presentationProvider
+ * the function to get presentations from the value of this
+ * column, not {@code null}
+ * @param renderer
+ * the new renderer, not {@code null}
+ *
+ * @param <P>
+ * the presentation type
+ *
+ * @return this column
+ *
+ * @since 8.1
+ */
+ public <P> Column<T, V> setRenderer(
+ ValueProvider<V, P> presentationProvider,
+ Renderer<? super P> renderer) {
+ Objects.requireNonNull(renderer, "Renderer can not be null");
+ Objects.requireNonNull(presentationProvider,
+ "Presentation provider can not be null");
// Remove old renderer
Connector oldRenderer = getState().renderer;
@@ -1898,6 +1963,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
// Set new renderer
getState().renderer = renderer;
addExtension(renderer);
+ this.presentationProvider = presentationProvider;
// Trigger redraw
getGrid().getDataCommunicator().reset();
@@ -1911,8 +1977,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* @return the renderer
* @since 8.1
*/
- public Renderer<? super V> getRenderer() {
- return (Renderer<? super V>) getState().renderer;
+ public Renderer<?> getRenderer() {
+ return (Renderer<?>) getState().renderer;
}
/**
@@ -1921,6 +1987,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* @return the grid that this column belongs to, or <code>null</code> if
* this column has not yet been associated with any grid
*/
+ @SuppressWarnings("unchecked")
protected Grid<T> getGrid() {
return (Grid<T>) getParent();
}
@@ -2500,7 +2567,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* @param valueProvider
* the value provider
* @param renderer
- * the column value class
+ * the column value renderer
* @param <V>
* the column value type
*
@@ -2510,8 +2577,67 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
*/
public <V> Column<T, V> addColumn(ValueProvider<T, V> valueProvider,
AbstractRenderer<? super T, ? super V> renderer) {
+ return addColumn(valueProvider, ValueProvider.identity(), renderer);
+ }
+
+ /**
+ * Adds a new column to this {@link Grid} with value provider and
+ * presentation provider.
+ * <p>
+ * <strong>Note:</strong> The presentation type for this method is set to be
+ * String. To use any custom renderer with the presentation provider, use
+ * {@link #addColumn(ValueProvider, ValueProvider, AbstractRenderer)}.
+ *
+ * @param valueProvider
+ * the value provider
+ * @param presentationProvider
+ * the value presentation provider
+ * @param <V>
+ * the column value type
+ *
+ * @see #addColumn(ValueProvider, ValueProvider, AbstractRenderer)
+ *
+ * @return the new column
+ * @since 8.1
+ */
+ public <V> Column<T, V> addColumn(ValueProvider<T, V> valueProvider,
+ ValueProvider<V, String> presentationProvider) {
+ return addColumn(valueProvider, presentationProvider,
+ new TextRenderer());
+ }
+
+ /**
+ * Adds a new column to this {@link Grid} with value provider, presentation
+ * provider and typed renderer.
+ *
+ * <p>
+ * The presentation provider is a method that takes the value from the value
+ * provider, and maps that to a value that the renderer accepts. This
+ * feature can be used for storing a complex value in a column for editing,
+ * but providing a simplified presentation for the user when not editing.
+ *
+ * @param valueProvider
+ * the value provider
+ * @param presentationProvider
+ * the value presentation provider
+ * @param renderer
+ * the column value renderer
+ * @param <V>
+ * the column value type
+ * @param <P>
+ * the column presentation type
+ *
+ * @return the new column
+ *
+ * @see AbstractRenderer
+ * @since 8.1
+ */
+ public <V, P> Column<T, V> addColumn(ValueProvider<T, V> valueProvider,
+ ValueProvider<V, P> presentationProvider,
+ AbstractRenderer<? super T, ? super P> renderer) {
String generatedIdentifier = getGeneratedIdentifier();
- Column<T, V> column = createColumn(valueProvider, renderer);
+ Column<T, V> column = createColumn(valueProvider, presentationProvider,
+ renderer);
addColumn(generatedIdentifier, column);
return column;
}
@@ -2536,21 +2662,28 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
}
/**
- * Creates a column instance from a value provider and a renderer.
+ * Creates a column instance from a value provider, presentation provider
+ * and a renderer.
*
* @param valueProvider
* the value provider
+ * @param presentationProvider
+ * the presentation provider
* @param renderer
* the renderer
* @return a new column instance
* @param <V>
* the column value type
+ * @param <P>
+ * the column presentation type
*
- * @since 8.0.3
+ * @since 8.1
*/
- protected <V> Column<T, V> createColumn(ValueProvider<T, V> valueProvider,
- AbstractRenderer<? super T, ? super V> renderer) {
- return new Column<>(valueProvider, renderer);
+ protected <V, P> Column<T, V> createColumn(
+ ValueProvider<T, V> valueProvider,
+ ValueProvider<V, P> presentationProvider,
+ AbstractRenderer<? super T, ? super P> renderer) {
+ return new Column<>(valueProvider, presentationProvider, renderer);
}
private void addColumn(String identifier, Column<T, ?> column) {
@@ -4019,7 +4152,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
column = addColumn(id);
} else {
DeclarativeValueProvider<T> provider = new DeclarativeValueProvider<>();
- column = createColumn(provider, new HtmlRenderer());
+ column = createColumn(provider, ValueProvider.identity(),
+ new HtmlRenderer());
addColumn(getGeneratedIdentifier(), column);
if (id != null) {
column.setId(id);
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnPresentation.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnPresentation.java
new file mode 100644
index 0000000000..2f099982a6
--- /dev/null
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnPresentation.java
@@ -0,0 +1,99 @@
+package com.vaadin.tests.components.grid;
+
+import com.vaadin.data.HasValue;
+import com.vaadin.server.ErrorMessage;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.Registration;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.tests.data.bean.Address;
+import com.vaadin.tests.data.bean.Person;
+import com.vaadin.ui.Composite;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.renderers.TextRenderer;
+
+/**
+ * An example for using a different value and presentation types in a Grid
+ * column.
+ */
+public class GridColumnPresentation extends AbstractTestUI {
+
+ /**
+ * Dummy HasValue for Address.
+ */
+ private static class AddressField extends Composite
+ implements HasValue<Address> {
+
+ Address address;
+ private Label label;
+
+ public AddressField() {
+ super();
+
+ label = new Label();
+ setCompositionRoot(label);
+ }
+
+ @Override
+ public void setValue(Address value) {
+ Address oldAddress = address;
+ address = value;
+ label.setValue(String.valueOf(address));
+ fireEvent(new ValueChangeEvent<>(this, oldAddress, false));
+ }
+
+ @Override
+ public Address getValue() {
+ return address;
+ }
+
+ @Override
+ public Registration addValueChangeListener(
+ ValueChangeListener<Address> listener) {
+ return addListener(ValueChangeEvent.class, listener,
+ ValueChangeListener.VALUE_CHANGE_METHOD);
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return super.isReadOnly();
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) {
+ super.setReadOnly(readOnly);
+ }
+
+ @Override
+ public boolean isRequiredIndicatorVisible() {
+ return super.isRequiredIndicatorVisible();
+ }
+
+ @Override
+ public void setRequiredIndicatorVisible(boolean visible) {
+ super.setRequiredIndicatorVisible(visible);
+ }
+
+ @Override
+ public void setComponentError(ErrorMessage componentError) {
+ label.setComponentError(componentError);
+ }
+ }
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ Grid<Person> personGrid = new Grid<>();
+ personGrid.setItems(Person.createTestPerson1(),
+ Person.createTestPerson2());
+ personGrid.addColumn(Person::getAddress)
+ .setRenderer(
+ address -> address.getCity() + " "
+ + address.getCountry().name(),
+ new TextRenderer())
+ .setCaption("Address")
+ .setEditorComponent(new AddressField(), Person::setAddress);
+ personGrid.getEditor().setEnabled(true);
+ addComponent(personGrid);
+ }
+
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnPresentationTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnPresentationTest.java
new file mode 100644
index 0000000000..79233dc50d
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnPresentationTest.java
@@ -0,0 +1,27 @@
+package com.vaadin.tests.components.grid;
+
+import com.vaadin.testbench.elements.GridElement;
+import com.vaadin.testbench.parallel.TestCategory;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+@TestCategory("grid")
+public class GridColumnPresentationTest extends SingleBrowserTest {
+
+ @Test
+ public void presenterAndEditor() {
+ openTestURL();
+ GridElement grid = $(GridElement.class).get(0);
+ assertEquals("Turku FINLAND", grid.getCell(0, 0).getText());
+ assertEquals("Amsterdam NETHERLANDS", grid.getCell(1, 0).getText());
+ //Activate editor
+ GridElement.GridCellElement cell = grid.getCell(1, 0);
+ cell.doubleClick();
+
+ assertEquals("Address [streetAddress=Red street, postalCode=12, city=Amsterdam, country=Netherlands]",
+ grid.getEditor().getField(0).getText());
+
+ }
+}