diff options
author | Henrik Paul <henrik@vaadin.com> | 2015-02-05 23:50:31 +0200 |
---|---|---|
committer | Leif Åstrand <leif@vaadin.com> | 2015-02-06 11:19:17 +0200 |
commit | 0e141e31bb30a0ab6726129f3c9fa892c92573e4 (patch) | |
tree | 672975bd0671b06d1498e5ce5f984e7d30179ef7 /client | |
parent | 0c82dad0ab225aeb9920b2e5c6f061da871bea66 (diff) | |
download | vaadin-framework-0e141e31bb30a0ab6726129f3c9fa892c92573e4.tar.gz vaadin-framework-0e141e31bb30a0ab6726129f3c9fa892c92573e4.zip |
Highlights erroneous cells in Grid editor (#16575)
Change-Id: Ie1f9d738db7a03ddb01b968782ad5e4877af1d7e
Diffstat (limited to 'client')
3 files changed, 192 insertions, 115 deletions
diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index 8d383ab0ae..60a730c80e 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -18,6 +18,7 @@ package com.vaadin.client.connectors; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -202,7 +203,7 @@ public class GridConnector extends AbstractHasComponentsConnector implements private EditorServerRpc rpc = getRpcProxy(EditorServerRpc.class); - private EditorRequest<?> currentRequest = null; + private EditorRequest<JsonObject> currentRequest = null; private boolean serverInitiated = false; public CustomEditorHandler() { @@ -227,12 +228,13 @@ public class GridConnector extends AbstractHasComponentsConnector implements @Override public void confirmBind(final boolean bindSucceeded) { - endRequest(bindSucceeded); + endRequest(bindSucceeded, null); } @Override - public void confirmSave(boolean saveSucceeded) { - endRequest(saveSucceeded); + public void confirmSave(boolean saveSucceeded, + List<String> errorColumnsIds) { + endRequest(saveSucceeded, errorColumnsIds); } }); } @@ -295,24 +297,34 @@ public class GridConnector extends AbstractHasComponentsConnector implements } } - private void startRequest(EditorRequest<?> request) { + private void startRequest(EditorRequest<JsonObject> request) { assert currentRequest == null : "Earlier request not yet finished"; currentRequest = request; } - private void endRequest(boolean succeeded) { + private void endRequest(boolean succeeded, List<String> errorColumnsIds) { assert currentRequest != null : "Current request was null"; /* * Clear current request first to ensure the state is valid if * another request is made in the callback. */ - EditorRequest<?> request = currentRequest; + EditorRequest<JsonObject> request = currentRequest; currentRequest = null; if (succeeded) { request.success(); } else { - request.fail(); + Collection<Column<?, JsonObject>> errorColumns; + if (errorColumnsIds != null) { + errorColumns = new ArrayList<Grid.Column<?, JsonObject>>(); + for (String colId : errorColumnsIds) { + errorColumns.add(columnIdToColumn.get(colId)); + } + } else { + errorColumns = null; + } + + request.failure(errorColumns); } } } diff --git a/client/src/com/vaadin/client/widget/grid/EditorHandler.java b/client/src/com/vaadin/client/widget/grid/EditorHandler.java index 07ec1b231c..1d152c708c 100644 --- a/client/src/com/vaadin/client/widget/grid/EditorHandler.java +++ b/client/src/com/vaadin/client/widget/grid/EditorHandler.java @@ -15,6 +15,8 @@ */ package com.vaadin.client.widget.grid; +import java.util.Collection; + import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.widgets.Grid; @@ -43,82 +45,27 @@ public interface EditorHandler<T> { * @param <T> * the row data type */ - public static class EditorRequest<T> { - - /** - * A callback interface used to notify the invoker of the editor handler - * of completed editor requests. - * - * @param <T> - * the row data type - */ - public interface RequestCallback<T> { - /** - * The method that must be called when the request has been - * processed correctly. - * - * @param request - * the original request object - */ - public void onSuccess(EditorRequest<T> request); - - /** - * The method that must be called when processing the request has - * produced an aborting error. - * - * @param request - * the original request object - */ - public void onError(EditorRequest<T> request); - } - - private Grid<T> grid; - private int rowIndex; - private RequestCallback<T> callback; - private boolean completed = false; - - /** - * Creates a new editor request. - * - * @param rowIndex - * the index of the edited row - * @param callback - * the callback invoked when the request is ready, or null if - * no need to call back - */ - public EditorRequest(Grid<T> grid, int rowIndex, - RequestCallback<T> callback) { - this.grid = grid; - this.rowIndex = rowIndex; - this.callback = callback; - } - + public interface EditorRequest<T> { /** * Returns the index of the row being requested. * * @return the row index */ - public int getRowIndex() { - return rowIndex; - } + public int getRowIndex(); /** * Returns the row data related to the row being requested. * * @return the row data */ - public T getRow() { - return grid.getDataSource().getRow(rowIndex); - } + public T getRow(); /** * Returns the grid instance related to this editor request. * * @return the grid instance */ - public Grid<T> getGrid() { - return grid; - } + public Grid<T> getGrid(); /** * Returns the editor widget used to edit the values of the given @@ -128,59 +75,30 @@ public interface EditorHandler<T> { * the column whose widget to get * @return the widget related to the column */ - public Widget getWidget(Grid.Column<?, T> column) { - Widget w = grid.getEditorWidget(column); - assert w != null; - return w; - } - - /** - * Completes this request. The request can only be completed once. This - * method should only be called by an EditorHandler implementer if the - * request handling is asynchronous in nature and {@link #startAsync()} - * is previously invoked for this request. Synchronous requests are - * completed automatically by the editor. - * - * @throws IllegalStateException - * if the request is already completed - */ - private void complete() { - if (completed) { - throw new IllegalStateException( - "An EditorRequest must be completed exactly once"); - } - completed = true; - } + public Widget getWidget(Grid.Column<?, T> column); /** * Informs Grid that the editor request was a success. */ - public void success() { - complete(); - if (callback != null) { - callback.onSuccess(this); - } - } + public void success(); /** * Informs Grid that an error occurred while trying to process the * request. + * + * @param errorColumns + * a collection of columns for which an error indicator + * should be shown, or <code>null</code> if no columns should + * be marked as erroneous. */ - public void fail() { - complete(); - if (callback != null) { - callback.onError(this); - } - } + public void failure(Collection<Grid.Column<?, T>> errorColumns); /** * Checks whether the request is completed or not. * * @return <code>true</code> iff the request is completed */ - public boolean isCompleted() { - return completed; - } + public boolean isCompleted(); } /** @@ -188,8 +106,9 @@ public interface EditorHandler<T> { * opened for editing. * <p> * The implementation <em>must</em> call either - * {@link EditorRequest#success()} or {@link EditorRequest#fail()} to signal - * a successful or a failed (respectively) bind action. + * {@link EditorRequest#success()} or + * {@link EditorRequest#failure(Collection)} to signal a successful or a + * failed (respectively) bind action. * * @param request * the data binding request diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index fb7ec3abb5..0f3ffc696a 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -83,7 +83,6 @@ import com.vaadin.client.widget.grid.DataAvailableEvent; import com.vaadin.client.widget.grid.DataAvailableHandler; import com.vaadin.client.widget.grid.EditorHandler; import com.vaadin.client.widget.grid.EditorHandler.EditorRequest; -import com.vaadin.client.widget.grid.EditorHandler.EditorRequest.RequestCallback; import com.vaadin.client.widget.grid.EventCellReference; import com.vaadin.client.widget.grid.RendererCellReference; import com.vaadin.client.widget.grid.RowReference; @@ -936,6 +935,106 @@ public class Grid<T> extends ResizeComposite implements } } + private static class EditorRequestImpl<T> implements EditorRequest<T> { + + /** + * A callback interface used to notify the invoker of the editor handler + * of completed editor requests. + * + * @param <T> + * the row data type + */ + public static interface RequestCallback<T> { + /** + * The method that must be called when the request has been + * processed correctly. + * + * @param request + * the original request object + */ + public void onSuccess(EditorRequest<T> request); + + /** + * The method that must be called when processing the request has + * produced an aborting error. + * + * @param request + * the original request object + */ + public void onError(EditorRequest<T> request); + } + + private Grid<T> grid; + private int rowIndex; + private RequestCallback<T> callback; + private boolean completed = false; + + public EditorRequestImpl(Grid<T> grid, int rowIndex, + RequestCallback<T> callback) { + this.grid = grid; + this.rowIndex = rowIndex; + this.callback = callback; + } + + @Override + public int getRowIndex() { + return rowIndex; + } + + @Override + public T getRow() { + return grid.getDataSource().getRow(rowIndex); + } + + @Override + public Grid<T> getGrid() { + return grid; + } + + @Override + public Widget getWidget(Grid.Column<?, T> column) { + Widget w = grid.getEditorWidget(column); + assert w != null; + return w; + } + + private void complete(Collection<Column<?, T>> errorColumns) { + if (completed) { + throw new IllegalStateException( + "An EditorRequest must be completed exactly once"); + } + completed = true; + + grid.getEditor().clearEditorColumnErrors(); + if (errorColumns != null) { + for (Column<?, T> column : errorColumns) { + grid.getEditor().setEditorColumnError(column, true); + } + } + } + + @Override + public void success() { + complete(null); + if (callback != null) { + callback.onSuccess(this); + } + } + + @Override + public void failure(Collection<Grid.Column<?, T>> errorColumns) { + complete(errorColumns); + if (callback != null) { + callback.onError(this); + } + } + + @Override + public boolean isCompleted() { + return completed; + } + } + /** * An editor UI for Grid rows. A single Grid row at a time can be opened for * editing. @@ -945,6 +1044,8 @@ public class Grid<T> extends ResizeComposite implements public static final int KEYCODE_SHOW = KeyCodes.KEY_ENTER; public static final int KEYCODE_HIDE = KeyCodes.KEY_ESCAPE; + private static final String ERROR_CLASS_NAME = "error"; + protected enum State { INACTIVE, ACTIVATING, BINDING, ACTIVE, SAVING } @@ -988,7 +1089,7 @@ public class Grid<T> extends ResizeComposite implements } }; - private final RequestCallback<T> saveRequestCallback = new RequestCallback<T>() { + private final EditorRequestImpl.RequestCallback<T> saveRequestCallback = new EditorRequestImpl.RequestCallback<T>() { @Override public void onSuccess(EditorRequest<T> request) { if (state == State.SAVING) { @@ -1027,7 +1128,7 @@ public class Grid<T> extends ResizeComposite implements + " remember to call success() or fail()?"); } }; - private final RequestCallback<T> bindRequestCallback = new RequestCallback<T>() { + private final EditorRequestImpl.RequestCallback<T> bindRequestCallback = new EditorRequestImpl.RequestCallback<T>() { @Override public void onSuccess(EditorRequest<T> request) { if (state == State.BINDING) { @@ -1055,6 +1156,9 @@ public class Grid<T> extends ResizeComposite implements } }; + /** A set of all the columns that display an error flag. */ + private final Set<Column<?, T>> columnErrors = new HashSet<Grid.Column<?, T>>(); + public Editor() { saveButton = new Button(); saveButton.setText(GridConstants.DEFAULT_SAVE_CAPTION); @@ -1132,7 +1236,7 @@ public class Grid<T> extends ResizeComposite implements hideOverlay(); grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); - EditorRequest<T> request = new EditorRequest<T>(grid, rowIndex, + EditorRequest<T> request = new EditorRequestImpl<T>(grid, rowIndex, null); handler.cancel(request); state = State.INACTIVE; @@ -1159,7 +1263,7 @@ public class Grid<T> extends ResizeComposite implements state = State.SAVING; setButtonsEnabled(false); saveTimeout.schedule(SAVE_TIMEOUT_MS); - EditorRequest<T> request = new EditorRequest<T>(grid, rowIndex, + EditorRequest<T> request = new EditorRequestImpl<T>(grid, rowIndex, saveRequestCallback); handler.save(request); } @@ -1222,8 +1326,8 @@ public class Grid<T> extends ResizeComposite implements if (state == State.ACTIVATING) { state = State.BINDING; bindTimeout.schedule(BIND_TIMEOUT_MS); - EditorRequest<T> request = new EditorRequest<T>(grid, rowIndex, - bindRequestCallback); + EditorRequest<T> request = new EditorRequestImpl<T>(grid, + rowIndex, bindRequestCallback); handler.bind(request); grid.getEscalator().setScrollLocked(Direction.VERTICAL, true); } @@ -1372,6 +1476,8 @@ public class Grid<T> extends ResizeComposite implements editorOverlay.removeFromParent(); scrollHandler.removeHandler(); + + clearEditorColumnErrors(); } protected void setStylePrimaryName(String primaryName) { @@ -1480,6 +1586,46 @@ public class Grid<T> extends ResizeComposite implements public String getCancelCaption() { return cancelButton.getText(); } + + public void setEditorColumnError(Column<?, T> column, boolean hasError) { + if (state != State.ACTIVE && state != State.SAVING) { + throw new IllegalStateException("Cannot set cell error " + + "status: editor is neither active nor saving."); + } + + if (isEditorColumnError(column) == hasError) { + return; + } + + Element editorCell = getWidget(column).getElement() + .getParentElement(); + if (hasError) { + editorCell.addClassName(ERROR_CLASS_NAME); + columnErrors.add(column); + } else { + editorCell.removeClassName(ERROR_CLASS_NAME); + columnErrors.remove(column); + } + } + + public void clearEditorColumnErrors() { + + /* + * editorOverlay has no children if it's not active, effectively + * making this loop a NOOP. + */ + Element e = editorOverlay.getFirstChildElement(); + while (e != null) { + e.removeClassName(ERROR_CLASS_NAME); + e = e.getNextSiblingElement(); + } + + columnErrors.clear(); + } + + public boolean isEditorColumnError(Column<?, T> column) { + return columnErrors.contains(column); + } } public static abstract class AbstractGridKeyEvent<HANDLER extends AbstractGridKeyEventHandler> @@ -3215,7 +3361,7 @@ public class Grid<T> extends ResizeComposite implements * * @return {@code true} if this column is editable, {@code false} * otherwise - * + * * @see #setEditable(boolean) */ public boolean isEditable() { |