java - 带有 Activity 编辑器的 Vaadin 双击网格上的按钮会引发异常 ("java.lang.NullPointerException: Editor can' t edit null")

标签 java vaadin vaadin8 vaadin-grid

我有一个带有 Activity 缓冲编辑器的 vaadin 网格。默认情况下,双击一行时会打开编辑器。一切正常,除非我双击按钮时出现异常:(异常未指向我的代码中的任何位置)

java.lang.NullPointerException: Editor can't edit null
at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at com.vaadin.ui.components.grid.EditorImpl.doEdit(EditorImpl.java:216)
at com.vaadin.ui.components.grid.EditorImpl$1.bind(EditorImpl.java:151)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:155)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:116)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:445)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:410)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
at com.vaadin.server.communication.PushHandler.lambda$new$1(PushHandler.java:145)
at com.vaadin.server.communication.PushHandler.callWithUi(PushHandler.java:235)
at com.vaadin.server.communication.PushHandler.onMessage(PushHandler.java:520)
at com.vaadin.server.communication.PushAtmosphereHandler.onMessage(PushAtmosphereHandler.java:87)
at com.vaadin.server.communication.PushAtmosphereHandler.onRequest(PushAtmosphereHandler.java:77)
at org.atmosphere.cpr.AsynchronousProcessor.action(AsynchronousProcessor.java:223)
at org.atmosphere.cpr.AsynchronousProcessor.suspended(AsynchronousProcessor.java:115)
at org.atmosphere.container.Servlet30CometSupport.service(Servlet30CometSupport.java:67)
at org.atmosphere.cpr.AtmosphereFramework.doCometSupport(AtmosphereFramework.java:2284)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:593)
at org.atmosphere.websocket.DefaultWebSocketProcessor$3.run(DefaultWebSocketProcessor.java:345)
at org.atmosphere.util.VoidExecutorService.execute(VoidExecutorService.java:101)
at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:340)
at org.atmosphere.websocket.DefaultWebSocketProcessor.invokeWebSocketProtocol(DefaultWebSocketProcessor.java:447)
at org.atmosphere.container.JSR356Endpoint$3.onMessage(JSR356Endpoint.java:272)
at org.atmosphere.container.JSR356Endpoint$3.onMessage(JSR356Endpoint.java:269)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:395)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:119)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:495)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:294)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:844)

这就是我将 Button 添加到网格的方式:
// Adding the column
    grid.addComponentColumn(this::buildAddButton);

这是返回每一行按钮的方法:
// Building the button
private Button buildAddButton(ProductTemplate p) {
    Button button = new Button("add");
    // Configurate the button
    ...
    return button;
}

不幸的是,我对这个问题的发现很少......
由于 vaadin 按钮不会停止点击事件传播,我试过这个:
  • 通过从布局中删除单击监听器,将按钮添加到布局以“阻止”单击事件。
    // Adding the column 
        grid.addComponentColumn(this::buildAddLayout);
    

    构建一个布局而不仅仅是一个按钮:
    // Building the layout with the button
    private VerticalLayout buildAddLayout(ProductTemplate p) {
        Button button = new Button("add");
    
        // Configurate the button
        ...
    
        VerticalLayout layout = new VerticalLayout();
        layout.addComponent(button);
        layout.getListeners(Event.class).clear();
    
        return layout;
    }
    
  • 单击时禁用按钮,直到它的任务是
    完成(禁止双击按钮)。
    // Building the button
    private Button buildAddButton(ProductTemplate p) {
        Button button = new Button("add");
        // Configurate the button
            ...
        button.addClickListener(e -> {
            button.setEnabled(false);
            buttonClicked(p);
            button.setEnabled(true);
        });        
        return button;
    }
    

  • 两者都没有让我能够摆脱异常。在启用编辑器的网格上双击按钮时,有什么建议,如何防止这种类型的异常?

    编辑:编辑器正在尝试编辑按钮的值,这显然是不可能的。我想阻止编辑这样做。 (“can't edit null”表示 vaadin 无法从按钮中创建有效的 bean)

    编辑 2:我之前在第一次编辑中做出的假设似乎是错误的。我的按钮点击正在刷新网格,我无法阻止编辑器尝试编辑一行,即使所有项目都被删除并重新加载。

    这里有一个类和一个pom,可以用来重现异常(快速点击按钮时出现异常):

    MyUI.java
    package com.example.sample;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.annotation.WebServlet;
    
    import com.vaadin.annotations.Theme;
    import com.vaadin.annotations.VaadinServletConfiguration;
    import com.vaadin.server.VaadinRequest;
    import com.vaadin.server.VaadinServlet;
    import com.vaadin.ui.Button;
    import com.vaadin.ui.Grid;
    import com.vaadin.ui.Notification;
    import com.vaadin.ui.TextField;
    import com.vaadin.ui.UI;
    import com.vaadin.ui.VerticalLayout;
    
    @Theme("mytheme")
    public class MyUI extends UI {
        List<Product> items = new ArrayList<Product>();
        Grid<Product> grid;
    
        @Override
        protected void init(VaadinRequest vaadinRequest) {
            items.add(new Product("test", "test test"));
    
            final VerticalLayout layout = new VerticalLayout();
    
            grid = new Grid<Product>();
            layout.addComponent(grid);
    
            grid.getEditor().setBuffered(true);
            grid.getEditor().setEnabled(true);
    
            grid.removeAllColumns();
    
            grid.addComponentColumn(this::buildAddButton);
    
            TextField nameField = new TextField();
            TextField descriptionField = new TextField();
    
            grid.addColumn(Product::getName).setCaption("Name").setEditorComponent(nameField, Product::setName)
                    .setExpandRatio(1);
            grid.addColumn(Product::getDescription).setCaption("Description")
                    .setEditorComponent(descriptionField, Product::setDescription).setExpandRatio(1);
    
            grid.getEditor().addSaveListener(event -> {
    
                Notification.show((event.getBean() + "saved"));
            });
    
            grid.setItems(items);
            setContent(layout);
        }
    
        private Button buildAddButton(Product p) {
    
            Button button = new Button();
    
            button.addClickListener(event -> addButtonClicked(p));
    
            return button;
        }
    
        private void addButtonClicked(Product p) {
            refreshGrid();
        }
    
        private void refreshGrid() {
            grid.setItems(items);
        }
    
        @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
        @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
        public static class MyUIServlet extends VaadinServlet {
        }
    
        public class Product {
            String name;
            String description;
    
            public Product(String name, String description) {
                super();
                this.name = name;
                this.description = description;
            }
    
            public Product() {
                super();
    
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public String getDescription() {
                return description;
            }
    
            public void setDescription(String description) {
                this.description = description;
            }
    
            @Override
            public String toString() {
                return name + " " + description;
            }
    
        }
    }
    

    pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example</groupId>
        <artifactId>sample</artifactId>
        <packaging>war</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>sample</name>
    
        <prerequisites>
            <maven>3</maven>
        </prerequisites>
    
        <properties>
            <vaadin.version>8.3.1</vaadin.version>
            <vaadin.plugin.version>8.3.1</vaadin.plugin.version>
            <jetty.plugin.version>9.3.9.v20160517</jetty.plugin.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <!-- If there are no local customizations, this can also be "fetch" or "cdn" -->
            <vaadin.widgetset.mode>local</vaadin.widgetset.mode>
        </properties>
    
        <repositories>
            <repository>
                <id>vaadin-addons</id>
                <url>http://maven.vaadin.com/vaadin-addons</url>
            </repository>
        </repositories>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>com.vaadin</groupId>
                    <artifactId>vaadin-bom</artifactId>
                    <version>${vaadin.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <dependencies>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-server</artifactId>
            </dependency>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-push</artifactId>
            </dependency>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-client-compiled</artifactId>
            </dependency>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-themes</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.0.0</version>
                    <configuration>
                        <failOnMissingWebXml>false</failOnMissingWebXml>
                        <!-- Exclude an unnecessary file generated by the GWT compiler. -->
                        <packagingExcludes>WEB-INF/classes/VAADIN/widgetsets/WEB-INF/**</packagingExcludes>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>com.vaadin</groupId>
                    <artifactId>vaadin-maven-plugin</artifactId>
                    <version>${vaadin.plugin.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>update-theme</goal>
                                <goal>update-widgetset</goal>
                                <goal>compile</goal>
                                <!-- Comment out compile-theme goal to use on-the-fly theme compilation -->
                                <goal>compile-theme</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.0.0</version>
                    <!-- Clean up also any pre-compiled themes -->
                    <configuration>
                        <filesets>
                            <fileset>
                                <directory>src/main/webapp/VAADIN/themes</directory>
                                <includes>
                                    <include>**/styles.css</include>
                                    <include>**/styles.scss.cache</include>
                                </includes>
                            </fileset>
                        </filesets>
                    </configuration>
                </plugin>
    
                <!-- The Jetty plugin allows us to easily test the development build by
                    running jetty:run on the command line. -->
                <plugin>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-maven-plugin</artifactId>
                    <version>${jetty.plugin.version}</version>
                    <configuration>
                        <scanIntervalSeconds>2</scanIntervalSeconds>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
        <profiles>
            <profile>
                <!-- Vaadin pre-release repositories -->
                <id>vaadin-prerelease</id>
                <activation>
                    <activeByDefault>false</activeByDefault>
                </activation>
    
                <repositories>
                    <repository>
                        <id>vaadin-prereleases</id>
                        <url>http://maven.vaadin.com/vaadin-prereleases</url>
                    </repository>
                    <repository>
                        <id>vaadin-snapshots</id>
                        <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
                        <releases>
                            <enabled>false</enabled>
                        </releases>
                        <snapshots>
                            <enabled>true</enabled>
                        </snapshots>
                    </repository>
                </repositories>
                <pluginRepositories>
                    <pluginRepository>
                        <id>vaadin-prereleases</id>
                        <url>http://maven.vaadin.com/vaadin-prereleases</url>
                    </pluginRepository>
                    <pluginRepository>
                        <id>vaadin-snapshots</id>
                        <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
                        <releases>
                            <enabled>false</enabled>
                        </releases>
                        <snapshots>
                            <enabled>true</enabled>
                        </snapshots>
                    </pluginRepository>
                </pluginRepositories>
            </profile>
        </profiles>
    
    </project>
    

    最佳答案

    免责声明 : 由于缺少 setHandleWidgetEvents,此解决方案不适用于 8.3 之前的版本方法。否则,虽然复杂,但此解决方案可能适用于某些人:

  • 从按钮 ( addClickListener ) 中删除单击事件处理。无法在那里获得有关双击的信息,但是
  • 有一种方法可以使用 addItemClickListener和它的 clickedItem.getMouseEventDetails().isDoubleClick() , 但是 再次
  • 使用 grid.getEditor().setEnabled(true); 启用网格后双击检查总是返回 false ,所以
  • 您需要默认禁用编辑器 grid.getEditor().setEnabled(false);(这里有一个很好的相关答案 Couldn't capture double click event using vaadin 7)
  • 并且,一旦发生双击事件(并打开一行),重新启用它 grid.getEditor().setEnabled(true); grid.getEditor().editRow(item.getRowIndex());
  • 如果,否则,单击按钮列一次,执行您的 addButtonClicked然后行动
  • 为了从 Grid 列的组件接收事件,当单击按钮时,列应该能够处理事件。这是使用 grid.addComponentColumn(this::buildAddButton).setHandleWidgetEvents(true).setId("buttonClick"); 实现的

  • 来自 init 的完整修改代码方法。所有其他保持不变(另外,请记住从按钮中删除 addClickListener):

            items.add(new Product("test", "test test"));
    
            grid = new Grid<Product>();
    
            grid.getEditor().setBuffered(true);
            grid.getEditor().setEnabled(false);
    
            grid.removeAllColumns();
    
            // Important! Propagate events from components to Grid
            grid.addComponentColumn(this::buildAddButton).setHandleWidgetEvents(true).setId("buttonClick");
    
            TextField nameField = new TextField();
            TextField descriptionField = new TextField();
    
            grid.addColumn(Product::getName).setCaption("Name").setEditorComponent(nameField, Product::setName)
                    .setExpandRatio(1);
            grid.addColumn(Product::getDescription).setCaption("Description")
                    .setEditorComponent(descriptionField, Product::setDescription).setExpandRatio(1);
    
            //Once close editor--> Disable it
            grid.getEditor().addSaveListener(event -> {
                grid.getEditor().setEnabled(false);
    
            });
            grid.getEditor().addCancelListener(e->{
                grid.getEditor().setEnabled(false);
            });
            //THIS IS WHERE ALL THE LOGIC IS HAPPENING
            grid.addItemClickListener(item->{
                //If the button column is clicked
               if("buttonClick".equals(item.getColumn().getId())){
                   //Regual click--> update content; also fired twice before editor is opened
                   if(!item.getMouseEventDetails().isDoubleClick()){
                      addButtonClicked(item.getItem());
                   }
                   //If Double click is detected, just opened editor. The data is already updated
                   else{
                       grid.getEditor().setEnabled(true);
                       grid.getEditor().editRow(item.getRowIndex());
                   }
                }
               //In all the other cases, when double click is detected--> open editor
               else if(item.getMouseEventDetails().isDoubleClick()){
                   grid.getEditor().setEnabled(true);
                   grid.getEditor().editRow(item.getRowIndex());
               }
            });
    
            grid.setItems(items);
            addComponent(grid);
    

    关于java - 带有 Activity 编辑器的 Vaadin 双击网格上的按钮会引发异常 ("java.lang.NullPointerException: Editor can' t edit null"),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48825075/

    相关文章:

    java - 在 Java 中创建 SSL 连接

    java - 图像字段作为 Vaadin7 中 FieldGroup 的一部分

    Vaadin 10,如何将 css 类添加到 Java 中的 div 元素?

    java - 从 Spring EventListener 更新 Vaadin 8 UI

    java - 将图像编码为视频时出现 JCodec 错误

    Java直接遍历数组而不是数组变量

    java - 我可以在 vaadin 中直接流式传输 ZipFile 吗?

    datagrid - 为 Vaadin 8 Grid 实现列渲染器

    Vaadin:在日期字段的字段焦点上打开日历

    java - JFrame:WAITING按下表单上的按钮以接收类中的值并继续