上周,我的帖子是关于如何自定义默认Vaadin HTML模板,以便可以添加lang
属性的。 我没有要求反馈,但是无论如何我还是得到了反馈,所以让我们利用这个机会来分析它。
首先,我必须承认我的解决方案不是Vaadin风格的,因为我使用AOP来完成工作。 我来自Vaadin团队的朋友Matti Tahvonen非常友善,不仅为我的提案提供了一个方案,而且提供了两种方案。 谢谢马蒂!
替代解决方案
第一个解决方案-我也得到了最后一个解决方案,由AMahdy AbdElAziz提交:
JavaScript.eval("document.querySelector('html').setAttribute('lang', 'fr')")
邪恶,但是有效,再简单不过了!
第二种解决方案使用Matti自己的框架-Viritin来实现。 我尚未看过该框架(但它在我的待办事项列表中),因此我无法对其进行评论,但这是代码段:
@Theme("mytheme")
publicclassMyUIextendsUI{
@Override
protectedvoidinit(VaadinRequestvaadinRequest){
// HtmlElementPropertySetter is part of Viritin add-on https://vaadin.com/directory=!addon/viritin
HtmlElementPropertySetterheps=newHtmlElementPropertySetter(this);
heps.setProperty("//html","lang","fr");
setContent(newLabel("Hello lang attr"));
}
@WebServlet(urlPatterns="/*",name="MyUIServlet",asyncSupported=true)
@VaadinServletConfiguration(ui=MyUI.class,productionMode=false)
publicstaticclassMyUIServletextendsVaadinServlet{}
}
Vaadin风格的标准服务器端方式有些复杂:
@Theme("mytheme")
publicclassMyUIextendsUI{
@Override
protectedvoidinit(VaadinRequestvaadinRequest){
setContent(newLabel("Hello lang attr"));
}
@WebServlet(urlPatterns="/*",name="MyUIServlet",asyncSupported=true)
@VaadinServletConfiguration(ui=MyUI.class,productionMode=false)
publicstaticclassMyUIServletextendsVaadinServlet{
@Override
protectedvoidservletInitialized()throwsServletException{
super.servletInitialized();
getService().addSessionInitListener(e->
e.getSession().addBootstrapListener(newBootstrapListener(){
@Override
publicvoidmodifyBootstrapFragment(
BootstrapFragmentResponseresponse){
// NOP, this is for portlets etc
}
@Override
publicvoidmodifyBootstrapPage(BootstrapPageResponseresponse){
response.getDocument().child(0).attr("lang","fr");
}
})
);
}
}
}
但是,我可以对此进行分析和评论:-)
流动分解
注意:对可能的改进更感兴趣的读者可以直接跳到该部分。
第一部分很容易。 它只是注册一个新的SessionInitListener(建议的代码使用lambda做到这一点):
第二部分发生在发出请求并且Vaadin注意到必须创建一个新会话时:
上一序列图的结尾是在Vaadin中以通用方式设计的。 在我们的特定情况下,它呈现为:
改进措施
我认为可以进行一些改进。
- 该代码非常冗长-即使使用Java 8的lambda
- 每次初始化会话时,都会创建两个对象:会话侦听器和引导侦听器
- 将servlet类设置为UI的内部类会使我的脊椎发抖。 尽管我知道这是要点,但不幸的是,这是使用Vaadin Maven原型获得的。 这与“单一责任原则”相去甚远。
由于我的最初示例同时使用了Spring Boot和Kotlin,因此这是我的版本:
@Configuration
openclassAppConfiguration{
// ... Abridged for readability's sake
@Bean
openfunvaadinServlet()=CustomVaadinServlet{event:SessionInitEvent->
event.session.addBootstrapListener(object: BootstrapListener{
overridefunmodifyBootstrapFragment(response:BootstrapFragmentResponse){
// NOP, this is for portlets etc
}
overridefunmodifyBootstrapPage(response:BootstrapPageResponse){
response.document.child(0).attr("lang","fr")
}
})
}
}
classCustomVaadinServlet(privatevallistener:(SessionInitEvent)->Unit):SpringVaadinServlet(){
overridefunservletInitialized(){
super.servletInitialized();
service.addSessionInitListener(listener)
}
}
使用Spring Boot,我可以将SessionInitListener
作为一个单例作用域Bean进行管理。 通过将其作为servlet参数传递,我只能分别创建SessionInitListener
和BootstrapListener
的单个实例。 当然,这是唯一可行的,因为语言值是固定的。
感谢Kotlin,我将重写的Servlet类共放置在配置文件中, 但在配置类之外。 由于servlet仅由配置使用,因此将它们放在一起是有意义的...但是不要太多。
最后,请注意SessionInitListener
是一个功能接口,这意味着它只有一个方法。 此单一方法等效于采用SessionInitEvent
并且不返回任何内容的函数。 在Kotlin中,签名是(SessionInitEvent) → Unit
。 我更喜欢使用该函数,而不是创建一个匿名内部类。 这不是改进,而是功能更强大的替代方案。 在运行时,这两种选择都将分配相同数量的内存。
完整的源代码可以在Github上的manage-lang分支中找到。
翻译自: https://blog.frankel.ch/feedback-customizing-vaadin-html-template/