Wednesday, October 27, 2010

ADF Region as Source of Partial Trigger

In my previous post, I describe a way on how to expose the state represented by the pageFlowScope object of a bounded task flow (BTF) to the outside world. Now, if there's an event inside the BTF that changes it exposed state, then the dependent outside component needs to refresh as well, and the idea is for it to have a partialTriggers property that point to the region where the BTF resides.

The problem is, the events that changes the state of the task flow are actually coming from the command button components and not the region. To have a hook on these events, then you need have to combine the relative Id of the region and the Id of the command button relative to the containing region like "r1:ctb1". Below is a code snippet from sample app on my previous post.

      <af:form id="f1">
        <af:panelStretchLayout id="psl1" topHeight="50px">
          <f:facet name="center">
            <af:region value="#{bindings.samplestatefulbtftaskflow1.regionModel}"
                       id="r1"/>
          </f:facet>
          <f:facet name="top">
            <af:panelGroupLayout id="pgl1" layout="horizontal">
              <af:toolbar id="t1">
                <af:commandToolbarButton text="Approve" id="ctb1"
                                         partialTriggers="r1:ctb1 r1:ctb2 r1:ctb3"
                                         disabled="#{sessionScope.region1StateHolder['editMode'] eq 'true'}"
                                         actionListener="#{viewScope.mainForm.approve}"/>
              </af:toolbar>
              <af:outputText value="This toolbar button disables and enables depending on the ('editMode') state of the bounded task flow below:" id="ot1"/>
            </af:panelGroupLayout>
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
But in the case of a dynamic region wherein you have no idea of the Ids of the contained command button components then the solution above cannot apply.

We need to step back a little bit and rethink the solution we have above. Since we cannot reference the Ids of the components which we don't have idea about for in the case of a dynamic region, then we should only have a hook on the region itself like "r1". We are left with no choice but to modify our BTFs so that when there's an internal action event that modifies the potentially exposed state of the current BTF, then we also raise a dummy region event as follows:

    private void broadcastAsRegionEvent(FacesEvent event) {
        RichRegion region = findImmediateContainingRegion(event);
        if (region != null) {
            AttributeChangeEvent dummyRegionEvent =
                new AttributeChangeEvent(region, "dummy", null, null);
            region.broadcast(dummyRegionEvent);
        }
    }
    private RichRegion findImmediateContainingRegion(FacesEvent event) {
        UIComponent comp = event.getComponent();
        while (!(comp instanceof RichRegion)) {
            comp = comp.getParent();
        }
        if (comp instanceof RichRegion) {
            return (RichRegion)comp;
        }
        return null;
    }
Below are some sample action listeners that calls the broadcastAsRegionEvent() method above:
    public void edit(ActionEvent actionEvent) {
        //some other operations here

        getPageFlowScope().put(EDIT_MODE, true);

        //workaround so that simply referencing the region
        //as partial trigger will work
        //in real app, need to confirm if the BTF is indeed used as region
        broadcastAsRegionEvent(actionEvent);
    }

    public void cancel(ActionEvent actionEvent) {
        //some other operations here
        getPageFlowScope().put(EDIT_MODE, false);
        broadcastAsRegionEvent(actionEvent);
    }

Wheew! I have the feeling that I have gone too far off :-( . This just started on the challenge of disabling a parent page component when my bounded task flow is on edit mode. Guys, comments and feedback are very much appreciated.

You can download the sample application from here.


No comments:

Post a Comment