IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JSF Java Discussion :

[JSF] Création de formulaire dynamique, Pb UIViewRoot


Sujet :

JSF Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Avril 2004
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Avril 2004
    Messages : 24
    Par défaut [JSF] Création de formulaire dynamique, Pb UIViewRoot
    Bonjour à tous,

    Mon problème je pense pourras peut-être en intéresser plus d'un. J'ai fait quelques recherches sur le net mais je n'arrive pas à trouver de solution alors j'en appel aux experts JSF.

    Mon but est de faire un formulaire dynamique. En gros, j'ai une page JSF dans laquelle se trouve un bouton "Add". A chaque clique sur le bouton j'ajoute un composant dans la page, en l'ajoutant simplement à un panelGrid.
    J'ai un bean dans lequel j'initialise la grid, puis ensuite une action méthod qui ajoute à chaque clique un composat à la grid.

    dans getGrid() :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    UIOutput output = new UIOutput();
    output.setId("myoutput");
    output.setValue("Hello");
    grid = new HtmlPanelGrid();
    grid.getChildren().add(output);
    dans addComponentToGrid() :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    nbCells++;
    for(int i=0;i<nbCells;i++) {
    UIOutput output = new UIOutput();
    output.setId("myOutputID" + <quelquechosedunique);
    output.setValue("Valeur ajoutée");
    grid.getChildren().add(output);
    }
    return null; // on recharge la même page
    ici nbCells est un attribut du bean qui stock le nombre de fois où je passe dans la méthode addComponentToGrid().

    Le problème est le suivant : cela marche une fois, deux fois, trois fois puis le comportement devient complètement irréaliste et des anciens objets sont ajoutés à la liste...
    J'ai alors été voir dans le debugger dans l'objet UIViewRoot du FaceContext et là je vois que des fois d'anciens objets réapparaissent dans la hièrarchie puis des fois disparraissent!!! Comme si un cache était géré par JSF mais que certains objets disparaissaient et réapparaissaient.
    Bref je ne comprends pas grand chose à la gestion du UIViewRoot donc si quelqu'un pouvait m'expliquer ce qui se passe je lui en serais très reconnaissant.

    Merci d'avance à tous.
    ++

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 31
    Par défaut
    Salut,

    j'ai récemment dû créer dynamiquement des UIComponents dans mon viewroot et j'ai eu un problème similaire.

    Lorsque j'ajoutais un composant, j'avais l'impression qu'il parcourait d'abord son arbre pour contrôler si un composant similaire n'avait pas été déjà crée. Et donc j'avais des comportements très étranges. Parfois il ne me créait pas le composant, parfois il l'initialisait avec des anciennes valeur etc...

    Ce que j'ai fait pour contourner le problème, c'est que pour toute création de composant je passe par une méthode qui contrôle l'existence du composant avant de le créer.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
        /**
         * Création ou récupération d'un composant déjà existant ayant le même nom.
         * @param componentType Type du composant à créer
         * @param componentName Nom du composant à créer
         * @return Le composant crée ou trouvé
         */
        private UIComponent createComponent(final String componentType, final  String componentName) {
    	final Application application = FacesContext.getCurrentInstance().getApplication();
    	UIComponent component = null;
    	try {
    	    component = FacesContext.getCurrentInstance().getViewRoot().findComponent(componentName);
    	    LOG.info("Le composant " + componentName + " a été trouvé.");
    	} catch (Exception e) {
    	    LOG.info("Le composant " + componentName + " n'a pas été trouvé, création. " + e);
    	}
    	if (component == null)
    	    component = application.createComponent(componentType);
     
    	return component;
        }
    Et pour créer mon composant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     HtmlInputHidden inputHidden = (HtmlInputHidden) createComponent(HtmlInputHidden.COMPONENT_TYPE, "parameterModulation");
    ça ne répond pas directement à ton problème à mon avis mais ça pourra sans doute te guider.

  3. #3
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    ta logique me semble relativement bonne. J'ai déjà travaillé avec des formulaires dynamiques par le passé et l'ajout de children dans un arbre ne pose aucun problème en JSF. On peut même, dans un renderer, crée un composant temporaire histoire de l'utiliser pour du rendu (si ma mémoire est bonne tomahawk le fait sur certains composant implicant plusieurs textfield :p)

    par contre deux points important.

    Très important: on n'appelle jamais le constructeur d'un composant JSF. Il y a beaucoup plus dans les composant JSF que leur simple construction. Utilise application.createComponent(componentType)

    Utilise un componentBinding sur ton panelgrid, qui point sur une propriété de ton bean, ca permettra une manipulation facile du datagrid

    N'oublie pas l'id

    attention, l'arbre pour une vue (en tout cas avec facelets) est construit une fois pour toute et mis dans la session user, tout changement est définitif pour l'utilisateur en cours -> te faudra réinitialiser aussi en force après l'action


    Autre suggestion: pourquoi y aller en force en augmentant le nombre de composant dans l'arbre alors que tu pourrais avoir facilement un comportement similaire avec un <h:dataTable/> qui itérerait sur un bean?

  4. #4
    Membre averti
    Inscrit en
    Avril 2004
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Avril 2004
    Messages : 24
    Par défaut
    Merci à vous deux d'avoir répondu aussi vite.

    dachman : j'ai suivi ton raisonnement, je récupère mon objet à partir de son id et essaie ensuite de lui ajouter des composants. La récupération est ok, j'arrive à ajouter des children au composant mais malheuresement un pb se pose : d'anciens composants apparaissent parfois dans la liste, parfois non. Si je te comprend bien, il me semble que c'est le même problème que tu as rencontré. Du coup, je me retrouve parfois avec trop de composants dans l'objet, puisque d'anciens composants réapparaissent.
    J'ai bien essayé de réinitialiser l'objet récupéré en utilisant le createComponent() sur celui-ci mais là c'est encore pire, l'objet ne change plus du tout...

    tchize_ : j'utilise facelets et je me demande si effectivement ce n'est pas ça qui pose pb.
    Pour répondre à ta dernière question je ne comprends pas tout à fait ce que tu veux faire En fait à chaque fois que je clique sur submit je reload le même formulaire en ajoutant une série de composants à la grille, mais ces composants peuvent être différents. Si je loop sur une liste de composants
    comment puis-je faire le binding dans la page? Aurait-tu un petit example de code?

    Merci encore à vous deux,
    ++

  5. #5
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    effectivement, le datatable ou le datagrid marche moins bien si les composants sont de types différents. Cependant, si le nombre de type est limités, tu peux faire ceci: (tomahawk datalist)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <t:datalist value="#{bean.collectionDeTrucs}" var="truc">
       <h:inputText value="#{truc.value}" rendered="#{truc.type='Text'}"/>
       <h:selectBoolean value="#{truc.value}" rendered="#{truc.type='Booleen'}"/>
        <t:inputCalendar  value="#{truc.value}" renderAsPopup="true"
        renderPopupButtonAsImage="true" 
        popupDateFormat="MM/dd/yyyy"
        alt="Calendar" title="Calendar" renderd="#{truc.type='Date'}">
     </t:inputCalendar>
    </t:datalist>
    A chaque itération de la boucle on va donc afficher l'un ou l'autre composant en fonction du type de la variable courante. Avantage, tu ne gère pas l'ajout d'élément dans ton backing bean (tu préserve en quelque sorte la séparation vue / modèle.

    Inconvénient, t'as besoin d'un modèle dans lequel chaque élément de la collection a la forme

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public class MonObjetDeCollection {
        public Object getValue() {...}
        public void setValue(Object object){....}
        public String getType() {.....}
    }
    (ça pourrait être évidement beaucoup plus beau à lire avec des génériques mais le principe reste le même).

    Ce genre de méthode est bien adaptée si la quantité de types différents est faible par rapport à la quantité de donnée (exemple, 100 données de 10 type différents, ok. 15 données de 20 types potentiellement différent, moins ok :p)


    Ce genre de méthode marche moins bien si tu dois faire des insertion ailleurs qu'en fin de liste, mais en modifiant l'arbre de composant c'est de toutes façons aussi le bordel en milieu de liste. De plus cette méthode avec le rendered permet d'ajouter ce que tu vex comme décoration autour facilement dans ton xhtml, tandis qu'en faisant ça avec du code, tu perd tout chance de gérer l'aspect ton interface via le xhtml

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 31
    Par défaut
    Citation Envoyé par JCitrouille Voir le message
    dachman : j'ai suivi ton raisonnement, je récupère mon objet à partir de son id et essaie ensuite de lui ajouter des composants. La récupération est ok, j'arrive à ajouter des children au composant mais malheuresement un pb se pose : d'anciens composants apparaissent parfois dans la liste, parfois non. Si je te comprend bien, il me semble que c'est le même problème que tu as rencontré. Du coup, je me retrouve parfois avec trop de composants dans l'objet, puisque d'anciens composants réapparaissent.
    J'ai bien essayé de réinitialiser l'objet récupéré en utilisant le createComponent() sur celui-ci mais là c'est encore pire, l'objet ne change plus du tout...
    Pour les anciens composants qui réapparaissent, tu peux essayer de leur mettre un .setRendered(false) pour éviter qu'ils soient interprétés au rendu. Mais, même si tu les vois dans l'arbre, es-tu sûr qu'ils sont reproduits sur ton interface?

  7. #7
    Membre averti
    Inscrit en
    Avril 2004
    Messages
    24
    Détails du profil
    Informations forums :
    Inscription : Avril 2004
    Messages : 24
    Par défaut
    Salut,

    tchize_ : ça y est j'ai compris, pas mal du tout ton idée, faut voir si ça s'adapte dans mon cas. En fait j'ai un bouton en haut de la page qui lors de chaque click me génère le bout de formulaire. Comme je fais un reload de la page je peux alimenter la liste et reconstruire l'arbre à chaque fois, c'est pas mal.

    dachman : en fait je ne comprends toujours pas comment JSF gère l'arbre de composants. J'ai fait pas mal de tests et la façon dont les anciens composants réapparaissent est aléatoire... Bon il doit y avoir une explication derrière mais disons que je ne l'ai pas encore trouvée... Pour répondre à ta question oui, les composants sont effectivement affichés dans la page JSF.
    Ce que je fais pour l'instant c'est que je reconstruit la datagrid à chaque coup en récupérant celle-ci du UIViewRoot. Une fois l'objet récupéré je fais un remove sur tout ses composants de cette grid pour être sûr de ne pas en avoir en cache et je reconstruits la grid. C'est pas super car à chaque fois je dois faire les contrôles et ensuite reconstruire la liste. Mais bon ça marche. Je préfèrerais comprendre le mécanisme de cache de JSF.

    Merci encore à vous, je vous tiens au courant de mes avancées.
    ++

Discussions similaires

  1. CRÉATION DE FORMULAIRE DYNAMIQUE
    Par blueette dans le forum Modélisation
    Réponses: 0
    Dernier message: 26/04/2013, 13h49
  2. [AC-2003] Création de formulaire dynamique
    Par newtownz dans le forum VBA Access
    Réponses: 8
    Dernier message: 17/08/2009, 15h47
  3. [2.0] Création de formulaires dynamiques
    Par mister3957 dans le forum ASP.NET
    Réponses: 2
    Dernier message: 13/08/2007, 16h22
  4. [VB6] Création de formulaire dynamique de recherche
    Par Mat_76 dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 07/05/2007, 09h41
  5. Création de formulaires dynamique?
    Par akademiks dans le forum JSF
    Réponses: 4
    Dernier message: 27/04/2007, 10h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo