How do I attach a FacesMessage from the backing bean to a specific field in a ui:repeat?

后端 未结 2 334
小鲜肉
小鲜肉 2021-01-20 05:57

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


    

        
2条回答
  •  余生分开走
    2021-01-20 06:29

    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 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 are reused everytime during generating HTML output of each iteration.

    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.

    Those binding attempts would have succeeded if you used 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.

    Putting in a panel group and attempting to find all children obviously won't work for the reasons mentioned before. The 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.

    Replacing by 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.

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


    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.

    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 yourself. You can do that with help of UIComponent#visitTree() this allows you to collect the input component's state of every iteration round.

    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;
        }
    });
    

    See also:

    • Validate order of items inside ui:repeat

提交回复
热议问题