为什么我在上一个UI中单击链接后单击创建的UI中的按钮时会出现通信问题?

ton*_*nix 4 java user-interface vaadin

当我尝试执行以下操作时,我有一个奇怪的"通信问题"Vaadin错误:

我有两个UI,第一个有一个指向另一个UI的链接,这里是代码:

public class MenuUI extends UI {

    @WebServlet(value = "/*", asyncSupported = true)
    @VaadinServletConfiguration(productionMode = false, ui = MenuUI.class)
    public static class Servlet extends VaadinServlet {
    }

    @Override
    protected void init(VaadinRequest request) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        setContent(layout);

        MenuBar menuBar = new MenuBar();

        MenuItem submenu1 = menuBar.addItem("Submenu 1", null);
        MenuItem submenu2 = menuBar.addItem("Submenu 2", null);

        submenu1.addItem("Option 1", null);
        submenu1.addItem("Option 2", null);

        submenu2.addItem("Option 3", null);
        submenu2.addItem("Option 4", new Command() {
            @Override
            public void menuSelected(MenuItem selectedItem) {
                Notification.show("That was option 4");
            }
        });
        layout.addComponent(menuBar);

        Link link = new Link("Go to ShortcutKeys",
                             new ExternalResource(Page.getCurrent().getLocation().toString() + "Shortcut"));
        link.setTargetName("_blank");
        layout.addComponent(link);
    }

}
Run Code Online (Sandbox Code Playgroud)

这个UI结果如下: 在此输入图像描述

当我点击链接时,会初始化一个新UI,这里是新UI的代码:

public class ShortcutUI extends UI {

    @WebServlet(value = "/Shortcut", asyncSupported = true)
    @VaadinServletConfiguration(productionMode = false, ui = ShortcutUI.class)
    public static class Servlet extends VaadinServlet {
    }

    @Override
    protected void init(VaadinRequest request) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        setContent(layout);

        final TextField tf = new TextField("Your data:");
        layout.addComponent(tf);

        Button button = new Button("Send data (ENTER)");
        button.setClickShortcut(KeyCode.ENTER, ModifierKey.SHIFT);

        button.addClickListener(new Button.ClickListener() {
            public void buttonClick(ClickEvent event) {
                layout.addComponent(new Label(tf.getValue()));
                tf.setValue("");
                tf.focus();
            }
        });

        layout.addComponent(button);
    }
Run Code Online (Sandbox Code Playgroud)

结果如下:

在此输入图像描述

在这里我键入一些文本,然后当我单击"发送数据"按钮时,我得到以下内容:

在此输入图像描述

为什么我会遇到此通信问题?如果UI不是从单击的链接创建的,例如,如果我将ShortcutUI init()内容放在MenuUI中,就像在下面的类中一样:

public class MenuUI extends UI {

    @WebServlet(value = "/*", asyncSupported = true)
    @VaadinServletConfiguration(productionMode = false, ui = MenuUI.class)
    public static class Servlet extends VaadinServlet {
    }

    @Override
    protected void init(VaadinRequest request) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        setContent(layout);

        MenuBar menuBar = new MenuBar();

        MenuItem submenu1 = menuBar.addItem("Submenu 1", null);
        MenuItem submenu2 = menuBar.addItem("Submenu 2", null);

        submenu1.addItem("Option 1", null);
        submenu1.addItem("Option 2", null);

        submenu2.addItem("Option 3", null);
        submenu2.addItem("Option 4", new Command() {
            @Override
            public void menuSelected(MenuItem selectedItem) {
                Notification.show("That was option 4");
            }
        });
        layout.addComponent(menuBar);

        Link link = new Link("Go to ShortcutKeys",
                             new ExternalResource(Page.getCurrent().getLocation().toString() + "Shortcut"));
        link.setTargetName("_blank");
        layout.addComponent(link);

        // Shortcut Keys
        final TextField tf = new TextField("Your data:");
        layout.addComponent(tf);

        Button button = new Button("Send data (ENTER)");
        button.setClickShortcut(KeyCode.ENTER, ModifierKey.SHIFT);

        button.addClickListener(new Button.ClickListener() {
            public void buttonClick(ClickEvent event) {
                layout.addComponent(new Label(tf.getValue()));
                tf.setValue("");
                tf.focus();
            }
        });

        layout.addComponent(button);
    }

}
Run Code Online (Sandbox Code Playgroud)

代码会导致这样:

在此输入图像描述

一切正常.根本没有通信问题("一些文本"和"其他一些文本"是在"发送数据"按钮上执行单击时添加的标签).

那么为什么如果我有一个从链接创建的UI,在Button上触发的Click事件会导致Vaadin中的这个"通信问题"?

这是来自?debug Vaadin控制台的完整错误:

Communication error: (SyntaxError) : Unexpected token < - Original JSON-text: html> 
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta 
http-equiv="X-UA-Compatible" content="IE=11;chrome=1" /> <style type="text/css">html, 
body {height:100%;margin:0;}</style> <link rel="shortcut icon" 
type="image/vnd.microsoft.icon" 
href="./../../VAADIN/themes/chapter_4_menubar_and_shortcutkeys/favicon.ico" /> <link 
rel="icon" type="image/vnd.microsoft.icon" 
href="./../../VAADIN/themes/chapter_4_menubar_and_shortcutkeys/favicon.ico" /> </head> 
<body scroll="auto" class=" v-generated-body"> <div id="Chapter4MenuBarandShortcutKeys-
1103734620" class=" v-app chapter_4_menubar_and_shortcutkeys"> <div class=" v-app-
loading"></div> <noscript> You have to enable javascript in your browser to use an  
application built with Vaadin. </noscript> </div> <script type="text/javascript" 
src="./../../VAADIN/vaadinBootstrap.js"></script> <script type="text/javascript">//<!
[CDATA[ if (!window.vaadin) alert("Failed to load the bootstrap javascript: 
./../../VAADIN/vaadinBootstrap.js"); if (typeof window.__gwtStatsEvent != 'function') { 
vaadin.gwtStatsEvents = []; window.__gwtStatsEvent = function(event) 
{vaadin.gwtStatsEvents.push(event); return true;}; } 
vaadin.initApplication("Chapter4MenuBarandShortcutKeys-1103734620",{ "authErrMsg": { 
"caption": "Authentication problem", "message": "Take note of any unsaved data, and 
<u>click here<\/u> or press ESC to continue." }, "comErrMsg": { "caption": "Communication 
problem", "message": "Take note of any unsaved data, and <u>click here<\/u> or press ESC 
to continue." }, "debug": true, "heartbeatInterval": 300, "serviceUrl": "./../..", 
"sessExpMsg": { "caption": "Session Expired", "message": "Take note of any unsaved data, 
and <u>click here<\/u> or press ESC key to continue." }, "standalone": true, "theme": 
"chapter_4_menubar_and_shortcutkeys", "vaadinDir": "./../../VAADIN/", "versionInfo": 
{"vaadinVersion": "7.3.7"}, "widgetset": "com.vaadin.DefaultWidgetSet" }); //]]></script> 
</body> </html
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

ton*_*nix 6

如果有人遇到我面临的同样问题,请注意如何解决此类问题.

问题是负责显示ShortcutUI的VaadinServlet的映射:

简而言之,这个:

 @WebServlet(value = "/Shortcut", asyncSupported = true)
Run Code Online (Sandbox Code Playgroud)

应该成为这样的:

 @WebServlet(value = "/Shortcut/*", asyncSupported = true)
Run Code Online (Sandbox Code Playgroud)

当在ShortcutUI上发生事件并发送到服务器时,它们会发送一个请求到URL,如下所示:

http://localhost:8080/myapp/Shortcut/UIDL/?v-uiId=0
Run Code Online (Sandbox Code Playgroud)

如果应用程序的上下文根是/myapp,或者像这样:

http://localhost:8080/Shortcut/UIDL/?v-uiId=0
Run Code Online (Sandbox Code Playgroud)

如果应用程序的上下文根是servlet容器的DocumentRoot(例如,在通过mvn命令创建的Maven项目中并mvn jetty:run使用嵌入式Jetty运行).

这就是为什么/Shortcut永远不会匹配/Shortcut/UIDL/?v-uiId=0,因此错误.Shortcut/*相反,将匹配"/*"后的Shortcut匹配/UIDL/?v-uiId=0.

另一个需要注意的重要事项是,您可能想要修改"入口点"UI(即映射到的UI /*)的servlet映射并更改此设置:

@WebServlet(value = "/*", asyncSupported = true)

对此:

@WebServlet(urlPatterns = { "", "/VAADIN/*" }, asyncSupported = true)

绝对不要那样做!这将一直有效,直到Vaadin应用程序发出/HEARTBEAT请求或触发用户界面上的事件并发送到服务器(发出"/ UIDL"请求).Vaadin仍然需要"*",因为它会匹配这样的请求("/ HEARTBEAT","/ UIDL",等等......).

所以IMHO的这个问题的独特解决方案是使用:

@WebServlet(value = "/*", asyncSupported = true)
Run Code Online (Sandbox Code Playgroud)

对于MenuUI,和:

@WebServlet(value = "/Shortcut/*", asyncSupported = true)
Run Code Online (Sandbox Code Playgroud)

对于ShortcutUI,做同样的应用程序的任何"子UI",如果需要多个用户界面.

否则,另一个选择是使用NavigatorAPI - > https://vaadin.com/book/-/page/advanced.navigator.html