如何将辅助bean中的FacesMessage附加到ui:repeat中的特定字段?

时间:2013-10-02 07:04:35

标签: jsf-2 facelets messages uirepeat backing-beans

我有一个包含可变数量输入元素的表单,如下所示:

<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对于最后一次(我猜?)迭代,它只存在一次。

2 个答案:

答案 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验证而不是生命周期验证,因此这不是问题。