Comment trouver l'ID client du component pour ajax update / render? Ne peut pas trouver le composant avec l'expression " foo "référencé de" barre"

le code suivant est inspiré de PrimeFaces DataGrid + DataTable Tutorials et mis dans un <p:tab> d'un <p:tabView> résidant dans un <p:layoutUnit> d'un <p:layout> . Voici la partie intérieure du code (à partir du composant p:tab ); la partie extérieure est triviale.

<p:tabView id="tabs">
    <p:tab id="search" title="Search">                        
        <h:form id="insTable">
            <p:dataTable id="table" var="lndInstrument" value="#{instrumentBean.instruments}">
                <p:column>
                    <p:commandLink id="select" update="insTable:display" oncomplete="dlg.show()">
                        <f:setPropertyActionListener value="#{lndInstrument}" 
                                        target="#{instrumentBean.selectedInstrument}" />
                        <h:outputText value="#{lndInstrument.name}" />
                    </p:commandLink>                                    
                </p:column>
            </p:dataTable>
            <p:dialog id="dlg" modal="true" widgetVar="dlg">
                <h:panelGrid id="display">
                    <h:outputText value="Name:" />
                    <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
                </h:panelGrid>
            </p:dialog>                            
        </h:form>
    </p:tab>
</p:tabView>

quand je clique sur le <p:commandLink> , le code cesse de fonctionner et donne le message:

ne trouve pas de composant avec expression "insTable:affichage" référencé à partir de "onglets:insTable:sélectionner".

quand j'essaie la même chose en utilisant <f:ajax> , il échoue avec un message différent disant essentiellement la même chose:

<f:ajax> contient un id inconnu "insTable: display" ne peut pas le localiser dans le contexte du composant "tabs: insTable: select "

comment cela est-il causé et comment puis-je le résoudre?

122
demandé sur BalusC 0000-00-00 00:00:00

4 réponses

recherche dans la sortie HTML pour l'identification réelle du client

vous devez regarder dans la sortie HTML générée pour trouver le bon ID client. Ouvrez la page dans le navigateur, faites un clic droit et View Source . Localisez la représentation HTML de la composante JSF d'intérêt et prenez son id comme ID client. Vous pouvez l'utiliser de manière absolue ou relative en fonction du conteneur de nommage courant. Voir le chapitre suivant.

Note: si elle contient un indice d'itération tel que :0: , :1: , etc (parce qu'elle se trouve à l'intérieur d'un composant d'itération), alors vous devez réaliser que la mise à jour d'un cycle d'itération spécifique n'est pas toujours supportée. Voir le bas de la réponse pour plus de détails à ce sujet.

mémoriser NamingContainer composants et toujours leur donner une identification fixe

si un composant que vous souhaitez référencer par ajax process / execute/update/render est à l'intérieur de la même NamingContainer parent, il suffit de faire référence à sa propre carte D'identité.

<h:form id="form">
    <p:commandLink update="result"> <!-- OK! -->
    <h:panelGroup id="result" />
</h:form>

si c'est et non à l'intérieur du même NamingContainer , alors vous devez y faire référence en utilisant un numéro d'identification absolu du client. Un identifiant de client absolu commence par le caractère de séparation NamingContainer , qui est par défaut : .

<h:form id="form">
    <p:commandLink update="result"> <!-- FAIL! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
    <p:commandLink update=":result"> <!-- OK! -->
</h:form>
<h:panelGroup id="result" />
<h:form id="form">
    <p:commandLink update=":result"> <!-- FAIL! -->
</h:form>
<h:form id="otherform">
    <h:panelGroup id="result" />
</h:form>
<h:form id="form">
    <p:commandLink update=":otherform:result"> <!-- OK! -->
</h:form>
<h:form id="otherform">
    <h:panelGroup id="result" />
</h:form>

NamingContainer composants sont par exemple <h:form> , <h:dataTable> , <p:tabView> , <cc:implementation> (ainsi, tous les composants composites), etc. Vous les reconnaissez facilement en regardant la sortie HTML générée, leur ID sera prépayé à l'ID du client généré de tous les composants enfant. Notez que lorsqu'ils n'ont pas d'ID fixe, JSF utilisera un ID autogénéré au format j_idXXX . Vous devriez absolument éviter cela en leur donnant une identification fixe. Le OmniFaces NoAutoGeneratedIdViewHandler peut être utile dans ce pendant le développement.

si vous savez trouver le javadoc du UIComponent en question, alors vous pouvez aussi vérifier s'il implémente l'interface NamingContainer ou non. Par exemple, le HtmlForm (le UIComponent derrière <h:form> étiquette) montre qu'il met en œuvre NamingContainer , mais le HtmlPanelGroup (le UIComponent derrière <h:panelGroup> étiquette) ne le montre pas, de sorte qu'il ne met pas en œuvre NamingContainer . voici le javadoc de tous les composants standards et voici le javadoc des PrimeFaces .

résoudre votre problème

donc dans votre cas de:

<p:tabView id="tabs"><!-- This is a NamingContainer -->
    <p:tab id="search"><!-- This is NOT a NamingContainer -->
        <h:form id="insTable"><!-- This is a NamingContainer -->
            <p:dialog id="dlg"><!-- This is NOT a NamingContainer -->
                <h:panelGrid id="display">

la sortie HTML générée de <h:panelGrid id="display"> ressemble à ceci:

<table id="tabs:insTable:display">

vous devez prendre exactement ce id comme ID client et puis préfixe avec : pour l'usage dans update :

<p:commandLink update=":tabs:insTable:display">

référence à l'extérieur include/tagfile / composite

si ce lien de commande est à l'intérieur d'un include/tagfile, et que la cible est à l'extérieur de celui-ci, et que vous ne connaissez donc pas nécessairement L'ID du conteneur de nommage parent du conteneur de nommage courant, alors vous pouvez le référencer dynamiquement via UIComponent#getNamingContainer() comme suit:

<p:commandLink update=":#{component.namingContainer.parent.namingContainer.clientId}:display">

ou, si cette liaison de commande est à l'intérieur d'un composant composite et que la cible est à l'extérieur de celui-ci:

<p:commandLink update=":#{cc.parent.namingContainer.clientId}:display">

ou, si la liaison de commande et la cible se trouvent à l'intérieur du même élément composite:

<p:commandLink update=":#{cc.clientId}:display">

Voir aussi Obtenir l'id du parent conteneur de dénomination dans le modèle de rendu / mise à jour de l'attribut

Comment ça fonctionne sous les couvertures

tout ça est défini comme étant "expression de recherche" dans le UIComponent#findComponent() javadoc :

Un la recherche de l'expression se compose d'un identifiant (qui est parfaitement adaptée à l'encontre de la propriété id de UIComponent , ou une série de tels identifiants liés par le UINamingContainer#getSeparatorChar valeur de caractère. L'algorithme de recherche devrait fonctionner comme suit, bien que d'autres alogrithmes puissent être utilisés aussi longtemps le résultat final est le même:

  • Identifier UIComponent qui sera la base pour la recherche, en s'arrêtant dès que l'une des conditions suivantes est remplie:
    • si l'expression de recherche commence par le caractère séparateur (appelé expression de recherche "absolue"), la base sera la racine UIComponent de l'arbre composant. Le caractère du séparateur de tête sera enlevé, et le reste du l'expression de recherche sera traitée comme une expression de recherche" relative " comme décrit ci-dessous.
    • Sinon, si ce UIComponent est un NamingContainer il servira de base.
    • sinon, cherchez les parents de cette composante. Si un NamingContainer est rencontré, il sera la base.
    • Sinon (si aucun NamingContainer n'est rencontré) la racine UIComponent sera la base.
  • l'expression de recherche (éventuellement modifiée à l'étape précédente) est maintenant une expression de recherche" relative " qui sera utilisée pour localiser le composant (le cas échéant) qui a un id qui correspond, dans le champ d'application du composant de base. Le match se déroule comme suit:
    • si l'expression de recherche est un simple identificateur, cette valeur est comparée à la propriété id, puis récursivement à travers les facettes et les enfants de la base UIComponent (sauf que si un descendant NamingContainer est trouvé, ses propres facettes et les enfants ne sont pas recherchés).
    • si l'expression de recherche comprend plus d'un identificateur séparé par le caractère séparateur, le premier identificateur est utilisé pour localiser un NamingContainer selon les règles du point de puce précédent. Ensuite, la méthode findComponent() de ce NamingContainer sera appelée, en passant le reste de l'expression de recherche.

notez que PrimeFaces adhère également à la spécification JSF, mais RichFaces utilise "quelques exceptions supplémentaires" .

"reRender" utilise l'algorithme UIComponent.findComponent() (avec quelques exceptions supplémentaires) pour trouver le composant dans l'arbre des composants.

ces exceptions supplémentaires ne sont décrites en détail nulle part, mais on sait que les identificateurs de composants relatifs (c'est-à-dire ceux qui ne commencent pas par : ) ne sont pas seulement recherchés dans le contexte du parent le plus proche NamingContainer , mais aussi dans tous les autres composants NamingContainer dans la même vue (ce qui est un travail relativement coûteux d'ailleurs).

ne Jamais utiliser de prependId="false"

Si tout cela ne fonctionne toujours pas, vérifiez si vous n'utilisez pas <h:form prependId="false"> . Cela échouera pendant le traitement de l'ajax submit et render. Voir aussi cette question connexe: Uiiforme with prependId= "false" breaks .

faisant référence au cycle d'itération spécifique des composants itératifs

il n'a pas été possible pendant longtemps de faire référence à un item itéré spécifique dans les composants itératifs comme <ui:repeat> et <h:dataTable> comme suit:

<h:form id="form">
    <ui:repeat id="list" value="#{['one','two','three']}" var="item">
        <h:outputText id="item" value="#{item}" /><br/>
    </ui:repeat>

    <h:commandButton value="Update second item">
        <f:ajax render=":form:list:1:item" />
    </h:commandButton>
</h:form>

cependant, depuis Mojarra 2.2.5 le <f:ajax> a commencé à le soutenir (il a simplement cessé la valider; ainsi vous ne feriez plus jamais face à l'exception dans la question mentionnée plus; un autre correctif d'amélioration est prévu pour cela plus tard).

cela ne fonctionne pas encore dans les versions actuelles de MyFaces 2.2.7 et PrimeFaces 5.2. Le soutien pourrait venir dans les futures versions. Dans le même temps, votre meilleur pari est de mettre à jour le composant itératif lui-même, ou un parent dans le cas où il ne rend pas HTML, comme <ui:repeat> .

en cas d'utilisation de PrimeFaces, tenir compte des Expressions de recherche ou des sélecteurs

PrimeFaces Search Expressions vous permet de référencer des composants via les expressions de recherche d'arborescence de composants JSF. La JSF a plusieurs éléments constitutifs:

  • @this : composante actuelle
  • @form : parent UIForm
  • @all : document complet
  • @none : rien

PrimeFaces a amélioré cette avec de nouveaux mots-clés et composite support des expressions:

  • @parent : composant parent
  • @namingcontainer : parent UINamingContainer
  • @widgetVar(name) : composant tel qu'identifié par widgetVar

vous pouvez également mélanger ces mots clés dans des expressions composites telles que @form:@parent , @this:@parent:@parent , etc.

PrimeFaces Selectors (PFS) comme dans @(.someclass) vous permet de faire référence à des composants via la syntaxe de sélection jQuery CSS. Par exemple: référencement des composants ayant tous une classe de style commune dans la sortie HTML. Cela est particulièrement utile dans le cas où vous avez besoin de référence "beaucoup de" composants. Cela ne nécessite que que les composants cibles aient tous un identifiant de client dans la sortie HTML (fixe ou autogénérée, peu importe). Voir aussi Comment faire PrimeFaces Sélecteurs comme dans la mise à jour="@(.myClass) " travail?

275
répondu BalusC 2017-05-23 11:54:44

tout d'abord: autant que je sache, placer le dialogue dans un tabview est une mauvaise pratique... vous mieux la prendre...

et maintenant à votre question:

désolé, ça m'a pris du temps d'obtenir ce que vous vouliez exactement mettre en œuvre,

n'a à mon application web moi-même tout à l'heure, et ça marche

comme je l'ai dit avant de placer les p:boîte de dialogue à côté de la `p:tabView ,

quitter le dialogue p:comme vous initialement suggéré:

<p:dialog modal="true" widgetVar="dlg">
    <h:panelGrid id="display">
        <h:outputText value="Name:" />
        <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
    </h:panelGrid>
</p:dialog>   

et le P:commandlink devrait ressembler à ceci (tout ce que j'ai fait est de changer l'attribut update)

<p:commandLink update="display" oncomplete="dlg.show()">
    <f:setPropertyActionListener value="#{lndInstrument}" 
        target="#{instrumentBean.selectedInstrument}" />
    <h:outputText value="#{lndInstrument.name}" />
</p:commandLink>  

les mêmes travaux dans mon application web, et si elle ne fonctionne pas pour vous , alors je suppose qu'il ya quelque chose de mal dans votre code Java bean...

9
répondu Daniel 2013-10-28 08:11:33

c'est parce que l'onglet est aussi un conteneur de nom... votre mise à jour devrait être update="Search:insTable:display" ce que vous pouvez faire aussi bien est de placer votre dialogue en dehors du formulaire et toujours à l'intérieur de l'onglet alors il serait: update="Search:display"

5
répondu Lyrion 2011-12-26 10:59:36

Essayez de changer update="insTable:display" à update="display" . Je crois que vous ne pouvez pas préfixer l'id avec l'ID du formulaire comme ça.

0
répondu Mr.J4mes 2011-12-26 09:25:56