Tuesday, March 17, 2015

ADF: Partial Rollback

Recently I developed a page where I provided edit functionality for a table row in a Popup with Dialog having Save/Cancel buttons.

If user add/edit/remove row and click Save, the data is getting saved. And if user clicks on Cancel button, the updated data/action inside popup needs to be revert back, but its not happening. af:resetActionListener won't help.

Initially I thought of execute rollback when user clicked on Cancel button, but it impacts other changes in the page. So I tried below steps to perform partial rollback to revert only changes done in Popup. Here I used similar save point concept explained in Link.

1. Add following methods to Bean class to create/restore/remove save point.

    public static DCBindingContainer getDCBindingContainer() {
        return (DCBindingContainer)getBindingContainer();
    }

    public static BindingContainer getBindingContainer() {
        return (BindingContainer)resolveExpression("#{bindings}");
    }

    public static Object resolveExpression(String expression) {
        FacesContext facesContext = getFacesContext();
        Application app = facesContext.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesContext.getELContext();
        ValueExpression valueExp =
            elFactory.createValueExpression(elContext, expression,
                                            Object.class);
        return valueExp.getValue(elContext);
    }

    public static FacesContext getFacesContext() {
        return FacesContext.getCurrentInstance();
    }

    public void createSavePoint(ActionEvent actionEvent) {
        DCBindingContainer bindingContainer = getDCBindingContainer();
        DCDataControl dcDataControl = bindingContainer.getDataControl();
        String spID = (String)dcDataControl.createSavepoint();
        AdfFacesContext.getCurrentInstance().getPageFlowScope().put("spID",
                                                                    spID);
        System.out.println("Save Point Created: " + spID);
    }

    public void restoreSavePoint() {
        String spID =
            (String)AdfFacesContext.getCurrentInstance().getPageFlowScope().get("spID");
        DCBindingContainer bindingContainer = getDCBindingContainer();
        DCDataControl dcDataControl = bindingContainer.getDataControl();
        dcDataControl.restoreSavepoint(spID);
        System.out.println("Save Point Restored: " + spID);
    }

    public void removeSavePoint() {
        AdfFacesContext.getCurrentInstance().getPageFlowScope().put("spID",
                                                                    null);
        System.out.println("Save Point Removed");
    }

2. Add button and call createSavePoint method when clicked on it. Since popup and also actionListener to be called at same time, set the showPopupBehaviour's TriggerType as Click.

     <af:commandButton text="Edit" id="cbj4"
                                       partialSubmit="true"
                                      actionListener="#{pageFlowScope.editBean.createSavePoint}">
                          <af:showPopupBehavior triggerType="click"
                                                popupId="editPop"/>
      </af:commandButton>

3. Set ContentDelivery of the af:popup to lazyUncached to display value from the base attribute. Set af:dialog 'type' as 'yesNo' and 'closeIconVisible' to 'false'.Add dialogListener method to listen to actions from Dialog.

      <af:popup id="editPop"
                  contentDelivery="lazyUncached"
                 <af:dialog id="d451" type="yesNo" title="Edit Host"
                                   dialogListener="#{pageFlowScope.editBean.editDialogListener}"
                                   closeIconVisible="false"
                                   affirmativeTextAndAccessKey="Save"
                                   noTextAndAccessKey="Cancel">
 <af:table value=" ..../>
        </af:dialog>
     </af:popup>

4. Add following Dialog Listener method to Bean class to call restore/remove save point methods based on dialog action.

  public void editDialogListener(DialogEvent dialogEvent) {
        Outcome outcome = dialogEvent.getOutcome();
        System.out.println("Outcome: " + outcome);
        if (outcome == Outcome.yes) {
            removeSavePoint();
        } else {
            restoreSavePoint();
        }
    }

5. Add Dialog component id to table's Partial trigger property to refresh when Save/Cancel performed.

   <af:table partialTriggers="d451" value=" ..../>