Oracle ADF : Bindings or not Bindings ?

Lorsque l’on veut manipuler les champs d’une page JSPX ou JSFF, souvent le premier réflexe est de mettre en place un Binding entre la page et le ManagedBean associé. Cette facilité a cependant l’inconvénient de mettre en œuvre des mécanismes qui ne sont pas obligatoirement nécessaires (Setters, Getters, Bindings de composants ADF Rich) et qui peuvent amener des lourdeurs voir des dysfonctionnements . Or plus le code est simple, plus le fonctionnement est rapide et la maintenance aisée.

 
 
Dans cet article, je vais exposer un exemple qui consiste à créer dynamiquement 5 champs de type InputText à l’écran, lire leurs contenus et afficher l’ensemble des valeurs lues dans un champ résultat.
3 boutons seront utilisés :

  • Un bouton qui permet de créer dynamiquement les champs.
  • Un bouton qui permet, après saisie de valeurs dans tous les champs, de lire leurs contenus et les afficher dans un champ résultat.
  • Un  bouton qui permet de supprimer les champs.

Avec Bindings

Le principe consiste à mettre en place pour les composants suivants :

  • Panneau dans lequel les champs seront créés (dynPanelGroupBind).
  • Champ résultat (logBind) sous la forme d’un inputtext (permettant d’afficher plusieurs lignes) dans lequel seront affichées les valeurs lues dans les champs créés.

un Binding qui permettra dans le ManagedBean d’implémenter l’exemple.

JSFF

        <af:panelStretchLayout id="psl1" startWidth="200px" endWidth="200px">
          <f:facet name="center">
              <af:panelGroupLayout id="dynPanelGroupBind" binding="#{welcomeBean.panelGroupDyn}"/>
          </f:facet>
          <f:facet name="start">
            <af:panelGroupLayout id="pgl2">
              <af:commandButton text="Afficher les champs" id="cb4"
                                actionListener="#{welcomeBean.createFieldBind}"
                                partialSubmit="true"/>
              <af:spacer height="5" id="s4"/>
              <af:commandButton text="Lire le contenu des champs" id="cb5"
                                actionListener="#{welcomeBean.readFieldBind}"
                                partialSubmit="true"/>
              <af:spacer  height="5" id="s5"/>
              <af:commandButton text="Supprimer les champs" id="cb6"
                                actionListener="#{welcomeBean.deleteFieldBind}"
                                partialSubmit="true"/>
              <af:spacer height="5" id="s6"/>
              <af:inputText label="" id="logBind" rows="10"
                   binding="#{welcomeBean.logBind}"/>
            </af:panelGroupLayout>            
          </f:facet>
        </af:panelStretchLayout>

 ManagedBean

Le principe consiste à utiliser la référence au panneau (dynPanelGroupBind)  de la page pour créer les champs (parmInputBind + séquence) et effectuer de même pour la lecture des valeurs ainsi que pour la suppression. La référence au composant InputText (logBind) est utilisée pour afficher les données lues dans les champs créés.

 public class WelcomeBean {
    private RichPanelGroupLayout panelGroupDyn;
    private RichInputText logBind;
    public WelcomeBean() {
        super();
    }
    public void createFieldBind(ActionEvent actionEvent) {
        for (int id = 0; id < 5; id++) {
            RichInputText inputText = new RichInputText();
            inputText.setId("parmInputBind" + id);
            inputText.setLabel("Label bind " + id);
            panelGroupDyn.getChildren().add(inputText);
        }
        AdfFacesContext.getCurrentInstance().addPartialTarget(panelGroupDyn);
    }
    public void readFieldBind(ActionEvent actionEvent) {
        String log = "Valeurs lues\n";
        RichInputText inputText = null;
        for (UIComponent component: panelGroupDyn.getChildren()) {
            if (component.getRendererType().contentEquals("oracle.adf.rich.Text")) {
                inputText = (RichInputText) component;
                if (null != inputText.getValue()) {
                    log = log + inputText.getValue().toString() + "\n";
                }
            }
        }
        logBind.setValue(log);
        AdfFacesContext.getCurrentInstance().addPartialTarget(logBind);
    }
    public void deleteFieldBind(ActionEvent actionEvent) {
        panelGroupDyn.getChildren().clear();
        AdfFacesContext.getCurrentInstance().addPartialTarget(panelGroupDyn);
        logBind.setValue(null);
        AdfFacesContext.getCurrentInstance().addPartialTarget(logBind);
    }
    public void setPanelGroupDyn(RichPanelGroupLayout panelGroupDyn) {
        this.panelGroupDyn = panelGroupDyn;
    }
    public RichPanelGroupLayout getPanelGroupDyn() {
        return panelGroupDyn;
    }
    public void setLogBind(RichInputText logBind) {
        this.logBind = logBind;
    }
    public RichInputText getLogBind() {
        return logBind;
    }
}

Sans Bindings

JSFF

        <af:panelStretchLayout id="psl1" startWidth="200px" endWidth="200px">
          <f:facet name="center">
              <af:panelGroupLayout id="dynPanelGroup"  partialTriggers="cb1 cb3"/>
          </f:facet>
          <f:facet name="start">
            <af:panelGroupLayout id="pgl2">
              <af:commandButton text="Afficher les champs" id="cb1"
                                actionListener="#{welcomeBean.createField}"
                                partialSubmit="true"/>
              <af:spacer height="5" id="s1"/>
              <af:commandButton text="Lire le contenu des champs" id="cb2"
                                actionListener="#{welcomeBean.readField}"
                                partialSubmit="true"/>
              <af:spacer  height="5" id="s2"/>
              <af:commandButton text="Supprimer les champs" id="cb3"
                                actionListener="#{welcomeBean.deleteField}"
                                partialSubmit="true"/>
              <af:spacer height="5" id="s3"/>
              <af:inputText label="" id="log" rows="10" partialTriggers="cb2 cb3"/>
            </af:panelGroupLayout>            
          </f:facet>
        </af:panelStretchLayout>

ManagedBean

public class WelcomeBean {
    private static final String DYN_COMPONENT = "dynPanelGroup";
    private static final String LOG_MESSAGE = "log";
    public WelcomeBean() {
        super();
    }
    public void createField(ActionEvent actionEvent) {
        UIComponent container =
          FacesContext.getCurrentInstance().getViewRoot().findComponent(DYN_COMPONENT);
        if (container != null) {
            for (int id = 0; id < 5; id++) {
                RichInputText inputText = new RichInputText();
                inputText.setId("parmInput" + id);
                inputText.setLabel("Label " + id);
                container.getChildren().add(inputText);
            }
        }
    }
    public void readField(ActionEvent actionEvent) {
        UIComponent container =
          FacesContext.getCurrentInstance().getViewRoot().findComponent(DYN_COMPONENT);
        List<UIComponent> children = container.getChildren();
        String log = "Valeurs lues\n";
        RichInputText inputText = null;
        for (UIComponent component: children) {
            if (component.getRendererType().contentEquals("oracle.adf.rich.Text")) {
                inputText = (RichInputText) component;
                if (null != inputText.getValue()) {
                    log = log + inputText.getValue().toString() + "\n";
                }
            }
        }
        UIComponent inputextLog =
          FacesContext.getCurrentInstance().getViewRoot().findComponent(LOG_MESSAGE);
        RichInputText message = (RichInputText) inputextLog;
        message.setValue(log);
    }
    public void deleteField(ActionEvent actionEvent) {
        UIComponent container =
          FacesContext.getCurrentInstance().getViewRoot().findComponent(DYN_COMPONENT);
        container.getChildren().clear();
        UIComponent inputextLog =
          FacesContext.getCurrentInstance().getViewRoot().findComponent(LOG_MESSAGE);
        RichInputText message = (RichInputText) inputextLog;
        message.setValue(null);
    }
}

 Conclusion

Pour le même résultat, deux façons de coder. Je pense qu’il est préférable de privilégier les solutions simples qui mettent en œuvre le moins de mécanisme possible.Toutefois, il ne faut pas perdre de vue que mettre en place un Bindings pour un composant Rich n’est pas une bonne pratique tant pour les scopes supérieurs au Request (Les composants Rich ne sont pas sérialisable) que pour le scope Request lui même (risque de comportement non attendu entre 2 vues utilisant un nom de composant identique). Il est recommandé de remplacer la variable membre du composant par une référence. Le code ci-dessous illustre l’utilisation du ComponentReference.

ManagedBean

 public class WelcomeBean {
    private ComponentReference panelGroupDyn;
    private ComponentReference logBind;
    public WelcomeBean() {
        super();
    }
    public void createFieldBind(ActionEvent actionEvent) {
        for (int id = 0; id < 5; id++) {
            RichInputText inputText = new RichInputText();
            inputText.setId("parmInputBind" + id);
            inputText.setLabel("Label bind " + id);
            getPanelGroupDyn().getChildren().add(inputText);
        }
        AdfFacesContext.getCurrentInstance().addPartialTarget(getPanelGroupDyn());
    }
    public void readFieldBind(ActionEvent actionEvent) {
        String log = "Valeurs lues\n";
        RichInputText inputText = null;
        for (UIComponent component: getPanelGroupDyn().getChildren()) {
            if (component.getRendererType().contentEquals("oracle.adf.rich.Text")) {
                inputText = (RichInputText) component;
                if (null != inputText.getValue()) {
                    log = log + inputText.getValue().toString() + "\n";
                }
            }
        }
        getLogBind().setValue(log);
        AdfFacesContext.getCurrentInstance().addPartialTarget(getLogBind());
    }
    public void deleteFieldBind(ActionEvent actionEvent) {
        getPanelGroupDyn().getChildren().clear();
        AdfFacesContext.getCurrentInstance().addPartialTarget(getPanelGroupDyn());
        getLogBind().setValue(null);
        AdfFacesContext.getCurrentInstance().addPartialTarget(getLogBind());
    }
    public void setPanelGroupDyn(RichPanelGroupLayout panelGroupDyn) {
        this.panelGroupDyn = ComponentReference.newUIComponentReference(panelGroupDyn);
    }
    public RichPanelGroupLayout getPanelGroupDyn() {
        if(this.panelGroupDyn != null){
            return (RichPanelGroupLayout) this.panelGroupDyn.getComponent();
        }
        return null;
    }
    public void setLogBind(RichInputText logBind) {
        this.logBind = ComponentReference.newUIComponentReference(logBind);
    }
    public RichInputText getLogBind() {
        if(this.logBind != null){
            return (RichInputText) this.logBind.getComponent();
        }        
        return null;
    }
}

Liens

Des backing et managed beans bien écrits et optimisés