我有一个包含可变数量输入元素的表单,如下所示:
<ui:repeat var="_lang" value="#{myBean.languages}">
<h:inputTextarea value="${_lang.title}" id="theTitle" />
<h:messages for="theTitle"/>
</ui:repeat>
当支持bean中的某个方法被触发时,我想在ui:repeat
的第二次迭代中添加一条消息,而不是其他方法。
我见过different variations of this question around here,所有问题似乎都是由于ui:repeat
的迭代在JSF组件树中不可用。
到目前为止我尝试过:
将h:inputTextarea
绑定到bean中的Map<String,UIComponent>
。 (a)......使用...binding="#{myBean.uiMap[_lang.id]}"
(其中_lang.id
是唯一的字符串)。这产生了 JBWEB006017:Target Unreachable,''BracketSuffix''返回null 。 (我使用id转储了相应的字符串映射,相同的语法在binding
)(b)...或使用...binding="#{myBean.uiMap.get()}"
之外正常工作。这样可以很好地呈现页面,但是当我按下我的方法的按钮时,不会调用setter,因此UIComponent
永远不会被添加到Map
。
将h:inputTextarea
绑定到bean中的数组UIComponent[]
,使用正确数量的空值预填充它,然后使用ui:repeat
的行计数器作为索引xhtml文件。得到空指针异常,从未调用过数组的setter,因此数组从未填充实际的UIComponent
。
将外部h:panelGroup
绑定到bean并尝试在JSF树中的子节点之间递归地查找输入元素。只找到其中一个输入,请参阅上面的“迭代不可用”问题。
我也尝试用ui:repeat
替换c:forEach
并手动生成行号(因此希望它们可以在JSF树中使用),但是我没有得到任何行号完全渲染输出。
(注意:目标是显示验证错误消息,但它们必须来自支持bean。使用f:validator
或类似的,甚至是自定义的,都不是真正的选择,因为我需要验证支持bean中的数据。)
修改
对于我的第三次尝试,绑定到外部h:panelGroup
,这是我的JSF查找器函数:
private List<UIComponent> findTitleComponents(UIComponent node) {
List<UIComponent> found = new ArrayList<UIComponent>();
for (UIComponent child : node.getChildren()) {
if (child.getId().equals("theTitle")) {
found.add(child);
log.debug("have found "+child.getClientId());
} else {
found.addAll(findTitleComponents(child));
log.debug("recursion into "+child.getClientId());
}
}
return found;
}
我在node
上调用它,这是UIComponent
周围h:panelGroup
的绑定ui:repeat
。 (我正在使用递归,因为我的实时应用程序有一个稍微更嵌套的结构)这个,我想,应该给我所有“theTitle”textareas,这样我就可以添加消息和阅读属性,因为我很高兴。唉,该方法只返回一个“theTitle”组件,日志消息显示原因:
在生成的页面的DOM中,id类似于“myform:myPanelGroup:0:theTitle”(包括ui:repeat
的迭代计数器),而bean只看到getClientId()s {{1对于最后一次(我猜?)迭代,它只存在一次。
答案 0 :(得分:2)
您尝试将输入组件绑定到地图/数组失败,因为JSF组件树中没有多个组件,但只有一个组件。 <ui:repeat>
在视图构建期间不会生成JSF组件树。相反,它在视图渲染时运行,生成HTML输出。换句话说,在每次迭代生成HTML输出期间,每次都会重用<ui:repeat>
的子组件。
特殊例外,&#34;目标无法访问,&#39;&#39; BracketSuffix&#39;&#39;返回null&#34; 被抛出,因为变量#{_lang}
在视图构建期间是不可用的,构建UI组件树的那一刻以及所有id
和{{ 1}}属性被评估。它仅在视图渲染时间内可用。
如果您使用binding
,那么绑定尝试就会成功。它在视图构建时运行,生成JSF组件树。然后,您将最终得到子组件的物理多个实例,这些实例又生成每个自己的HTML输出,而不会多次重复使用。
由于之前提到的原因,放入一个小组并试图找到所有孩子显然不会工作。 <c:forEach>
不会在组件树中生成多个物理上的JSF组件。相反,它会重复使用相同的组件来多次生成HTML输出,具体取决于当前迭代轮次的状态。
替换<ui:repeat>
应该有效。也许您正面临一个时间问题,因为它在视图构建期间运行,并且您在例如期间准备模型。 <c:forEach>
而不是preRenderView
左右。
如果您仔细阅读JSTL in JSF2 Facelets... makes sense?
,以上所有内容都会更容易理解关于您的具体功能要求,您通常会使用@PostConstruct
来完成工作。如果在输入组件上注册它,则会在每次迭代轮次中调用它。您将立即拥有正确状态的正确输入组件作为Validator
方法的第二个参数,并将提交/转换后的值作为第三个参数。
如果您确实需要在之后执行该作业,例如因为您需要了解所有输入,那么您应该以编程方式自己迭代validate()
。您可以在UIComponent#visitTree()
的帮助下完成此操作,这样您就可以收集每轮迭代的输入组件的状态。
E.g。
<ui:repeat>
答案 1 :(得分:0)
还有另一种选择:自己替换整个FacesMessages shebang。随着二十一点。和...
无论如何,基于与Johannes Brodwall的讨论,我们选择避免整个visitTree混乱并构建我们自己的消息机制。其中包括:
1)包含Multimaps地图的ViewScoped bean:
private Map<Object, Multimap<String, String>> fieldValidationMessages = new HashMap<>();
这需要Object
作为字段标识符(可以是String
内运行时生成的相应bean本身,UI组件甚至是ui:repeat
。该标识符可以具有
String
个消息。非常灵活。
bean还有方便的方法来获取和设置字段和子字段的消息,以及检查是否存储任何消息(即是否存在验证错误)。
2)一个简单的xhtml包含显示给定字段的错误消息,替换h:messages for...
而且已经是这样了。问题在于,它在生命周期的应用程序和呈现阶段运行,而不是JSF自己的验证阶段。但由于我们的项目已经决定进行bean验证而不是生命周期验证,因此这不是问题。