Jak znaleźć identyfikator klienta komponentu do aktualizacji / renderowania Ajax? Nie można znaleźć komponentu z wyrażeniem „foo”, do którego odwołuje się „bar”

140

Poniższy kod został zainspirowany samouczkami PrimeFaces DataGrid + DataTable i umieszczony w <p:tab>pliku <p:tabView>znajdującym się w <p:layoutUnit>pliku <p:layout>. Oto wewnętrzna część kodu (zaczynając od p:tabkomponentu); zewnętrzna część jest trywialna.

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

Gdy kliknę <p:commandLink>, kod przestaje działać i wyświetla komunikat:

Nie można znaleźć komponentu z wyrażeniem „insTable: display”, do którego odwołuje się „tabs: insTable: select”.

Kiedy próbuję tego samego użycia <f:ajax>, kończy się niepowodzeniem, a inny komunikat zasadniczo mówi to samo:

<f:ajax> zawiera nieznany identyfikator „insTable: display” nie może go zlokalizować w kontekście komponentu „tabs: insTable: select”

Jak to się dzieje i jak mogę to rozwiązać?

perissf
źródło

Odpowiedzi:

313

Sprawdź w wyjściu HTML rzeczywisty identyfikator klienta

Musisz spojrzeć na wygenerowany wynik HTML, aby znaleźć właściwy identyfikator klienta. Otwórz stronę w przeglądarce, kliknij prawym przyciskiem myszy i wyświetl źródło . Znajdź reprezentację HTML interesującego komponentu JSF i wybierz go idjako identyfikator klienta. Możesz go używać w sposób bezwzględny lub względny, w zależności od bieżącego kontenera nazewnictwa. Patrz następny rozdział.

Uwaga: jeśli zdarzy się, że zawiera indeks iteracji, taki jak :0:, :1:itp. (Ponieważ znajduje się wewnątrz komponentu iterującego), musisz zdać sobie sprawę, że aktualizacja określonej rundy iteracji nie zawsze jest obsługiwana. Więcej szczegółów na ten temat znajduje się na dole odpowiedzi.

Zapamiętaj NamingContainerkomponenty i zawsze nadawaj im stały identyfikator

Jeśli komponent, do którego chcesz się odwoływać przez proces / wykonanie / aktualizację / renderowanie AJAX, znajduje się wewnątrz tego samego NamingContainerrodzica, po prostu odwołaj się do jego własnego ID.

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

Jeśli to nie wewnątrz taka sama NamingContainer, to trzeba odwoływać się do niej za pomocą bezwzględnego identyfikator klienta. Bezwzględny identyfikator klienta zaczyna się od NamingContainerznaku separatora, który jest domyślnie :.

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

NamingContainerSkładniki są na przykład <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(czyli wszystkie elementy kompozytowe), itp je łatwo rozpoznać patrząc na wyjściu generowany HTML, ich ID będzie dołączany do wygenerowanego ID klienta wszystkich elementów podrzędnych. Zwróć uwagę, że jeśli nie mają stałego identyfikatora, JSF użyje automatycznie wygenerowanego identyfikatora w j_idXXXformacie. Należy tego bezwzględnie unikać, dając im stały identyfikator. W OmniFacesNoAutoGeneratedIdViewHandler mogą być pomocne w tym czasie rozwoju.

Jeśli wiesz, jak znaleźć javadoc, o którym UIComponentmowa, możesz również sprawdzić, czy implementuje on NamingContainerinterfejs, czy nie. Na przykład HtmlForm( tag UIComponentza <h:form>) pokazuje, że implementuje NamingContainer, ale HtmlPanelGroup( tag UIComponentza <h:panelGroup>) go nie pokazuje, więc nie implementuje NamingContainer. Oto javadoc wszystkich standardowych komponentów, a tutaj javadoc PrimeFaces .

Rozwiązanie twojego problemu

Więc w twoim przypadku:

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

Wygenerowany wynik HTML <h:panelGrid id="display">wygląda następująco:

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

Musisz wziąć dokładnie to idjako identyfikator klienta, a następnie przedrostek :do użycia w update:

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

Odwołanie poza include / tagfile / composite

Jeśli to łącze polecenia znajduje się wewnątrz pliku include / tagfile, a cel znajduje się poza nim, a zatem niekoniecznie znasz identyfikator nadrzędnego kontenera nazewnictwa bieżącego kontenera nazewnictwa, możesz dynamicznie odwoływać się do niego w następujący UIComponent#getNamingContainer()sposób:

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

Lub, jeśli to łącze polecenia znajduje się wewnątrz komponentu złożonego, a cel znajduje się poza nim:

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

Lub, jeśli zarówno łącze polecenia, jak i cel znajdują się wewnątrz tego samego komponentu złożonego:

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

Zobacz także Pobierz identyfikator nadrzędnego kontenera nazewnictwa w szablonie dla atrybutu renderowania / aktualizacji

Jak to działa pod kołdrą

To wszystko jest określona jako „szukaj wyrażenia” w tym UIComponent#findComponent()javadoc :

Wyrażenie wyszukiwania składa się albo z identyfikatorem (która jest dopasowana dokładnie przeciwko własności identyfikator ID UIComponentlub serii takich identyfikatorów połączonych przez UINamingContainer#getSeparatorCharwartości znaków. Algorytm wyszukiwania powinien działa następująco, choć alternatywne alogrithms mogą być stosowane tak długo, jak efekt końcowy jest taki sam:

  • Zidentyfikuj, UIComponentktóry będzie podstawą wyszukiwania, zatrzymując się, gdy tylko zostanie spełniony jeden z następujących warunków:
    • Jeśli wyrażenie wyszukiwania zaczyna się od znaku separatora (zwanego „absolutnym” wyrażeniem wyszukiwania), podstawą będzie korzeń UIComponentdrzewa komponentów. Początkowy separator zostanie usunięty, a pozostała część wyszukiwanego wyrażenia będzie traktowana jako „względne” wyrażenie wyszukiwania, jak opisano poniżej.
    • W przeciwnym razie, jeśli UIComponentjest NamingContainerto podstawa, będzie służyć jako podstawa.
    • W przeciwnym razie wyszukaj elementy nadrzędne tego składnika. Jeśli NamingContainerzostanie napotkany, będzie bazą.
    • W przeciwnym razie (jeśli nie NamingContainerzostanie napotkany) root UIComponentbędzie podstawą.
  • Wyrażenie wyszukiwania (prawdopodobnie zmodyfikowane w poprzednim kroku) jest teraz „względnym” wyrażeniem wyszukiwania, które zostanie użyte do zlokalizowania komponentu (jeśli istnieje), którego identyfikator jest zgodny, w zakresie komponentu podstawowego. Mecz przebiega w następujący sposób:
    • Jeśli wyrażenie wyszukiwania jest prostym identyfikatorem, ta wartość jest porównywana z właściwością id, a następnie rekurencyjnie przez aspekty i elementy podrzędne podstawy UIComponent(z wyjątkiem tego, że jeśli NamingContainerzostanie znaleziony element podrzędny , jego własne aspekty i elementy potomne nie są przeszukiwane).
    • Jeśli wyrażenie wyszukiwania zawiera więcej niż jeden identyfikator oddzielony znakiem separatora, pierwszy identyfikator jest używany do zlokalizowania a NamingContainerwedług reguł z poprzedniego punktu. Następnie zostanie wywołana findComponent()metoda tego NamingContainer, przekazując pozostałą część wyszukiwanego wyrażenia.

Zauważ, że PrimeFaces również stosuje się do specyfikacji JSF, ale RichFaces używa „kilku dodatkowych wyjątków” .

„reRender” używa UIComponent.findComponent()algorytmu (z pewnymi dodatkowymi wyjątkami) do znalezienia komponentu w drzewie komponentów.

Te dodatkowe wyjątki nie są nigdzie szczegółowo opisane, ale wiadomo, że względne identyfikatory komponentów (tj. Te, które nie zaczynają się od :) są wyszukiwane nie tylko w kontekście najbliższego rodzica NamingContainer, ale także we wszystkich innych NamingContainerkomponentach w tym samym widoku (co jest względnie droga praca przy okazji).

Nigdy nie używaj prependId="false"

Jeśli to wszystko nadal nie działa, sprawdź, czy nie używasz <h:form prependId="false">. To się nie powiedzie podczas przetwarzania przesyłania i renderowania Ajax. Zobacz także to pokrewne pytanie: UIForm z prependId = "false" przerywa <f: ajax render> .

Odwoływanie się do określonej rundy iteracji składników iteracyjnych

Przez długi czas nie było możliwe odniesienie się do konkretnego iterowanego elementu w iteracyjnych komponentach, takich jak <ui:repeat>i <h:dataTable>podobne:

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

Jednak od czasu, gdy Mojarra 2.2.5 <f:ajax>zaczął go obsługiwać (po prostu przestał go sprawdzać; w ten sposób nigdy więcej nie spotkałbyś się z wymienionym w pytaniu wyjątkiem; kolejne ulepszenie jest planowane później).

Nie działa to jeszcze tylko w aktualnych wersjach MyFaces 2.2.7 i PrimeFaces 5.2. Wsparcie może pojawić się w przyszłych wersjach. W międzyczasie najlepiej jest zaktualizować sam komponent iterujący lub element nadrzędny, jeśli nie renderuje HTML, na przykład <ui:repeat>.

Korzystając z PrimeFaces, rozważ wyrażenia wyszukiwania lub selektory

PrimeFaces Search Expressions umożliwia odwoływanie się do komponentów poprzez wyrażenia wyszukiwania w drzewie komponentów JSF. JSF ma kilka wbudowanych:

  • @this: obecny składnik
  • @form: rodzic UIForm
  • @all: cały dokument
  • @none: nic

PrimeFaces rozszerzył to o nowe słowa kluczowe i obsługę wyrażeń złożonych:

  • @parent: składnik macierzysty
  • @namingcontainer: rodzic UINamingContainer
  • @widgetVar(name): składnik określony przez podany widgetVar

Można też mieszać te słowa kluczowe w wyrażeniach złożonych, takich jak @form:@parent, @this:@parent:@parentitp

PrimeFaces Selectors (PFS) as in @(.someclass)umożliwia odwoływanie się do komponentów za pomocą składni selektora jQuery CSS. Np. Odwoływanie się do komponentów mających wszystkie wspólne klasy stylów w wyjściu HTML. Jest to szczególnie przydatne w przypadku, gdy trzeba odwołać się do „wielu” komponentów. Wymaga to tylko, aby komponenty docelowe miały cały identyfikator klienta w wyjściu HTML (stały lub wygenerowany automatycznie, nie ma znaczenia). Zobacz także Jak działają selektory PrimeFaces, jak w update = "@ (. MyClass)"?

BalusC
źródło
@jack: Po prostu przeczytaj javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/ ... Od czasu JSF 2.0 stał się konfigurowalny zamiast stałej.
BalusC
Czy SEPARATOR_CHAR nie jest przestarzały? Czy możesz podać przykład, jak wywołać zagnieżdżony komponent, na przykład: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );Dołącz również kod xhtml.
jacktrades
1
Dziękuję, zwróć uwagę na niepowodzenie zwrotu AJAX w formularzu z prependId="false"zapisaniem mojego dnia.
Gaim
jakie jest dokładne znaczenie identyfikatora klienta, jak określono w wyjaśnieniu? Czy jest taki sam jak w JSF -> „Identyfikator po stronie klienta dla tego komponentu”. pozdrawiam + dziękuję za twoją pracę.
Steve Oh,
1
@ antonu17: Jak stwierdzono w odpowiedzi, jest obsługiwany tylko w f: Ajax Mojarra.
BalusC
9

po pierwsze: o ile wiem, umieszczanie okna dialogowego w widoku tabulatora jest złą praktyką ... lepiej to wyjmij ...

a teraz na twoje pytanie:

przepraszam, zajęło mi trochę czasu, aby uzyskać to, co dokładnie chciałeś zaimplementować,

zrobiłem w mojej aplikacji internetowej i to działa

jak powiedziałem wcześniej umieść p: dialog poza `p: tabView,

zostaw okno dialogowe p: tak, jak sugerowałeś na początku:

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

a p: commandlink powinien wyglądać tak (wszystko, co zrobiłem, to zmienić atrybut aktualizacji)

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

to samo działa w mojej aplikacji internetowej, a jeśli to nie zadziała, to chyba coś jest nie tak w kodzie java bean ...

Daniel
źródło
Polecam wypróbowanie innych zmian, które napisałem w mojej odpowiedzi (z wiązaniem i face-config i innymi ...), które mają rozwiązać twój "INFO: Cannot find compone ..."
Daniel
Ponownie próbowałem wdrożyć twoją drugą sugestię, ale nadal nie działa. Okno dialogowe otwiera się, ale nie zawiera danych wybranego elementu. W dzienniku jest wyświetlany komunikat „Nie można znaleźć komponentu o identyfikatorze„ j_idt31 ”w widoku” i nie mogę więcej debugować.
perissf
5

Dzieje się tak, ponieważ karta jest również kontenerem nazewnictwa ... Twoja aktualizacja powinna być update="Search:insTable:display"To, co możesz zrobić, to po prostu umieścić swoje okno dialogowe poza formularzem i nadal wewnątrz karty, wtedy wyglądałoby to tak:update="Search:display"

Lyrion
źródło
0

Spróbuj zmienić update="insTable:display"na update="display". Uważam, że nie można poprzedzić identyfikatora takim identyfikatorem formularza.

Panie J4mes
źródło
2
Bardzo stara, ale myląca odpowiedź. Zapoznaj się z powyższym postem BalusC, w którym wyraźnie widać przedrostek identyfikatora komponentu z identyfikatorem załączonego formularza: <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

Wiem, że to już ma świetną odpowiedź od BalusC, ale tutaj jest mała sztuczka, której używam, aby kontener podał mi poprawny clientId .

  1. Usuń aktualizację składnika, który nie działa
  2. Umieść tymczasowy komponent z fałszywą aktualizacją w komponencie, który próbujesz zaktualizować
  3. trafi na stronę, błąd wyjątku serwletu wskaże poprawny identyfikator klienta, do którego należy się odwołać.
  4. Usuń fałszywy komponent i umieść poprawny clientId w oryginalnej aktualizacji

Oto przykład kodu, ponieważ moje słowa mogą go nie opisać najlepiej.

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

Usuń nieudaną aktualizację w tym komponencie

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

Dodaj składnik w składniku identyfikatora, który próbujesz zaktualizować, za pomocą aktualizacji, która się nie powiedzie

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

Wejdź na tę stronę i zobacz błąd. Wystąpił błąd: javax.servlet.ServletException: Nie można znaleźć komponentu wyrażenia „BogusUpdate”, do którego odwołuje się zakładka: insTable: BogusButton

Zatem prawidłowy identyfikator clientId do użycia byłby pogrubiony plus identyfikator kontenera docelowego (w tym przypadku wyświetlany)

tabs:insTable:display
jeff
źródło