0

I would like to execute processDecodes of JSF UI components subtree on ajax request. I'll detail a little better, if I specify:

<f:ajax event="change" execute="componentId" render="componentPanelId" />

I would like that processDecodes is invoked on every component starting from the UIViewRoot to the component with id componentId, and on all componentId ancestors. I would do so because, ui custom component parent of the target component do some evaluation, specifically they narrow the portion of XML document, on which ui component value apply.

Suppose to have the following facelet fragment:

 <m:node id="root" ref="/Root">
  <m:node id="element" ref="Element">
    <m:label>Some Label</m:label>
    <m:selectOne id="componentId" ref="@Attribute1" execute="@this" render="toRender">
        ...
    </m:selectOne>
    <m:selectOne id="toRender" ref="@Attribute2">
        ...
    </m:selectOne>
  </m:node>
</m:node>

Provided that I have already implemented ajax behaviour in custom component, with attributes execute and render. When componentId ajax event trigger, I would process the components with id: 'root', 'element' and 'componentId', in the specified order.

I tried to do use @form as execute attribute value:

<m:selectOne id="componentId" ref="@Attribute1" execute="@form" render="toRender">
    ...
</m:selectOne>   

It works, because JSF processes all ui components in the form, but this is not the desired behaviour.

Maybe by extending PartialViewContextFactory?

Any Hint or Help will be appreciated Thank you

landal79
  • 1,520
  • 1
  • 11
  • 26

1 Answers1

0

I found a solution, maybe not the best in performance. I extended PartialViewContextFactory, wrapping the original PartialViewContextFactory.

The new class (shown below) does the following things:

1) It creates a new CustomPartialViewContextImpl passing it the original PartialViewContext. 2) It overridesCustomPartialViewContextImpl.getExecuteIds(), in order to find all the ancestors.

A note about id list, it should be ordered from the root the leafs.

Thank you to BalusC for the find component algorithm.

    public class CustomPartialViewContextFactory extends PartialViewContextFactory {

        public static class CustomPartialViewContextImpl extends PartialViewContext {

            private FacesContext facesContext;
            private PartialViewContext wrappedViewContext;

            /**
             * Id to execute during ajax request.
             */
            private Collection<String> executeIds;

            public CustomPartialViewContextImpl(PartialViewContext wrappedViewContext, FacesContext facesContext) {
                this.facesContext = facesContext;
                this.wrappedViewContext = wrappedViewContext;
            }

            public Collection<String> getExecuteIds() {
                if(this.executeIds == null){                
                    this.executeIds = findAncestors(executeIds);
                }

                return this.executeIds;
            }

            // ... delegate other methods to wrappedViewContext

            private Collection<String> findAncestors(Collection<String> executeIds) {
                Set<String> ids = new HashSet<>(executeIds);
                for (String id : executeIds) {
                    findAncestors(ids, findComponent(id, facesContext));
                }


                List<String> idList = new ArrayList<>(ids);
                Collections.reverse(idList);
                return idList;
            }

            private UIComponent findComponent(final String id,  FacesContext context){
                UIViewRoot root = context.getViewRoot();
                final UIComponent[] found = new UIComponent[1];
                root.visitTree(new FullVisitContext(context), new VisitCallback() {
                    @Override
                    public VisitResult visit(VisitContext context, UIComponent component) {
                        if(component.getId().equals(id)){
                            found[0] = component;
                            return VisitResult.COMPLETE;
                        }
                        return VisitResult.ACCEPT;
                    }
                });
                return found[0];
            }

            private void findAncestors(Set<String> ids, UIComponent component) {

                if(component == null){
                    return;
                }

                UIComponent parent = component.getParent();

                if (parent == null) {
                    return;
                }


                String clientId = parent.getClientId(facesContext);
                if (StringUtils.isBlank(clientId) || ids.contains(clientId)) {
                  return;
                }

                ids.add(clientId);                                  

                findAncestors(ids, parent);
            }

        }

        private PartialViewContextFactory parentFactory;

        public CustomPartialViewContextFactory(PartialViewContextFactory parentFactory) {
            this.parentFactory = parentFactory;
        }

        @Override
        public PartialViewContext getPartialViewContext(FacesContext context) {
            return new CustomPartialViewContextImpl(parentFactory.getPartialViewContext(context), context);
        }

        @Override
        public PartialViewContextFactory getWrapped() {
            return this.parentFactory;
        }

    }
Community
  • 1
  • 1
landal79
  • 1,520
  • 1
  • 11
  • 26