summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeif Åstrand <legioth@gmail.com>2017-01-31 09:15:50 +0200
committerGitHub <noreply@github.com>2017-01-31 09:15:50 +0200
commit839c37a6ccda1f4f2d88c1210372d76dc15ebc6e (patch)
tree764ffa9e387c983e2c1b5523aca8d6911c0e72e5
parentbc34610865f7ae71286d19a6a6a4b00bce3ec2ce (diff)
downloadvaadin-framework-839c37a6ccda1f4f2d88c1210372d76dc15ebc6e.tar.gz
vaadin-framework-839c37a6ccda1f4f2d88c1210372d76dc15ebc6e.zip
Refactor editor API to use Binding instead of a component generator (#8368)
Fixes #8366
-rw-r--r--documentation/components/components-grid.asciidoc33
-rw-r--r--server/src/main/java/com/vaadin/ui/Grid.java195
-rw-r--r--server/src/main/java/com/vaadin/ui/components/grid/EditorComponentGenerator.java45
-rw-r--r--server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java9
-rw-r--r--server/src/test/java/com/vaadin/tests/server/component/grid/GridDeclarativeTest.java3
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java15
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/GridEditingWithNoScrollBars.java2
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java52
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorMultiselect.java14
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorUI.java14
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java21
11 files changed, 190 insertions, 213 deletions
diff --git a/documentation/components/components-grid.asciidoc b/documentation/components/components-grid.asciidoc
index 67923952e3..3ea0e578f3 100644
--- a/documentation/components/components-grid.asciidoc
+++ b/documentation/components/components-grid.asciidoc
@@ -692,11 +692,12 @@ editor. In the editor, the input fields can be edited, as well as navigated with
kbd:[Tab] and kbd:[Shift+Tab] keys. If validation fails, an error is displayed and the user
can correct the inputs.
-The [classname]#Editor# is accessible via [methodname]#getEditor()#, and to enable editing, you need to call [methodname]#setEnabled(true) on it.
+The [classname]#Editor# is accessible via [methodname]#getEditor()#, and to enable editing, you need to call [methodname]#setEnabled(true)# on it.
-The editor is based on [classname]#Binder# which is used to bind the data
-to the editor. See <<dummy/../../../framework/datamodel/datamodel-forms.asciidoc#datamodel.forms.beans,"Binding Beans to Forms">> for more information on setting up field components and validation by using [classname]#Binder.
-The [classname]#Binder# needs to be set with [methodname]#setBinder# in [classname]#Editor#.
+The editor is based on [classname]#Binder# which is used to bind the data to the editor.
+See <<dummy/../../../framework/datamodel/datamodel-forms.asciidoc#datamodel.forms.beans,"Binding Beans to Forms">> for more information on setting up field components and validation by using [classname]#Binder#.
+For each column that should be editable, a binding should be created in the editor binder and then the column is configured to use that binding.
+For simple cases where no conversion or validation is needed, it is also possible to directly use `setEditorComponent` on a `Column` to only define the editor component and a setter that updates the row object when saving.
[source, java]
----
@@ -707,26 +708,22 @@ Grid<Todo> grid = new Grid<>();
TextField taskField = new TextField();
CheckBox doneField = new CheckBox();
-Binder<Todo> binder = new Binder<>();
-binder.bind(taskField, Todo::getTask, Todo::setTask);
-binder.bind(doneField, Todo::isDone, Todo::setDone);
+Binder<Todo> binder = grid.getEditor().getBinder();
-grid.getEditor().setBinder(binder);
-grid.getEditor().setEnabled(true);
+Binding<Todo, Boolean> doneBinding = binder.bind(
+ doneField, Todo::isDone, Todo::setDone);
-Column<Todo, String> column = grid
- .addColumn(todo -> String.valueOf(todo.isDone()));
+Column<Todo, String> column = grid.addColumn(
+ todo -> String.valueOf(todo.isDone()));
column.setWidth(75);
-column.setEditorComponent(doneField);
-
-grid.addColumn(Todo::getTask).setEditorComponent(taskField);
-----
+column.setEditorBinding(doneBinding);
-It is possible to customize the used editor component for each column and row,
-by using [methodname]#setEditorComponentGenerator(EditorComponentGenerator)# in
-[classname]#Column#.
+grid.addColumn(Todo::getTask).setEditorComponent(
+ taskField, Todo::setTask).setExpandRatio(1);
+grid.getEditor().setEnabled(true);
+----
[[components.grid.editing.buffered]]
=== Buffered / Unbuffered Mode
diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java
index 7aed70461e..4052282fdc 100644
--- a/server/src/main/java/com/vaadin/ui/Grid.java
+++ b/server/src/main/java/com/vaadin/ui/Grid.java
@@ -42,7 +42,9 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.vaadin.data.Binder;
+import com.vaadin.data.Binder.Binding;
import com.vaadin.data.HasDataProvider;
+import com.vaadin.data.HasValue;
import com.vaadin.data.ValueProvider;
import com.vaadin.data.provider.DataCommunicator;
import com.vaadin.data.provider.DataProvider;
@@ -63,6 +65,7 @@ import com.vaadin.server.Extension;
import com.vaadin.server.JsonCodec;
import com.vaadin.server.SerializableComparator;
import com.vaadin.server.SerializableFunction;
+import com.vaadin.server.Setter;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.DataCommunicatorConstants;
@@ -84,7 +87,6 @@ import com.vaadin.ui.components.grid.ColumnVisibilityChangeListener;
import com.vaadin.ui.components.grid.DescriptionGenerator;
import com.vaadin.ui.components.grid.DetailsGenerator;
import com.vaadin.ui.components.grid.Editor;
-import com.vaadin.ui.components.grid.EditorComponentGenerator;
import com.vaadin.ui.components.grid.EditorImpl;
import com.vaadin.ui.components.grid.Footer;
import com.vaadin.ui.components.grid.FooterCell;
@@ -560,8 +562,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
assert columnInternalIds.length == directions.length : "Column and sort direction counts don't match.";
- List<GridSortOrder<T>> list = new ArrayList<>(
- directions.length);
+ List<GridSortOrder<T>> list = new ArrayList<>(directions.length);
for (int i = 0; i < columnInternalIds.length; ++i) {
Column<T, ?> column = columnKeys.get(columnInternalIds[i]);
list.add(new GridSortOrder<>(column, directions[i]));
@@ -784,7 +785,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
private StyleGenerator<T> styleGenerator = item -> null;
private DescriptionGenerator<T> descriptionGenerator;
- private EditorComponentGenerator<T> componentGenerator;
+ private Binding<T, ?> editorBinding;
private String userId;
@@ -793,12 +794,13 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* provider.
*
* @param valueProvider
- * the function to get values from items
+ * the function to get values from items, not
+ * <code>null</code>
* @param renderer
- * the type of value
+ * the type of value, not <code>null</code>
*/
protected Column(ValueProvider<T, ? extends V> valueProvider,
- Renderer<V> renderer) {
+ Renderer<V> renderer) {
Objects.requireNonNull(valueProvider,
"Value provider can't be null");
Objects.requireNonNull(renderer, "Renderer can't be null");
@@ -819,7 +821,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
Class<V> valueType = renderer.getPresentationType();
if (Comparable.class.isAssignableFrom(valueType)) {
- comparator = (a, b) -> compareComparables(valueProvider.apply(a), valueProvider.apply(b));
+ comparator = (a, b) -> compareComparables(
+ valueProvider.apply(a), valueProvider.apply(b));
state.sortable = true;
} else if (Number.class.isAssignableFrom(valueType)) {
/*
@@ -827,7 +830,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* Provide explicit comparison support in this case even though
* Number itself isn't Comparable.
*/
- comparator = (a, b) -> compareNumbers((Number) valueProvider.apply(a),
+ comparator = (a, b) -> compareNumbers(
+ (Number) valueProvider.apply(a),
(Number) valueProvider.apply(b));
state.sortable = true;
} else {
@@ -837,7 +841,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
@SuppressWarnings("unchecked")
private static int compareComparables(Object a, Object b) {
- return ((Comparator) Comparator.nullsLast(Comparator.naturalOrder())).compare(a, b);
+ return ((Comparator) Comparator
+ .nullsLast(Comparator.naturalOrder())).compare(a, b);
}
@SuppressWarnings("unchecked")
@@ -845,17 +850,20 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
Number valueA = a != null ? a : Double.POSITIVE_INFINITY;
Number valueB = b != null ? b : Double.POSITIVE_INFINITY;
// Most Number implementations are Comparable
- if (valueA instanceof Comparable && valueA.getClass().isInstance(valueB)) {
+ if (valueA instanceof Comparable
+ && valueA.getClass().isInstance(valueB)) {
return ((Comparable<Number>) valueA).compareTo(valueB);
} else if (valueA.equals(valueB)) {
return 0;
} else {
// Fall back to comparing based on potentially truncated values
- int compare = Long.compare(valueA.longValue(), valueB.longValue());
+ int compare = Long.compare(valueA.longValue(),
+ valueB.longValue());
if (compare == 0) {
// This might still produce 0 even though the values are not
// equals, but there's nothing more we can do about that
- compare = Double.compare(valueA.doubleValue(), valueB.doubleValue());
+ compare = Double.compare(valueA.doubleValue(),
+ valueB.doubleValue());
}
return compare;
}
@@ -979,7 +987,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
"Column identifier cannot be changed");
}
this.userId = id;
- getParent().setColumnId(id, this);
+ getGrid().setColumnId(id, this);
return this;
}
@@ -1018,7 +1026,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
Objects.requireNonNull(caption, "Header caption can't be null");
getState().caption = caption;
- HeaderRow row = getParent().getDefaultHeaderRow();
+ HeaderRow row = getGrid().getDefaultHeaderRow();
if (row != null) {
row.getCell(this).setText(caption);
}
@@ -1128,7 +1136,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
Objects.requireNonNull(cellStyleGenerator,
"Cell style generator must not be null");
this.styleGenerator = cellStyleGenerator;
- getParent().getDataCommunicator().reset();
+ getGrid().getDataCommunicator().reset();
return this;
}
@@ -1154,7 +1162,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
public Column<T, V> setDescriptionGenerator(
DescriptionGenerator<T> cellDescriptionGenerator) {
this.descriptionGenerator = cellDescriptionGenerator;
- getParent().getDataCommunicator().reset();
+ getGrid().getDataCommunicator().reset();
return this;
}
@@ -1202,7 +1210,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
checkColumnIsAttached();
if (expandRatio != getExpandRatio()) {
getState().expandRatio = expandRatio;
- getParent().markAsDirty();
+ getGrid().markAsDirty();
}
return this;
}
@@ -1267,8 +1275,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
}
if (pixelWidth != getWidth()) {
getState().width = pixelWidth;
- getParent().markAsDirty();
- getParent().fireColumnResizeEvent(this, false);
+ getGrid().markAsDirty();
+ getGrid().fireColumnResizeEvent(this, false);
}
return this;
}
@@ -1297,8 +1305,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
checkColumnIsAttached();
if (!isWidthUndefined()) {
getState().width = -1;
- getParent().markAsDirty();
- getParent().fireColumnResizeEvent(this, false);
+ getGrid().markAsDirty();
+ getGrid().fireColumnResizeEvent(this, false);
}
return this;
}
@@ -1324,7 +1332,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
+ maxwidth + ")");
}
getState().minWidth = pixels;
- getParent().markAsDirty();
+ getGrid().markAsDirty();
return this;
}
@@ -1361,7 +1369,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
}
getState().maxWidth = pixels;
- getParent().markAsDirty();
+ getGrid().markAsDirty();
return this;
}
@@ -1389,7 +1397,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
checkColumnIsAttached();
if (resizable != isResizable()) {
getState().resizable = resizable;
- getParent().markAsDirty();
+ getGrid().markAsDirty();
}
return this;
}
@@ -1444,8 +1452,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
checkColumnIsAttached();
if (hidden != isHidden()) {
getState().hidden = hidden;
- getParent().fireColumnVisibilityChangeEvent(this, hidden,
- false);
+ getGrid().fireColumnVisibilityChangeEvent(this, hidden, false);
}
return this;
}
@@ -1511,17 +1518,19 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
/**
* Sets whether this Column has a component displayed in Editor or not.
+ * A column can only be editable if an editor component or binding has
+ * been set.
*
* @param editable
* {@code true} if column is editable; {@code false} if not
* @return this column
*
- * @see #setEditorComponent(Component)
- * @see #setEditorComponentGenerator(EditorComponentGenerator)
+ * @see #setEditorComponent(HasValue, Setter)
+ * @see #setEditorBinding(Binding)
*/
public Column<T, V> setEditable(boolean editable) {
- Objects.requireNonNull(componentGenerator,
- "Column has no editor component defined");
+ Objects.requireNonNull(editorBinding,
+ "Column has no editor binding or component defined");
getState().editable = editable;
return this;
}
@@ -1537,56 +1546,91 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
}
/**
- * Sets a static editor component for this column.
+ * Sets an editor binding for this column. The {@link Binding} is used
+ * when a row is in editor mode to define how to populate an editor
+ * component based on the edited row and how to update an item based on
+ * the value in the editor component.
* <p>
- * <strong>Note:</strong> The same component cannot be used for multiple
- * columns.
+ * To create a binding to use with a column, define a binding for the
+ * editor binder (<code>grid.getEditor().getBinder()</code>) using e.g.
+ * {@link Binder#forField(HasValue)}. You can also use
+ * {@link #setEditorComponent(HasValue, Setter)} if no validator or
+ * converter is needed for the binding.
+ * <p>
+ * The {@link HasValue} that the binding is defined to use must be a
+ * {@link Component}.
*
- * @param component
- * the editor component
+ * @param binding
+ * the binding to use for this column
* @return this column
*
+ * @see #setEditorComponent(HasValue, Setter)
+ * @see Binding
+ * @see Grid#getEditor()
* @see Editor#getBinder()
- * @see Editor#setBinder(Binder)
- * @see #setEditorComponentGenerator(EditorComponentGenerator)
*/
- public Column<T, V> setEditorComponent(Component component) {
- Objects.requireNonNull(component,
- "null is not a valid editor field");
- return setEditorComponentGenerator(t -> component);
+ public Column<T, V> setEditorBinding(Binding<T, ?> binding) {
+ Objects.requireNonNull(binding, "null is not a valid editor field");
+
+ if (!(binding.getField() instanceof Component)) {
+ throw new IllegalArgumentException(
+ "Binding target must be a component.");
+ }
+
+ this.editorBinding = binding;
+
+ return setEditable(true);
}
/**
- * Sets a component generator to provide an editor component for this
- * Column. This method can be used to generate any dynamic component to
- * be displayed in the editor row.
+ * Gets the binder binding that is currently used for this column.
+ *
+ * @return the used binder binding, or <code>null</code> if no binding
+ * is configured
+ *
+ * @see #setEditorBinding(Binding)
+ */
+ public Binding<T, ?> getEditorBinding() {
+ return editorBinding;
+ }
+
+ /**
+ * Sets a component and setter to use for editing values of this column
+ * in the editor row. This is a shorthand for use in simple cases where
+ * no validator or converter is needed. Use
+ * {@link #setEditorBinding(Binding)} to support more complex cases.
* <p>
* <strong>Note:</strong> The same component cannot be used for multiple
* columns.
*
- * @param componentGenerator
- * the editor component generator
+ * @param editorComponent
+ * the editor component
+ * @param setter
+ * a setter that stores the component value in the row item
* @return this column
*
- * @see EditorComponentGenerator
- * @see #setEditorComponent(Component)
+ * @see Grid#getEditor()
*/
- public Column<T, V> setEditorComponentGenerator(
- EditorComponentGenerator<T> componentGenerator) {
- Objects.requireNonNull(componentGenerator);
- this.componentGenerator = componentGenerator;
- return setEditable(true);
+ public <C extends HasValue<V> & Component> Column<T, V> setEditorComponent(
+ C editorComponent, Setter<T, V> setter) {
+ Objects.requireNonNull(editorComponent,
+ "Editor component cannot be null");
+ Objects.requireNonNull(setter, "Setter cannot be null");
+
+ Binding<T, V> binding = getGrid().getEditor().getBinder()
+ .bind(editorComponent, valueProvider::apply, setter);
+
+ return setEditorBinding(binding);
}
/**
- * Gets the editor component generator for this Column.
- *
- * @return editor component generator
+ * Gets the grid that this column belongs to.
*
- * @see EditorComponentGenerator
+ * @return the grid that this column belongs to, or <code>null</code> if
+ * this column has not yet been associated with any grid
*/
- public EditorComponentGenerator<T> getEditorComponentGenerator() {
- return componentGenerator;
+ protected Grid<T> getGrid() {
+ return getParent();
}
/**
@@ -1597,7 +1641,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* if the column is no longer attached to any grid
*/
protected void checkColumnIsAttached() throws IllegalStateException {
- if (getParent() == null) {
+ if (getGrid() == null) {
throw new IllegalStateException(
"Column is no longer attached to a grid.");
}
@@ -1621,7 +1665,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
ColumnState defaultState = new ColumnState();
if (getId() == null) {
- setId("column" + getParent().getColumns().indexOf(this));
+ setId("column" + getGrid().getColumns().indexOf(this));
}
DesignAttributeHandler.writeAttribute("column-id", attributes,
@@ -1672,6 +1716,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* @param designContext
* the design context
*/
+ @SuppressWarnings("unchecked")
protected void readDesign(Element design, DesignContext designContext) {
Attributes attributes = design.attributes();
@@ -1690,9 +1735,10 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
* inline data type. It will work incorrectly for other types
* but we don't support them anyway.
*/
- setEditorComponentGenerator(item -> new TextField(
- Optional.ofNullable(valueProvider.apply(item))
- .map(Object::toString).orElse("")));
+ setEditorComponent((HasValue<V> & Component) new TextField(),
+ (item, value) -> {
+ // Ignore user value since we don't know the setter
+ });
setEditable(DesignAttributeHandler.readAttribute("editable",
attributes, boolean.class));
}
@@ -1840,7 +1886,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
/**
* Creates a new {@code Grid} using the given caption
- *
+ *
* @param caption
* the caption of the grid
*/
@@ -1852,7 +1898,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
/**
* Creates a new {@code Grid} using the given caption and
* {@code DataProvider}
- *
+ *
* @param caption
* the caption of the grid
* @param dataProvider
@@ -1865,7 +1911,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
/**
* Creates a new {@code Grid} using the given {@code DataProvider}
- *
+ *
* @param dataProvider
* the data provider, not {@code null}
*/
@@ -1877,7 +1923,7 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
/**
* Creates a new {@code Grid} using the given caption and collection of
* items
- *
+ *
* @param caption
* the caption of the grid
* @param items
@@ -2833,9 +2879,8 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
*
*/
public void sort(Column<T, ?> column, SortDirection direction) {
- setSortOrder(
- Collections
- .singletonList(new GridSortOrder<>(column, direction)));
+ setSortOrder(Collections
+ .singletonList(new GridSortOrder<>(column, direction)));
}
/**
@@ -3287,9 +3332,9 @@ public class Grid<T> extends AbstractListing<T> implements HasComponents,
protected SerializableComparator<T> createSortingComparator() {
BinaryOperator<SerializableComparator<T>> operator = (comparator1,
- comparator2) -> SerializableComparator
- .asInstance((Comparator<T> & Serializable) comparator1
- .thenComparing(comparator2));
+ comparator2) -> SerializableComparator
+ .asInstance((Comparator<T> & Serializable) comparator1
+ .thenComparing(comparator2));
return sortOrder.stream().map(
order -> order.getSorted().getComparator(order.getDirection()))
.reduce((x, y) -> 0, operator);
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/EditorComponentGenerator.java b/server/src/main/java/com/vaadin/ui/components/grid/EditorComponentGenerator.java
deleted file mode 100644
index b33c59a891..0000000000
--- a/server/src/main/java/com/vaadin/ui/components/grid/EditorComponentGenerator.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.vaadin.ui.components.grid;
-
-import com.vaadin.server.SerializableFunction;
-import com.vaadin.ui.Component;
-
-/**
- * A callback interface for generating an editor component corresponding to an
- * editable column of a grid. The generated component will be used in the grid
- * editor to edit the value of the column for the selected grid row.
- *
- * @author Vaadin Ltd.
- * @since 8.0
- *
- * @param <BEAN>
- * the bean type this generator is compatible with
- */
-@FunctionalInterface
-public interface EditorComponentGenerator<BEAN>
- extends SerializableFunction<BEAN, Component> {
-
- /**
- * Gets a component for a given {@code bean}.
- *
- * @param bean
- * the bean this component will be used to edit
- * @return the generated component
- */
- @Override
- public Component apply(BEAN bean);
-}
diff --git a/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java b/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java
index 56bf4d6414..a001a5026a 100644
--- a/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java
+++ b/server/src/main/java/com/vaadin/ui/components/grid/EditorImpl.java
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.vaadin.data.Binder;
+import com.vaadin.data.Binder.Binding;
import com.vaadin.data.BinderValidationStatus;
import com.vaadin.data.BinderValidationStatusHandler;
import com.vaadin.shared.ui.grid.editor.EditorClientRpc;
@@ -216,8 +217,12 @@ public class EditorImpl<T> extends AbstractGridExtension<T>
getParent().getColumns().stream().filter(Column::isEditable)
.forEach(c -> {
- Component component = c.getEditorComponentGenerator()
- .apply(edited);
+ Binding<T, ?> binding = c.getEditorBinding();
+
+ assert binding
+ .getField() instanceof Component : "Grid should enforce that the binding field is a component";
+
+ Component component = (Component) binding.getField();
addComponentToGrid(component);
columnFields.put(c, component);
getState().columnFields.put(getInternalIdForColumn(c),
diff --git a/server/src/test/java/com/vaadin/tests/server/component/grid/GridDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/grid/GridDeclarativeTest.java
index 5cbf5eb8b1..a24d4dac15 100644
--- a/server/src/test/java/com/vaadin/tests/server/component/grid/GridDeclarativeTest.java
+++ b/server/src/test/java/com/vaadin/tests/server/component/grid/GridDeclarativeTest.java
@@ -37,6 +37,7 @@ import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.Column;
import com.vaadin.ui.Grid.SelectionMode;
import com.vaadin.ui.Label;
+import com.vaadin.ui.TextField;
import com.vaadin.ui.components.grid.FooterCell;
import com.vaadin.ui.components.grid.FooterRow;
import com.vaadin.ui.components.grid.HeaderCell;
@@ -175,7 +176,7 @@ public class GridDeclarativeTest extends AbstractListingDeclarativeTest<Grid> {
boolean sortable = false;
column1.setSortable(sortable);
boolean editable = true;
- column1.setEditorComponentGenerator(component -> null);
+ column1.setEditorComponent(new TextField(), Person::setLastName);
column1.setEditable(editable);
boolean resizable = false;
column1.setResizable(resizable);
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java
index a09aba038c..0459294fe5 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.List;
import com.vaadin.data.Binder;
+import com.vaadin.data.Binder.Binding;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.CheckBox;
@@ -41,21 +42,21 @@ public class GridCheckBoxDisplay extends AbstractReindeerTestUI {
TextField taskField = new TextField();
CheckBox doneField = new CheckBox();
- Binder<Todo> binder = new Binder<>();
- binder.bind(taskField, Todo::getTask, Todo::setTask);
- binder.bind(doneField, Todo::isDone, Todo::setDone);
+ Binder<Todo> binder = grid.getEditor().getBinder();
- grid.getEditor().setBinder(binder);
- grid.getEditor().setEnabled(true);
+ Binding<Todo, Boolean> doneBinding = binder.bind(doneField,
+ Todo::isDone, Todo::setDone);
Column<Todo, String> column = grid
.addColumn(todo -> String.valueOf(todo.isDone()));
column.setWidth(75);
- column.setEditorComponent(doneField);
+ column.setEditorBinding(doneBinding);
grid.addColumn(Todo::getTask).setExpandRatio(1)
- .setEditorComponent(taskField);
+ .setEditorComponent(taskField, Todo::setTask);
+
+ grid.getEditor().setEnabled(true);
grid.setSelectionMode(Grid.SelectionMode.SINGLE);
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditingWithNoScrollBars.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditingWithNoScrollBars.java
index 203a69b1f0..d637151986 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditingWithNoScrollBars.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditingWithNoScrollBars.java
@@ -41,7 +41,7 @@ public class GridEditingWithNoScrollBars extends AbstractTestUI {
stCombo.setEmptySelectionAllowed(false);
stCombo.setSizeFull();
- column.setEditorComponent(stCombo);
+ column.setEditorComponent(stCombo, Person::setLastName);
grid.setSelectionMode(SelectionMode.SINGLE);
grid.getEditor().setEnabled(true);
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java
index 40f8695ece..e72280860c 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorCustomField.java
@@ -15,11 +15,7 @@
*/
package com.vaadin.tests.components.grid;
-import java.util.HashSet;
-import java.util.Set;
-
import com.vaadin.annotations.Theme;
-import com.vaadin.data.Binder;
import com.vaadin.data.provider.ListDataProvider;
import com.vaadin.data.provider.Query;
import com.vaadin.server.VaadinRequest;
@@ -42,52 +38,36 @@ public class GridEditorCustomField extends AbstractTestUIWithLog {
@Override
protected void setup(VaadinRequest request) {
- Grid<ComplexPerson> grid = createGrid();
-
ListDataProvider<ComplexPerson> dataProvider = ComplexPerson
.createDataProvider(100);
- grid.setDataProvider(dataProvider);
-
- Set<String> cities = new HashSet<>();
- dataProvider.fetch(new Query<>()).forEach(person -> {
- cities.add(person.getAddress().getCity());
- });
- CustomCitySelect cityEditor = new CustomCitySelect(
- cities.toArray(new String[cities.size()]));
+ String[] cities = dataProvider.fetch(new Query<>())
+ .map(person -> person.getAddress().getCity()).distinct()
+ .toArray(String[]::new);
+ CustomCitySelect cityEditor = new CustomCitySelect(cities);
TextField firstNameField = new TextField();
TextField lastNameField = new TextField();
- Binder<ComplexPerson> binder = new Binder<>();
- binder.bind(firstNameField, ComplexPerson::getFirstName,
- ComplexPerson::setFirstName);
- binder.bind(lastNameField, ComplexPerson::getLastName,
- ComplexPerson::setLastName);
- binder.bind(cityEditor, person -> person.getAddress().getCity(),
- (person, city) -> person.getAddress().setCity(city));
-
- grid.getEditor().setBinder(binder);
- grid.getColumn(ADDRESS_CITY_IDENTIFIER).setEditorComponent(cityEditor);
- grid.getColumn(FIRST_NAME_IDENTIFIER)
- .setEditorComponent(firstNameField);
- grid.getColumn(LAST_NAME_IDENTIFIER).setEditorComponent(lastNameField);
-
- addComponent(grid);
- }
-
- private Grid<ComplexPerson> createGrid() {
Grid<ComplexPerson> grid = new Grid<>();
grid.setWidth("800px");
grid.addColumn(person -> person.getFirstName())
- .setId(FIRST_NAME_IDENTIFIER).setCaption("First Name");
+ .setId(FIRST_NAME_IDENTIFIER).setCaption("First Name")
+ .setEditorComponent(firstNameField,
+ ComplexPerson::setFirstName);
grid.addColumn(person -> person.getLastName())
- .setId(LAST_NAME_IDENTIFIER).setCaption("Last Name");
+ .setId(LAST_NAME_IDENTIFIER).setCaption("Last Name")
+ .setEditorComponent(lastNameField, ComplexPerson::setLastName);
grid.addColumn(person -> person.getAddress().getCity())
- .setId(ADDRESS_CITY_IDENTIFIER).setCaption("City Name");
+ .setId(ADDRESS_CITY_IDENTIFIER).setCaption("City Name")
+ .setEditorComponent(cityEditor,
+ (person, city) -> person.getAddress().setCity(city));
+
grid.getEditor().setEnabled(true);
- return grid;
+ grid.setDataProvider(dataProvider);
+
+ addComponent(grid);
}
public static class CustomCitySelect extends CustomField<String> {
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorMultiselect.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorMultiselect.java
index dbeb1bbc8a..b216ef9fbf 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorMultiselect.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorMultiselect.java
@@ -3,6 +3,7 @@ package com.vaadin.tests.components.grid;
import java.util.stream.IntStream;
import com.vaadin.data.Binder;
+import com.vaadin.data.Binder.Binding;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
@@ -24,17 +25,14 @@ public class GridEditorMultiselect extends AbstractTestUI {
.addColumn(Person::getAge, new NumberRenderer())
.setCaption("age");
- Binder<Person> binder = new Binder<>();
- grid.getEditor().setBinder(binder);
+ Binder<Person> binder = grid.getEditor().getBinder();
- TextField name = new TextField();
- nameColumn.setEditorComponent(name);
- binder.bind(name, Person::getFirstName, Person::setFirstName);
+ nameColumn.setEditorComponent(new TextField(), Person::setFirstName);
- TextField age = new TextField();
- ageColumn.setEditorComponent(age);
- binder.forField(age).withConverter(new StringToIntegerConverter(""))
+ Binding<Person, Integer> ageBinding = binder.forField(new TextField())
+ .withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);
+ ageColumn.setEditorBinding(ageBinding);
grid.setItems(IntStream.range(0, 30).mapToObj(this::createPerson));
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorUI.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorUI.java
index bf8b997f80..d2d865498e 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorUI.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditorUI.java
@@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
-import com.vaadin.data.Binder;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.tests.util.Person;
@@ -67,9 +66,6 @@ public class GridEditorUI extends AbstractTestUI {
protected Grid<Person> createGrid() {
Grid<Person> grid = new Grid<>();
- Binder<Person> binder = new Binder<>();
- grid.getEditor().setBinder(binder);
-
grid.addColumn(Person::getEmail).setCaption("Email");
Column<Person, String> fistNameColumn = grid
.addColumn(Person::getFirstName).setCaption("First Name");
@@ -88,19 +84,15 @@ public class GridEditorUI extends AbstractTestUI {
grid.getEditor().setEnabled(true);
PasswordField passwordField = new PasswordField();
- fistNameColumn.setEditorComponent(passwordField);
- binder.bind(passwordField, Person::getFirstName, Person::setFirstName);
+ fistNameColumn.setEditorComponent(passwordField, Person::setFirstName);
TextField lastNameEditor = new TextField();
- lastNameColumn.setEditorComponent(lastNameEditor);
+ lastNameColumn.setEditorComponent(lastNameEditor, Person::setLastName);
lastNameEditor.setMaxLength(50);
- binder.bind(lastNameEditor, Person::getLastName, Person::setLastName);
TextField phoneEditor = new TextField();
phoneEditor.setReadOnly(true);
- phoneColumn.setEditorComponent(phoneEditor);
- binder.bind(phoneEditor, Person::getPhoneNumber,
- Person::setPhoneNumber);
+ phoneColumn.setEditorComponent(phoneEditor, Person::setPhoneNumber);
return grid;
}
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java
index 208f91d71d..eabf281bbd 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java
@@ -16,6 +16,7 @@ import java.util.stream.Stream;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Widgetset;
import com.vaadin.data.Binder;
+import com.vaadin.data.Binder.Binding;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.event.selection.MultiSelectionEvent;
import com.vaadin.event.selection.SingleSelectionEvent;
@@ -199,38 +200,40 @@ public class GridBasics extends AbstractTestUIWithLog {
TextField coordinates = new TextField();
TextField rowNumber = new TextField();
- binder.bind(html, DataObject::getHtmlString, DataObject::setHtmlString);
- binder.forField(smallRandom)
+ Binding<DataObject, Integer> smallRandomBinding = binder
+ .forField(smallRandom)
.withConverter(new StringToIntegerConverter(
"Could not convert value to Integer"))
.withValidator(i -> i >= 0 && i < 5,
"Small random needs to be in range [0..5)")
.bind(DataObject::getSmallRandom, DataObject::setSmallRandom);
- binder.bind(coordinates, DataObject::getCoordinates,
- DataObject::setCoordinates);
- binder.forField(rowNumber)
+ Binding<DataObject, Integer> rowNumberBinding = binder
+ .forField(rowNumber)
.withConverter(new StringToIntegerConverter(
"Could not convert value to Integer"))
.bind(DataObject::getRowNumber, DataObject::setRowNumber);
grid.addColumn(DataObject::getCoordinates)
- .setCaption(COLUMN_CAPTIONS[0]).setEditorComponent(coordinates);
+ .setCaption(COLUMN_CAPTIONS[0])
+ .setEditorComponent(coordinates, DataObject::setCoordinates);
grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 1)")
.setCaption(COLUMN_CAPTIONS[1]);
grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 2)")
.setCaption(COLUMN_CAPTIONS[2]);
grid.addColumn(DataObject::getRowNumber, new NumberRenderer())
- .setCaption(COLUMN_CAPTIONS[3]).setEditorComponent(rowNumber);
+ .setCaption(COLUMN_CAPTIONS[3])
+ .setEditorBinding(rowNumberBinding);
grid.addColumn(DataObject::getDate, new DateRenderer())
.setCaption(COLUMN_CAPTIONS[4]);
grid.addColumn(DataObject::getHtmlString, new HtmlRenderer())
- .setCaption(COLUMN_CAPTIONS[5]).setEditorComponent(html);
+ .setCaption(COLUMN_CAPTIONS[5])
+ .setEditorComponent(html, DataObject::setHtmlString);
grid.addColumn(DataObject::getBigRandom, new NumberRenderer())
.setCaption(COLUMN_CAPTIONS[6]);
grid.addColumn(data -> data.getSmallRandom() / 5d,
new ProgressBarRenderer()).setCaption(COLUMN_CAPTIONS[7])
- .setEditorComponent(smallRandom);
+ .setEditorBinding(smallRandomBinding);
selectionListenerRegistration = ((SingleSelectionModelImpl<DataObject>) grid
.getSelectionModel())