web-dev-qa-db-de.com

Wie finde ich die Client-ID der Komponente für Ajax Update / Render? Komponente mit dem Ausdruck "foo", auf die von "bar" verwiesen wird, kann nicht gefunden werden

Der folgende Code wurde von den PrimeFaces DataGrid + DataTable-Lernprogrammen inspiriert und in einen <p:tab> Von <p:tabView> Eingefügt, der sich in einem <p:layoutUnit> Von <p:layout> Befindet. Hier ist der innere Teil des Codes (beginnend mit der Komponente p:tab); der äußere Teil ist trivial.

<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>

Wenn ich auf <p:commandLink> Klicke, funktioniert der Code nicht mehr und gibt die folgende Meldung aus:

Die Komponente mit dem Ausdruck "insTable: display", auf die auf den Registerkarten "insTable: select" verwiesen wird, kann nicht gefunden werden.

Wenn ich dasselbe mit <f:ajax> Versuche, schlägt dies mit einer anderen Meldung fehl, die im Grunde dasselbe sagt:

<f:ajax> Enthält eine unbekannte ID "insTable: display" kann sie nicht im Kontext der Komponente "tabs: insTable: select" finden

Wie entsteht das und wie kann ich es lösen?

126
perissf

Suchen Sie in der HTML-Ausgabe nach der tatsächlichen Client-ID

Sie müssen in der generierten HTML-Ausgabe nachsehen, um die richtige Client-ID zu ermitteln. Öffne die Seite im Browser, mache einen Rechtsklick und Quelltext anzeigen. Suchen Sie die HTML-Darstellung der gewünschten JSF-Komponente und nehmen Sie deren id als Client-ID. Sie können es je nach aktuellem Namenscontainer absolut oder relativ verwenden. Siehe folgendes Kapitel.

Hinweis: Wenn es zufällig einen Iterationsindex wie :0:, :1: Usw. enthält (weil er sich in einer iterierenden Komponente befindet), müssen Sie sich darüber im Klaren sein, dass die Aktualisierung einer bestimmten Iterationsrunde nicht immer unterstützt wird. Siehe unten in der Antwort, um mehr darüber zu erfahren.

Merken Sie sich NamingContainer Komponenten und geben Sie ihnen immer eine feste ID

Befindet sich eine Komponente, auf die Sie mit ajax process/execute/update/render verweisen möchten, im selben NamingContainer übergeordneten Element, verweisen Sie einfach auf ihre eigene ID.

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

Wenn sich nicht innerhalb desselben NamingContainer befindet, müssen Sie mit einer absoluten Client-ID darauf verweisen. Eine absolute Client-ID beginnt mit dem Trennzeichen NamingContainer, das standardmäßig : Lautet.

<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 Komponenten sind beispielsweise <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation> (also alle zusammengesetzten Komponenten) ) usw. Sie erkennen sie leicht an der generierten HTML-Ausgabe. Ihre ID wird der generierten Client-ID aller untergeordneten Komponenten vorangestellt. Beachten Sie, dass JSF, wenn sie keine feste ID haben, eine automatisch generierte ID im Format j_idXXX Verwendet. Sie sollten dies unbedingt vermeiden, indem Sie ihnen einen festen Ausweis geben. Das OmniFaces NoAutoGeneratedIdViewHandler kann dabei während der Entwicklung hilfreich sein.

Wenn Sie wissen, dass Sie das Javadoc des fraglichen UIComponent finden, können Sie dort auch einfach überprüfen, ob das Interface NamingContainer implementiert ist oder nicht. Zum Beispiel zeigt das HtmlForm (das UIComponent hinter dem <h:form> - Tag), dass es NamingContainer implementiert, aber das HtmlPanelGroup (das UIComponent hinter <h:panelGroup> - Tag) zeigt es nicht an, so dass es NamingContainer nicht implementiert. Hier ist das Javadoc aller Standardkomponenten und Hier ist das Javadoc von PrimeFaces .

Lösen Sie Ihr Problem

Also in Ihrem Fall von:

<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">

Die generierte HTML-Ausgabe von <h:panelGrid id="display"> Sieht folgendermaßen aus:

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

Sie müssen genau dieses id als Client-ID verwenden und dann das Präfix : Für die Verwendung in update eingeben:

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

Verweise außerhalb von include/tagfile/composite

Befindet sich diese Befehlsverknüpfung in einer include/tag-Datei und befindet sich das Ziel außerhalb der Include/tag-Datei und kennen Sie daher nicht unbedingt die ID des übergeordneten Namenscontainers des aktuellen Namenscontainers, können Sie über UIComponent#getNamingContainer() wie folgt:

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

Oder, wenn sich diese Befehlsverknüpfung innerhalb einer zusammengesetzten Komponente befindet und sich das Ziel außerhalb davon befindet:

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

Oder, wenn sich sowohl die Befehlsverknüpfung als auch das Ziel in derselben zusammengesetzten Komponente befinden:

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

Siehe auch ID des übergeordneten Namenscontainers in der Vorlage für das Attribut render/update abrufen

Wie funktioniert es unter der Decke

Dies alles wird angegeben als "Suchausdruck" in das UIComponent#findComponent() javadoc :

Ein Suchausdruck besteht entweder aus einem Bezeichner (der genau mit der id-Eigenschaft eines UIComponent verglichen wird, oder aus einer Reihe solcher Bezeichner, die durch den UINamingContainer#getSeparatorChar Verknüpft sind. Der Suchalgorithmus sollte wie folgt funktionieren, es können jedoch auch alternative Algorithmen verwendet werden, solange das Endergebnis dasselbe ist:

  • Identifizieren Sie das UIComponent, das als Basis für die Suche dienen soll, indem Sie anhalten, sobald eine der folgenden Bedingungen erfüllt ist:
    • Wenn der Suchausdruck mit dem Trennzeichen beginnt (als "absoluter" Suchausdruck bezeichnet), ist die Basis der Stamm UIComponent des Komponentenbaums. Das führende Trennzeichen wird entfernt, und der Rest des Suchausdrucks wird wie unten beschrieben als "relativer" Suchausdruck behandelt.
    • Andernfalls dient dieses UIComponent als Basis, wenn es ein NamingContainer ist.
    • Suchen Sie andernfalls nach den übergeordneten Elementen dieser Komponente. Wenn ein NamingContainer angetroffen wird, ist es die Basis.
    • Andernfalls (wenn kein NamingContainer angetroffen wird) ist der Stamm UIComponent die Basis.
  • Der Suchausdruck (möglicherweise im vorherigen Schritt geändert) ist jetzt ein "relativer" Suchausdruck, der verwendet wird, um die Komponente (falls vorhanden) mit einer übereinstimmenden ID im Bereich der Basiskomponente zu lokalisieren. Das Match wird wie folgt durchgeführt:
    • Wenn der Suchausdruck ein einfacher Bezeichner ist, wird dieser Wert mit der Eigenschaft id verglichen und dann rekursiv über die Facetten und untergeordneten Elemente der Basis UIComponent (außer wenn ein Nachkomme NamingContainer gefunden wird). eigene Facetten und Kinder werden nicht durchsucht).
    • Wenn der Suchausdruck mehr als einen durch das Trennzeichen getrennten Bezeichner enthält, wird der erste Bezeichner verwendet, um ein NamingContainer nach den Regeln im vorherigen Aufzählungspunkt zu lokalisieren. Dann wird die Methode findComponent() dieses NamingContainer aufgerufen und der Rest des Suchausdrucks übergeben.

Beachten Sie, dass PrimeFaces auch die JSF-Spezifikation einhält, RichFaces jedoch "einige zusätzliche Ausnahmen" verwendet.

"reRender" verwendet den Algorithmus UIComponent.findComponent() (mit einigen zusätzlichen Ausnahmen), um die Komponente im Komponentenbaum zu finden.

Diese zusätzlichen Ausnahmen werden nirgendwo im Detail beschrieben, aber es ist bekannt, dass relative Komponenten-IDs (dh solche, die nicht mit : Beginnen) nicht nur im Kontext des nächsten übergeordneten NamingContainer, sondern auch in durchsucht werden alle anderen NamingContainer Komponenten in derselben Ansicht (was übrigens ein relativ teurer Job ist).

Verwenden Sie niemals prependId="false"

Wenn dies immer noch nicht funktioniert, überprüfen Sie, ob Sie <h:form prependId="false"> Nicht verwenden. Dies schlägt während der Verarbeitung von Ajax Submit und Render fehl. Siehe auch diese verwandte Frage: IForm mit prependId = "false" unterbricht <f: ajax render> .

Referenzieren einer bestimmten Iterationsrunde iterierender Komponenten

Es war lange Zeit nicht möglich, ein bestimmtes iteriertes Element in iterierenden Komponenten wie <ui:repeat> Und <h:dataTable> Wie folgt zu referenzieren:

<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>

Seit Mojarra 2.2.5 wird es jedoch von <f:ajax> Unterstützt (die Validierung wurde einfach abgebrochen; Sie würden also nie mehr mit der in der Frage genannten Ausnahme konfrontiert sein; eine weitere Verbesserung ist für später geplant).

Dies funktioniert nur in den aktuellen Versionen von MyFaces 2.2.7 und PrimeFaces 5.2 noch nicht. Die Unterstützung könnte in zukünftigen Versionen kommen. In der Zwischenzeit empfiehlt es sich, die iterierende Komponente selbst oder eine übergeordnete Komponente zu aktualisieren, falls keine HTML-Darstellung erfolgt, wie z. B. <ui:repeat>.

Berücksichtigen Sie bei der Verwendung von PrimeFaces Suchausdrücke oder Selektoren

PrimeFaces Search Expressions ermöglicht es Ihnen, Komponenten über JSF-Komponentenbaum-Suchausdrücke zu referenzieren. JSF hat mehrere eingebaute:

  • @this: Aktuelle Komponente
  • @form: Übergeordnetes UIForm
  • @all: Gesamtes Dokument
  • @none: Nichts

PrimeFaces hat dies mit neuen Schlüsselwörtern und Unterstützung für zusammengesetzte Ausdrücke erweitert:

  • @parent: Übergeordnete Komponente
  • @namingcontainer: Übergeordnetes UINamingContainer
  • @widgetVar(name): Komponente wie durch widgetVar identifiziert

Sie können diese Schlüsselwörter auch in zusammengesetzten Ausdrücken wie @form:@parent, @this:@parent:@parent Usw. mischen.

PrimeFaces Selectors (PFS) wie in @(.someclass) ermöglicht es Ihnen, Komponenten über die jQuery CSS-Selektorsyntax zu referenzieren. Z.B. Verweisen auf Komponenten mit allen gemeinsamen Stilklassen in der HTML-Ausgabe. Dies ist besonders hilfreich, wenn Sie auf "viele" Komponenten verweisen müssen. Dies setzt nur voraus, dass die Zielkomponenten alle eine Client-ID in der HTML-Ausgabe haben (fest oder automatisch generiert, spielt keine Rolle). Siehe auch Wie funktionieren PrimeFaces-Selektoren wie in update = "@ (. MyClass)"?

289
BalusC

zuallererst: Soweit ich weiß, ist das Platzieren von Dialogen in einer Tab-Ansicht eine schlechte Praxis. Nehmen Sie es besser raus.

und nun zu deiner frage:

entschuldigung, ich habe einige Zeit gebraucht, um herauszufinden, was genau Sie implementieren wollten.

habe gerade meine Web-App selbst gemacht und es funktioniert

wie ich bereits sagte, platziere das p: -Dialogfeld außerhalb des `p: tabView,

verlassen Sie den p: -Dialog wie ursprünglich vorgeschlagen:

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

und der p: commandlink sollte so aussehen (alles was ich getan habe ist das update Attribut zu ändern)

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

das gleiche funktioniert in meiner Web-App, und wenn es bei Ihnen nicht funktioniert, dann ist vermutlich etwas in Ihrem Java Bean-Code ...

9
Daniel

Das liegt daran, dass die Registerkarte auch ein Namenscontainer ist ... Ihr Update sollte update="Search:insTable:display" Sein. Sie können Ihr Dialogfeld nur außerhalb des Formulars und immer noch innerhalb der Registerkarte platzieren. Dann wäre es: update="Search:display"

5
Lyrion

Versuchen Sie es mit change update="insTable:display" bis update="display". Ich glaube, Sie können der ID keine solche Formular-ID voranstellen.

0
Mr.J4mes

Ich weiß, dass dies bereits eine großartige Antwort von BalusC hat, aber hier ist ein kleiner Trick, mit dem ich den Container dazu bringe, mir die richtige clientId mitzuteilen.

  1. Entfernen Sie das Update auf Ihrer Komponente, die nicht funktioniert
  2. Fügen Sie eine temporäre Komponente mit einem falschen Update in die Komponente ein, die Sie aktualisieren möchten
  3. wenn Sie auf die Seite klicken, wird Ihnen der Servlet-Ausnahmefehler die richtige Client-ID mitteilen, auf die Sie verweisen müssen.
  4. Entfernen Sie die gefälschte Komponente und fügen Sie die richtige clientId in das ursprüngliche Update ein

Hier ist ein Codebeispiel, da meine Worte es möglicherweise nicht am besten beschreiben.

<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"

Entfernen Sie das fehlerhafte Update innerhalb dieser Komponente

 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">

Fügen Sie eine Komponente in die Komponente der ID ein, die Sie aktualisieren möchten. Verwenden Sie dazu ein Update, bei dem ein Fehler auftritt.

   <p:commandButton id="BogusButton" update="BogusUpdate"></p:commandButton>

                    <h:outputText value="Name:" />
                    <h:outputText value="#{instrumentBean.selectedInstrument.name}" />
                </h:panelGrid>
            </p:dialog>                            
        </h:form>
    </p:tab>
</p:tabView>

Schlagen Sie diese Seite und sehen Sie den Fehler an. Der Fehler lautet: javax.servlet.ServletException: Die Komponente für den Ausdruck "BogusUpdate", auf die auf den Registerkarten verwiesen wird, wurde nicht gefunden: insTable: BogusButton

Die richtige zu verwendende clientId wäre dann fett und die ID des Zielcontainers (in diesem Fall anzeigen).

tabs:insTable:display
0
jeff