如何将FacesMessage从辅助bean附加到ui:repeat中的特定字段? [英] How do I attach a FacesMessage from the backing bean to a specific field in a ui:repeat?

查看:121
本文介绍了如何将FacesMessage从辅助bean附加到ui:repeat中的特定字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a form with a variable number of input elements, like this:

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

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

When a certain method in the backing bean is triggered, I want to add a message to, say, the second iteration of the ui:repeat, but not the other ones.

我已经看到此问题在此处的不同变化,所有问题似乎都是由于ui:repeat的迭代在JSF组件树中不可用.

I've seen different variations of this question around here, and all problems appear to be due to the ui:repeat's iterations not being available in the JSF component tree.

到目前为止我尝试过的事情:

What I have tried so far:

  • h:inputTextarea绑定到Bean中的Map<String,UIComponent>. (a)...使用...binding="#{myBean.uiMap[_lang.id]}"(其中_lang.id是唯一的字符串).这产生了 JBWEB006017:无法到达目标,"BracketSuffix"返回null . (我使用id转储了相应的字符串映射,相同的语法在binding外部也可以正常工作)(b)...或使用...binding="#{myBean.uiMap.get()}".这样可以使页面正常显示,但是当我按下方法的按钮时,不会调用setter,因此UIComponent永远不会添加到Map.

  • Bind the h:inputTextareas to a Map<String,UIComponent> in the bean. (a) ...Using ...binding="#{myBean.uiMap[_lang.id]}" (where _lang.id is a unique String). This produced JBWEB006017: Target Unreachable, ''BracketSuffix'' returned null. (I dumped a corresponding map of strings using the ids, the same syntax works just fine outside of binding) (b) ...or using ...binding="#{myBean.uiMap.get()}". This renders the page fine, but when I push the button for my method, the setter does not get called, and thus the UIComponents never get added to the Map.

h:inputTextarea绑定到Bean中的数组UIComponent[],使用正确的空值预填充它,然后将ui:repeat的行计数器用作xhtml文件中的索引.得到了Null指针异常,该数组的setter从未被调用,因此该数组从未填充实际的UIComponent s.

Bind the h:inputTextareas to an array UIComponent[] in the bean, prepopulating it with the right number of nulls and then using the row counter of ui:repeat as an index in the xhtml file. Got Null Pointer exceptions, the setter of the array was never called and thus the array was never populated with the actual UIComponents.

在Bean外部绑定h:panelGroup,并尝试在JSF树的子元素中递归地找到输入元素.只能找到其中一种输入,请参见上面的无法使用迭代"问题.

Bind an outer h:panelGroup to the bean and try to find the input elements recursively amongst its children in the JSF tree. Only one of the inputs is ever found, see the "iterations not available" problem above.

我还尝试用c:forEach替换ui:repeat并手动生成行号(以便希望它们在JSF树中可用),但是我根本没有得到任何渲染的输出.

I also tried replacing the ui:repeat with c:forEach and generate row numbers manually (so that they'd hopefully be available in the JSF tree), but there I didn't get any rendered output at all.

(注意:目标是显示验证错误消息,但是它们必须来自备用bean.使用f:validator之类的东西,甚至是自定义的东西也不是真正的选择,因为我需要针对备用bean中的数据.)

(Note: The goal is to display a validation error message, but they have to come from the backing bean. Using an f:validator or the likes, even custom ones, is not really an option because I need to validate against data in the backing bean.)

坦白说,我没主意了.这不是那么困难,不是吗?

Frankly, I'm out of ideas. This can't be so difficult, can it?

对于第三次绑定到外部h:panelGroup的尝试,这是我的JSF finder函数:

For my third attempt, binding to an outer h:panelGroup, here's my JSF finder function:

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上称呼它,这是h:panelGroupui:repeat周围的绑定UIComponent. (我正在使用递归,因为我的实时应用程序具有稍微嵌套的结构)我认为这应该给我所有"theTitle"文本区域,以便随后我可以添加消息并按需读取属性. ,,该方法仅返回 one "theTitle"组件,并且日志消息显示原因:

I'm calling this on node, which is the binding UIComponent of the h:panelGroup around the ui:repeat. (I'm using recursion because my live application has a slightly more nested structure) This, I thought, should give me all "theTitle" textareas, so that I then could add messages and read attributes as I pleased. Alas, the method only returns one "theTitle" component, and the log messages show why:

在生成的页面的DOM中,id类似于"myform:myPanelGroup:0:theTitle"(包括ui:repeat的迭代计数器),而Bean仅看到诸如myform:myPanelGroup:theTitle的getClientId(),并且只能存在一次,用于最后一次(我猜是?)迭代.

In the DOM of the generated page, the ids are like "myform:myPanelGroup:0:theTitle" (including the iteration counter of the ui:repeat) while the bean only sees getClientId()s like myform:myPanelGroup:theTitle - and that only exist once, for the last (I guess?) iteration.

推荐答案

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

Your attempts to bind the input component to a map/array failed because there are not multiple of those components in the JSF component tree, but only one. The <ui:repeat> doesn't run during view build time producing the JSF component tree. Instead, it runs during view render time producing the HTML output. In other words, the child components of <ui:repeat> are reused everytime during generating HTML output of each iteration.

抛出了特定异常"Target Unreachable," BracketSuffix返回null" ,因为变量#{_lang}在视图构建期间(即UI组件的那一刻)不可用构造树,并评估所有idbinding属性.仅在视图渲染期间可用.

The particular exception, "Target Unreachable, ''BracketSuffix'' returned null" is been thrown because the variable #{_lang} isn't available during view build time, that moment when the UI component tree is constructed and all id and binding attributes are evaluated. It's only available during view render time.

如果使用<c:forEach>代替,则这些绑定尝试将成功.它在视图构建期间运行,从而生成JSF组件树.然后,您将最终获得子组件的多个实例,这些子组件又会各自生成自己的HTML输出,而不会多次重复使用.

Those binding attempts would have succeeded if you used <c:forEach> instead. It runs during view build time producing the JSF component tree. You would then end up with physically multiple instances of the child components which in turn produce each their own HTML output without being reused multiple times.

放入面板小组并试图找到所有孩子显然由于上述原因而行不通. <ui:repeat>不会在组件树中实际生成多个JSF组件.取而代之的是,它将根据当前迭代回合的状态重复使用同一组件来多次产生HTML输出.

Putting in a panel group and attempting to find all children obviously won't work for the reasons mentioned before. The <ui:repeat> doesn't generate physically multiple JSF components in the component tree. Instead, it reuses the same component to produce the HTML output multiple times depending on the state of the current iteration round.

<c:forEach>替换应该已经起作用.也许您遇到了时序问题,因为它在视图构建期间运行,并且您正在例如建模期间准备模型. preRenderView而不是@PostConstruct左右.

Replacing by <c:forEach> should have worked. Perhaps you were facing a timing issue because it runs during view build time and you're preparing the model during e.g. preRenderView instead of @PostConstruct or so.

如果您仔细阅读JSF2 Facelets中的 JSTL,则以上所有内容将更易于理解.合理吗?

All of above is easier to understand if you carefully read JSTL in JSF2 Facelets... makes sense?

关于您的具体功能要求,通常将Validator用于该工作.如果将其注册在输入组件上,则将在每个迭代回合中调用它.您将立即获得具有正确状态的正确输入组件作为validate()方法的第二个参数,提交/转换后的值作为第三个参数.

As to your concrete functional requirement, you would normally use a Validator for the job. If you register it on the input component, then it would be invoked for every iteration round. You would immediately have the right input component with the right state at hands as 2nd argument of validate() method and the submitted/converted value as 3rd argument.

如果您以后确实需要执行该工作,例如,因为您需要了解所有输入,那么您应该自己以编程方式遍历<ui:repeat>.您可以在

If you really need to perform the job afterwards, for example because you need to know about all inputs, then you should be programmatically iterating over the <ui:repeat> yourself. You can do that with help of UIComponent#visitTree() this allows you to collect the input component's state of every iteration round.

例如

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中项目的顺序
  • See also:

    • Validate order of items inside ui:repeat
    • 这篇关于如何将FacesMessage从辅助bean附加到ui:repeat中的特定字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆