我对这个事实背后的想法有疑问,只有UIForm
获得了属性prependId
。为什么NamingContainer
接口中未指定属性?你现在可能会说这是因为后向兼容性但我宁愿打破兼容性并让实现该接口的用户也实现prependId的方法。
我对UIForm
组件中prependId的看法的主要问题是,它会中断findComponent()
我希望如果我使用prependId
,那么NamingContainer
行为会发生变化,不仅与渲染有关,而且在想要在组件树中搜索组件时也是如此。
这是一个简单的例子:
<h:form id="test" prependId="false">
<h:panelGroup id="group"/>
</h:form>
现在,当我想获取panelGroup组件时,我希望将字符串"group"
传递给方法findComponent()
,但它找不到任何内容,我必须使用"test:group"
代替。
具体问题是,当使用带有prependId="false"
的ajax时。 ajax标记在属性更新和处理中期望值关注命名容器。有点奇怪的是,当我使用prependId="false"
时,我必须指定完整的id或路径,但没关系。
<h:form id="test" prependId="false">
<h:panelGroup id="group"/>
</h:form>
<h:form id="test1" prependId="false">
<h:commandButton value="go">
<f:ajax render="test:group"/>
</h:commandButton>
</h:form>
这段代码将呈现没有问题,但它不会更新panelGroup,因为它无法找到它。 PartialViewContext
将仅包含id "group"
作为renderIds的元素。我不知道这是否是预期的,可能是但我不知道代码。现在我们来到方法findComponent()
无法找到组件的点,因为作为参数传递的表达式是"group"
,方法希望"test:group"
找到组件。
一种解决方案是编写自己的findComponent()
,这是我选择处理此问题的方式。在这个方法中,我处理一个NamingContainer
的组件,并将属性prependId设置为false,就像普通UIComponent
一样。我必须为每个提供prependId属性的UIComponent
执行此操作,这很糟糕。反射将有助于绕过类型的静态定义,但它仍然不是一个非常干净的解决方案。
另一种方法是在NamingContainer
界面中引入prependId属性,并将findComponent()
的行为更改为如上所述。
最后提出的解决方案是改变ajax标记的行为以传递整个id,但这只会解决ajax问题,而不是findComponent()
实现背后的程序问题。
你怎么看待这个以及它为什么会这样实现呢?我不能成为第一个遇到此问题的人,但我无法找到相关主题?!
答案 0 :(得分:18)
确实,使用UIComponent#findComponent()
时,<f:ajax render>
执行<h:form prependId="false">
失败。这个问题是众所周知的,并且是“无法修复”:JSF spec issue 573。
我认为,在JSF 1.2时代,他们永远不应该在prependId
添加UIForm
属性。这样做只是为了让j_security_check
用户满意,他们希望将JSF表单与JSF输入组件一起使用(j_security_check
需要精确的输入字段名称j_username
和j_password
无法通过配置进行修改)。但是他们没有完全意识到在JSF 1.2期间引入了另一项改进,使您可以继续使用<form>
而不是坚持<h:form>
。然后CSS / jQuery纯粹主义者开始滥用prependId="false"
以避免在他们选择不当的CSS选择器中转义分隔符:
。
永远不要使用prependId="false"
。
对于j_security_check
,只需使用<form>
或新的Servlet 3.0 HttpServletRequest#login()
即可。另请参阅Performing user authentication in Java EE / JSF using j_security_check。
对于CSS选择器,如果您绝对需要ID选择器(因此不是更可重用的类选择器),只需将感兴趣的组件包装在纯HTML <div>
或<span>
中。