Wie finde ich die Client-ID der Komponente für das Ajax-Update / Rendering heraus? Komponente mit Ausdruck "foo", auf den in "bar" verwiesen wird, kann nicht gefunden werden

140

Der folgende Code ist von PrimeFaces DataGrid + DataTable-Tutorials inspiriert und wird in einen <p:tab>von a mit <p:tabView>Wohnsitz in einem <p:layoutUnit>von a eingefügt <p:layout>. Hier ist der innere Teil des Codes (ausgehend von der p:tabKomponente); 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 klicke <p:commandLink>, funktioniert der Code nicht mehr und gibt die folgende Meldung aus:

Komponente mit dem Ausdruck "insTable: display", auf den von "tabs: insTable: select" verwiesen wird, kann nicht gefunden werden.

Wenn ich dasselbe mit versuche <f:ajax>, 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 wird das verursacht und wie kann ich es lösen?

perissf
quelle

Antworten:

313

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 herauszufinden. Öffnen Sie die Seite im Browser, klicken Sie mit der rechten Maustaste und zeigen Sie die Quelle an . Suchen Sie die HTML-Darstellung der interessierenden JSF-Komponente und nehmen Sie sie idals Client-ID. Sie können es je nach aktuellem Namenscontainer absolut oder relativ verwenden. Siehe folgendes Kapitel.

Hinweis: Wenn es Iteration Index enthalten geschieht wie :0:, :1:usw. (weil es nicht in einem Iterieren Komponente ist), dann müssen Sie feststellen , dass eine bestimmte Iteration Runde Aktualisierung wird nicht immer unterstützt. Weitere Informationen hierzu finden Sie unten in der Antwort.

Merken Sie sich NamingContainerKomponenten und geben Sie ihnen immer eine feste ID

Wenn sich eine Komponente, auf die Sie mit ajax process / execute / update / render verweisen möchten, innerhalb desselben NamingContainerübergeordneten Elements befindet , verweisen Sie einfach auf ihre eigene ID.

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

Wenn es nicht dasselbe ist NamingContainer, müssen Sie es mit einer absoluten Client-ID referenzieren. Eine absolute Client-ID beginnt mit dem NamingContainerTrennzeichen, das standardmäßig verwendet wird :.

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

NamingContainerKomponenten sind zum Beispiel <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(also alle Verbundbauteile), etc. Sie sie , indem man die generierten HTML - Ausgabe leicht erkennen, ihre ID zu der generierten Client - ID aller untergeordneten Komponenten vorangestellt werden. Beachten Sie, dass JSF eine automatisch generierte ID im j_idXXXFormat verwendet , wenn sie keine feste ID haben . Sie sollten dies unbedingt vermeiden, indem Sie ihnen einen festen Ausweis geben. Die OmniFacesNoAutoGeneratedIdViewHandler können dabei während der Entwicklung hilfreich sein.

Wenn Sie wissen, dass Sie das Javadoc des UIComponentbetreffenden Objekts finden , können Sie dort auch einfach überprüfen, ob es die NamingContainerSchnittstelle implementiert oder nicht. Zum Beispiel zeigt das HtmlForm(das UIComponenthintere <h:form>Tag), dass es implementiert ist NamingContainer, aber das HtmlPanelGroup(das UIComponenthintere <h:panelGroup>Tag) zeigt es nicht an, so dass es nicht implementiert wird NamingContainer. 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 das idals Client-ID verwenden und dann ein Präfix :für die Verwendung in update:

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

Verweise außerhalb von include / tagfile / compos

Befindet sich diese Befehlsverknüpfung innerhalb einer Include- / Tag-Datei und das Ziel außerhalb davon, und Sie kennen daher nicht unbedingt die ID des übergeordneten Namenscontainers des aktuellen Namenscontainers, können Sie sie dynamisch UIComponent#getNamingContainer()wie folgt referenzieren :

<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 dieser 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 als angegeben „Such Ausdruck“ in der UIComponent#findComponent()javadoc :

Ein Suchausdruck besteht entweder aus einem Bezeichner (der genau mit der ID-Eigenschaft von a übereinstimmt UIComponent, oder einer Reihe solcher Bezeichner, die durch den UINamingContainer#getSeparatorCharZeichenwert verknüpft sind . Der Suchalgorithmus sollte wie folgt funktionieren, obwohl alternative Alogrithmen verwendet werden können, solange der Endergebnis ist das gleiche:

  • Identifizieren Sie die UIComponentBasis für die Suche, indem Sie anhalten, sobald eine der folgenden Bedingungen erfüllt ist:
    • Wenn der Suchausdruck mit dem Trennzeichen (als "absoluter" Suchausdruck bezeichnet) beginnt, ist die Basis die Wurzel UIComponentdes Komponentenbaums. Das führende Trennzeichen wird entfernt und der Rest des Suchausdrucks wird wie unten beschrieben als "relativer" Suchausdruck behandelt.
    • Andernfalls, wenn dies UIComponentist eine NamingContainerwird als Grundlage dienen.
    • Suchen Sie andernfalls die übergeordneten Elemente dieser Komponente. Wenn a NamingContainerangetroffen wird, ist es die Basis.
    • Andernfalls (wenn keine gefunden NamingContainerwird) ist die Wurzel UIComponentdie 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 durch die Facetten und untergeordneten Elemente der Basis UIComponent(außer wenn ein Nachkomme NamingContainergefunden wird, werden seine eigenen Facetten und untergeordneten Elemente nicht durchsucht).
    • Wenn der Suchausdruck mehr als eine durch das Trennzeichen getrennte Kennung enthält, wird die erste Kennung verwendet, um eine NamingContainernach den Regeln im vorherigen Aufzählungspunkt zu lokalisieren . Dann wird die findComponent()Methode hierfür NamingContaineraufgerufen, wobei der Rest des Suchausdrucks übergeben wird.

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

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

Diese zusätzlichen Ausnahmen werden nirgends 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 Elements gesucht werden NamingContainer, sondern auch in allen anderen NamingContainerKomponenten in derselben Ansicht (was relativ ist) teurer Job übrigens).

Benutze niemals prependId="false"

Wenn dies alles immer noch nicht funktioniert, überprüfen Sie, ob Sie es nicht verwenden <h:form prependId="false">. Dies schlägt während der Verarbeitung des Ajax-Submit und -Renderings fehl. Siehe auch diese verwandte Frage: UIForm mit prependId = "false" bricht <f: ajax render> .

Referenzieren einer bestimmten Iterationsrunde iterierender Komponenten

Es war für lange Zeit nicht möglich , eine bestimmte iteriert Artikel in Iterieren Komponenten wie zu verweisen <ui:repeat>und in <h:dataTable>etwa so:

<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 hat es jedoch <f:ajax>begonnen, es zu unterstützen (es hat einfach aufgehört, es zu validieren; daher würden Sie nie mehr mit der in der Frage genannten Ausnahme konfrontiert sein; eine weitere Verbesserung ist später geplant).

Dies funktioniert nur in den aktuellen Versionen von MyFaces 2.2.7 und PrimeFaces 5.2 noch nicht. Die Unterstützung wird möglicherweise in zukünftigen Versionen verfügbar sein. In der Zwischenzeit ist es am besten, die iterierende Komponente selbst oder ein übergeordnetes Element zu aktualisieren, falls es kein HTML rendert <ui:repeat>.

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

Mit PrimeFaces-Suchausdrücken können Sie Komponenten über JSF-Komponentenbaum- Suchausdrücke referenzieren. JSF hat mehrere eingebaute:

  • @this: aktuelle Komponente
  • @form: Elternteil UIForm
  • @all: gesamtes Dokument
  • @none: nichts

PrimeFaces hat dies durch neue Schlüsselwörter und Unterstützung für zusammengesetzte Ausdrücke erweitert:

  • @parent: übergeordnete Komponente
  • @namingcontainer: Elternteil UINamingContainer
  • @widgetVar(name): Komponente wie durch gegeben identifiziert widgetVar

Sie können auch diese Keywords in zusammengesetzten Begriffen wie mischen @form:@parent, @this:@parent:@parentusw.

Mit PrimeFaces Selectors (PFS) wie in @(.someclass)können Sie Komponenten über die jQuery CSS-Selektorsyntax referenzieren. Verweisen auf Komponenten mit einer gemeinsamen Stilklasse 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)"?

BalusC
quelle
@jack: Lesen Sie einfach javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/… Seit JSF 2.0 ist es anstelle einer Konstante konfigurierbar geworden.
BalusC
Ist SEPARATOR_CHAR nicht veraltet? Können Sie ein Beispiel zum Aufrufen einer verschachtelten Komponente geben, zum Beispiel: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );Geben Sie auch den xhtml-Code an.
Jacktrades
1
Vielen Dank, Hinweis auf fehlgeschlagene Ajax-Wiedergabe innerhalb des Formulars mit prependId="false"meinem Tag gerettet.
Gaim
Was ist die genaue Bedeutung der Kunden-ID, wie in Ihrer Erklärung beschrieben? Ist es dasselbe wie in JSF -> "Die clientseitige Kennung für diese Komponente". Grüße + Danke für deine Arbeit.
Steve Oh
1
@ antonu17: Wie in der Antwort angegeben, wird es nur in Mojarras f: ajax unterstützt.
BalusC
9

Zuallererst: Soweit ich weiß, ist es eine schlechte Praxis, einen Dialog in einer Tab-Ansicht zu platzieren. Nehmen Sie ihn besser heraus.

und nun zu deiner frage:

Entschuldigung, ich habe einige Zeit gebraucht, um genau das zu bekommen, was Sie implementieren wollten.

habe gerade an meiner Web-App selbst gearbeitet, und es funktioniert

Wie ich bereits sagte, platzieren Sie den p: -Dialog außerhalb des `p: tabView.

Verlassen Sie den Dialog p: 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: Befehlslink 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 stimmt wohl etwas in Ihrem Java-Bean-Code nicht ...

Daniel
quelle
Ich empfehle Ihnen, die anderen Änderungen zu versuchen, die ich in meiner Antwort geschrieben habe (mit der Bindung und der Gesichtskonfiguration und anderen ...), um Ihr "INFO: Compone kann nicht gefunden werden ..." zu lösen
Daniel
Ich habe erneut versucht, Ihren zweiten Vorschlag umzusetzen, aber er funktioniert immer noch nicht. Der Dialog wird geöffnet, enthält jedoch keine Daten des ausgewählten Elements. Das Protokoll zeigt "Komponente mit Bezeichner" j_idt31 "in Ansicht kann nicht gefunden werden" an, und ich kann nicht mehr debuggen.
Perissf
5

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

Lyrion
quelle
0

Versuchen Sie Änderung update="insTable:display"an update="display". Ich glaube, Sie können der ID keine solche Formular-ID voranstellen.

Mr.J4mes
quelle
2
Sehr alte, aber irreführende Antwort. Lesen Sie den obigen Beitrag von BalusC, in dem das Präfix der ID einer Komponente mit der ID des beiliegenden Formulars deutlich dargestellt wird: <h: form id = "form"> <p: commandLink update = ": otherform: result"> <! - OK! -> </ h: form> <h: form id = "otherform"> <h: panelGroup id = "result" /> </ h: form>
J Slick
0

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 wollten
  3. Klicken Sie auf die Seite. Der Servlet-Ausnahmefehler zeigt Ihnen die richtige Client-ID an, 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 innerhalb der Komponente der ID hinzu, die Sie mit einem fehlgeschlagenen Update aktualisieren möchten

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

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

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

tabs:insTable:display
Jeff
quelle