如何将 FacesMessage 从支持 bean 附加到 ui:repeat 中的特定字段?

2023-12-13

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

<ui:repeat var="_lang" value="#{myBean.languages}">
    <h:inputTextarea value="${_lang.title}" id="theTitle" />
    <h:messages for="theTitle"/>
</ui:repeat>

当触发支持 bean 中的某个方法时,我想添加一条消息,例如,第二次迭代ui:repeat,但不是其他的。

我见过这个问题在这里有不同的变体,所有问题似乎都是由于ui:repeat的迭代在 JSF 组件树中不可用。

到目前为止我已经尝试过:

  • 绑定h:inputTextareas to a Map<String,UIComponent>在豆子里。 (a) ...使用...binding="#{myBean.uiMap[_lang.id]}" (where _lang.id是一个唯一的字符串)。这产生了JBWEB006017:目标无法访问,“BracketSuffix”返回 null。 (我使用 ids 转储了相应的字符串映射,相同的语法在外部工作得很好binding) (b) ...或使用...binding="#{myBean.uiMap.get()}"。这可以很好地呈现页面,但是当我按下方法的按钮时,设置器不会被调用,因此UIComponents 永远不会被添加到Map.

  • 绑定h:inputTextareas 到一个数组UIComponent[]在 bean 中,用正确数量的空值预填充它,然后使用ui:repeat作为 xhtml 文件中的索引。出现空指针异常,数组的 setter 从未被调用,因此数组从未填充实际的值UIComponents.

  • 绑定外层h:panelGroup到 bean 并尝试在 JSF 树中的子元素中递归地查找输入元素。仅找到其中一个输入,请参阅上面的“迭代不可用”问题。

  • 我也尝试更换ui:repeat with c:forEach并手动生成行号(以便它们可以在 JSF 树中使用),但我根本没有得到任何渲染输出。

(注意:目标是显示验证错误消息,但它们必须来自支持 bean。使用f:validator或类似的,甚至是自定义的,并不是真正的选择,因为我需要根据支持 bean 中的数据进行验证。)

坦白说,我没有主意。这不会那么困难,不是吗?

Edit:

对于我的第三次尝试,绑定到外部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 of the h:panelGroup周围的ui:repeat。 (我使用递归是因为我的实时应用程序具有稍微更多的嵌套结构)我认为,这应该为我提供所有“theTitle”文本区域,以便我可以根据需要添加消息并读取属性。唉,该方法只返回one“theTitle”组件,日志消息显示了原因:

在生成页面的 DOM 中,id 类似于“myform:myPanelGroup:0:theTitle”(包括迭代计数器ui:repeat) 而 bean 只看到 getClientId() 类似myform:myPanelGroup:theTitle- 并且只存在一次,用于最后一次(我猜?)迭代。


您尝试将输入组件绑定到映射/数组失败,因为 JSF 组件树中没有多个这些组件,而只有一个。这<ui:repeat>在生成 JSF 组件树的视图构建期间不会运行。相反,它在视图渲染期间运行,生成 HTML 输出。换句话说,子组件<ui:repeat>在每次迭代生成 HTML 输出期间每次都会重用。

特殊的例外是,“目标无法到达,''BracketSuffix'' 返回 null”被抛出是因为变量#{_lang}在视图构建期间不可用,即构建 UI 组件树的那一刻以及所有id and binding属性被评估。它仅在视图渲染期间可用。

如果您使用的话,这些绑定尝试就会成功<c:forEach>反而。它在视图构建期间运行,生成 JSF 组件树。然后,您最终会得到子组件的物理多个实例,这些实例又会生成各自的 HTML 输出,而不会被多次重用。

由于前面提到的原因,建立一个小组并试图找到所有孩子显然是行不通的。这<ui:repeat>不会在组件树中生成物理上的多个 JSF 组件。相反,它根据当前迭代轮的状态重复使用相同的组件来多次生成 HTML 输出。

替换为<c:forEach>应该有效。也许您遇到了计时问题,因为它在视图构建期间运行,而您正在准备模型,例如preRenderView代替@PostConstruct or so.

如果您仔细阅读以上内容,就更容易理解JSF2 Facelets 中的 JSTL...有意义吗?


至于您的具体功能要求,您通常会使用Validator为了工作。如果您在输入组件上注册它,那么它将在每个迭代轮中被调用。您将立即拥有正确的输入组件和正确的状态作为第二个参数validate()方法和提交/转换的值作为第三个参数。

如果您确实需要事后执行该工作,例如因为您需要了解所有输入,那么您应该以编程方式迭代<ui:repeat>你自己。你可以在以下的帮助下做到这一点UIComponent#visitTree()这允许您收集每个迭代轮次的输入组件的状态。

E.g.

final FacesContext facesContext = FacesContext.getCurrentInstance();
UIComponent repeat = getItSomehow(); // findComponent, binding, etc.

repeat.visitTree(VisitContext.createVisitContext(facesContext), new VisitCallback() {
    @Override
    public VisitResult visit(VisitContext context, UIComponent target) {
        if (target instanceof UIInput && target.getId().equals("theTitle")) {
            String clientId = target.getClientId(facesContext);
            Object value = ((UIInput) target).getValue();
            // ...
            facesContext.addMessage(clientId, message);                
        }
        return VisitResult.ACCEPT;
    }
});

也可以看看:

  • 验证 ui 内项目的顺序:repeat
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将 FacesMessage 从支持 bean 附加到 ui:repeat 中的特定字段? 的相关文章

随机推荐