vaadin / flow-components

Java counterpart of Vaadin Web Components
100 stars 66 forks source link

Adding new items to editable grid fails when more items than page size. #1223

Open stefanuebe opened 5 years ago

stefanuebe commented 5 years ago

Following scenario:

The view contains an editable grid with some hundred items (page size is default). Per button the user can add a new item into the grid and edit it.

This leads to an exception, when there are more items then page size. It works, when the item amount is below page size or lazy loading is not used (e.g. by using height-by-rows).

I guess this is related to the lazy loading mechanism.

Exception

[qtp709931821-22] ERROR com.vaadin.flow.server.DefaultErrorHandler - 
java.lang.IllegalStateException: The item SimpleBean{a='null', b='null', c='null'} is not in the backing data provider
    at com.vaadin.flow.component.grid.editor.EditorImpl.validate(EditorImpl.java:217)
    at com.vaadin.flow.component.grid.editor.EditorImpl.requestEditItem(EditorImpl.java:145)
    at com.vaadin.flow.component.grid.editor.EditorImpl.lambda$editItem$2d5ea2ce$1(EditorImpl.java:136)
    at com.vaadin.flow.internal.StateTree.lambda$runExecutionsBeforeClientResponse$1(StateTree.java:350)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at com.vaadin.flow.internal.StateTree.runExecutionsBeforeClientResponse(StateTree.java:347)
    at com.vaadin.flow.server.communication.UidlWriter.encodeChanges(UidlWriter.java:392)
    at com.vaadin.flow.server.communication.UidlWriter.createUidl(UidlWriter.java:182)
    at com.vaadin.flow.server.communication.UidlRequestHandler.writeUidl(UidlRequestHandler.java:116)
    at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:89)
    at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
    at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1540)
    at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:246)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
...

Example code:

package org.vaadin.issue;

import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.editor.Editor;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.router.Route;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Route(value = "")
public class GridLLAddView extends VerticalLayout {

    private SimpleBean simpleBean = new SimpleBean();
    private Grid<SimpleBean> grid;
    private Editor<SimpleBean> editor;
    private List<SimpleBean> items;

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        items = new ArrayList<>();

        grid = new Grid<>();
        editor = grid.getEditor();
        editor.setBuffered(true);

        Binder<SimpleBean> binder = new Binder<>();
        editor.setBinder(binder);

        TextField aField = new TextField();
        TextField bField = new TextField();
        TextField cField = new TextField();

        binder.bind(aField, SimpleBean::getA, SimpleBean::setA);
        binder.bind(bField, SimpleBean::getB, SimpleBean::setB);
        binder.bind(cField, SimpleBean::getC, SimpleBean::setC);

        grid.addComponentColumn(bean -> new Div(
                new Button(VaadinIcon.EDIT.create(), event -> editItem(bean)),
                new Button(VaadinIcon.TRASH.create(), event -> deleteItem(bean))))
                .setEditorComponent(new HorizontalLayout(
                        new Button(VaadinIcon.CHECK.create(), event2 -> editor.save()),
                        new Button(VaadinIcon.CLOSE.create(), event2 -> editor.cancel())));
        grid.addColumn(SimpleBean::getA).setHeader("A").setEditorComponent(aField);
        grid.addColumn(SimpleBean::getB).setHeader("B").setEditorComponent(bField);
        grid.addColumn(SimpleBean::getC).setHeader("C").setEditorComponent(cField);

        for (int i = 0; i < 200; i++) {
            items.add(new SimpleBean("a" + i, "b" + i, "c" + i));
        }
        grid.setItems(items);

        Button open = new Button("Add", event -> {
            SimpleBean bean = new SimpleBean();

            items.add(bean);
            grid.getDataProvider().refreshAll();

            editItem(bean);
        });

        add(open, grid);
    }

    private void editItem(SimpleBean bean) {
        if (editor.isOpen()) {
            editor.cancel();
        }

        editor.editItem(bean);
    }

    private void deleteItem(SimpleBean bean) {
        items.remove(bean);
        grid.getDataProvider().refreshAll();
    }

    private static class SimpleBean {
        private static long ids; // for test purposes
        private long id;
        private String a;
        private String b;
        private String c;

        public SimpleBean() {
            this(null, null, null);
        }

        public SimpleBean(String a, String b, String c) {
            this.id = ids++;
            this.a = a;
            this.b = b;
            this.c = c;
        }

       // ... getters / setters / toString omitted

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof SimpleBean)) return false;
            SimpleBean that = (SimpleBean) o;
            return id == that.id;
        }

        @Override
        public int hashCode() {
            return Objects.hash(id);
        }
    }
}
fleahy commented 4 years ago

I'm getting the same error on a grid with Vaadin 14.1.17 (vaadin grid flow 4.1.0).

paodb commented 3 years ago

Issue still can be reproduced in latest LTS version, 14.6.8.

johannest commented 3 years ago

Workaround: call grid.getDataCommunicator().getKeyMapper().key(bean); before editItem(bean);

See e.g. https://github.com/johannest/gridtestv14/blob/main/src/main/java/com/example/application/views/test/TestView.java#L78