Jak znaleźć identyfikator klienta komponentu dla ajax update / render? Nie można znaleźć komponentu z wyrażeniem " foo " odwołującym się z "bar"

Poniższy kod jest zainspirowany tutorialami PrimeFaces DataGrid + DataTable i umieszczony w <p:tab> z <p:tabView> z <p:layoutUnit> z <p:layout>. Oto wewnętrzna część kodu (zaczynając od p:tab komponentu); 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>

Kiedy klikam <p:commandLink>, kod przestaje działać i daje komunikat:

Nie można znaleźć komponentu z wyrażeniem " insTable: display "odwołującym się do"tabs:insTable:select".

Kiedy próbuję tego samego używając <f:ajax>, zawodzi z inną wiadomością w zasadzie mówiącą to samo:

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

Jak to jest spowodowane i jak Mogę to rozwiązać?

Author: BalusC, 0000-00-00

4 answers

Spójrz na wyjście HTML dla Rzeczywistego ID klienta

Musisz zajrzeć do wygenerowanego wyjścia HTML, aby znaleźć odpowiedni identyfikator klienta. Otwórz stronę w przeglądarce, wykonaj rightclick i Zobacz źródło. Zlokalizuj reprezentację HTML interesującego komponentu JSF i weź jego id jako identyfikator klienta. Można go używać w sposób bezwzględny lub względny, w zależności od bieżącego kontenera nazw. Patrz następujący rozdział.

Uwaga: jeśli będzie zawierać indeks iteracji jak :0:, :1:, etc (ponieważ znajduje się wewnątrz komponentu iteracyjnego), wtedy musisz zdać sobie sprawę, że aktualizacja określonej rundy iteracji nie zawsze jest obsługiwana. Więcej szczegółów na ten temat znajdziesz na dole odpowiedzi.

Zapamiętaj NamingContainer komponenty i zawsze nadaj im stały identyfikator

Jeśli komponent, do którego chcesz się odwoływać przez proces ajax/execute/update/render, znajduje się wewnątrz tego samego NamingContainer rodzica, a następnie wystarczy podać swój własny identyfikator.

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

If it ' s not inside the to samo NamingContainer, następnie musisz odwołać się do niego za pomocą absolutnego ID klienta. Absolutny identyfikator klienta zaczyna się od znaku separatora NamingContainer, 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>

NamingContainer komponenty są na przykład <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation> (tak więc wszystkie komponenty kompozytowe), itp. Rozpoznajesz je łatwo patrząc na wygenerowany wynik HTML, ich ID zostanie dołączone do wygenerowanego identyfikatora klienta wszystkich komponentów potomnych. Zauważ, że gdy jeśli nie masz stałego ID, JSF użyje automatycznie wygenerowanego ID w formacie j_idXXX. Należy bezwzględnie tego unikać, podając im stały identyfikator. OmniFaces NoAutoGeneratedIdViewHandler może być pomocne w tym podczas rozwoju.

Jeśli wiesz, aby znaleźć javadoc UIComponent w pytaniu, możesz również sprawdzić, czy implementuje on NamingContainer interfejs czy nie. Na przykładHtmlForm ( UIComponent za <h:form> znacznikiem) pokazuje, że implementuje NamingContainer, ale HtmlPanelGroup (UIComponent za znacznikiem <h:panelGroup>) nie pokazuje go, więc nie implementuje NamingContainer. tutaj jest javadoc wszystkich standardowych komponentów i tutaj jest 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">

Wygenerowane wyjście HTML <h:panelGrid id="display"> wygląda tak:

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

Musisz wziąć dokładnie to id jako identyfikator klienta, a następnie prefiks z : do użycia w update:

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

Odwoływanie się do zewnątrz include / tagfile / composite

Jeśli ten link polecenia znajduje się wewnątrz pliku include/tag, a obiekt docelowy znajduje się poza nim, a zatem nie musisz znać ID rodzica kontenera nazw bieżącego kontenera nazw, możesz dynamicznie odwoływać się do niego za pomocą UIComponent#getNamingContainer() w następujący sposób:

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

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

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

Lub, jeśli zarówno Komenda link i cel znajduje się wewnątrz tego samego komponentu kompozytowego:

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

Zobacz także pobranie identyfikatora nadrzędnego kontenera nazw w szablonie dla atrybutu render / update

Jak to działa pod pokrywami

To wszystko jest określone jako "wyrażenie wyszukiwania" w UIComponent#findComponent() javadoc :

A wyrażenie wyszukiwania składa się z identyfikatora (który jest dopasowany dokładnie do właściwości id UIComponent, lub serii takich identyfikatory połączone przez wartość znaku UINamingContainer#getSeparatorChar. Algorytm wyszukiwania powinien działać w następujący sposób, chociaż alternatywne alogrytmy mogą być używane tak długo, jak wynik końcowy jest taki sam: {]}

  • Zidentyfikuj UIComponent, które będą bazą do wyszukiwania, zatrzymując się, gdy tylko spełniony zostanie jeden z następujących warunków:
    • jeśli szukane wyrażenie zaczyna się od znaku separatora (zwanego "absolutnym" szukanym wyrażeniem), bazą będzie root UIComponent Z drzewo komponentów. Główny znak separatora zostanie usunięty, a pozostała część szukanego wyrażenia będzie traktowana jako "względne" szukane wyrażenie, jak opisano poniżej.
    • W przeciwnym razie, jeśli to UIComponent jest {[15] } będzie służyć jako podstawa.
    • W Przeciwnym Razie wyszukaj rodziców tego komponentu. Jeśli zostanie napotkana NamingContainer, będzie to baza.
    • W przeciwnym razie (jeśli nie występuje NamingContainer) root UIComponent będzie bazą.
  • wyrażenie wyszukiwania (prawdopodobnie zmodyfikowane w poprzednim kroku) jest teraz "względnym" wyrażeniem wyszukiwania, które będzie używane do zlokalizowania komponentu (jeśli istnieje), który ma pasujący identyfikator, w zakresie komponentu podstawowego. Mecz odbywa się w następujący sposób:
    • jeśli szukane wyrażenie jest prostym identyfikatorem, wartość ta jest porównywana z właściwością id, a następnie rekurencyjnie przez Fasety i dzieci bazy UIComponent (z wyjątkiem tego, że jeśli zostanie znaleziony potomek NamingContainer, jego własne oblicza i dzieci nie są wyszukiwane).
    • jeśli szukane wyrażenie zawiera więcej niż jeden identyfikator oddzielony znakiem separatora, pierwszy identyfikator jest używany do zlokalizowania NamingContainer zgodnie z regułami w poprzednim podpunkcie. Następnie zostanie wywołana metoda findComponent() tego NamingContainer, przekazująca pozostałą część szukanego wyrażenia.

Zauważ, że PrimeFaces również przestrzega specyfikacji JSF, ale RichFaces używa "niektórych dodatkowych wyjątki " .

"rerender" używa algorytmu UIComponent.findComponent() (z pewnymi dodatkowymi wyjątkami), aby znaleźć komponent 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 rozpoczynają się od :) są wyszukiwane nie tylko w kontekście najbliższego rodzica NamingContainer, ale także we wszystkich innych komponentach NamingContainer w tym samym widoku (co jest stosunkowo kosztownym zadaniem przez serwer). sposób).

Nigdy nie używaj prependId="false"

Jeśli to wszystko nadal nie działa, sprawdź, czy nie używasz <h:form prependId="false">. To nie powiedzie się podczas przetwarzania Ajax submit i renderowania. Zobacz także podobne pytanie: UIForm with prependId="false" breaks .

Odniesienie do specyficznej rundy iteracji komponentów iteracyjnych

Przez długi czas nie było możliwe odwoływanie się do określonego iteracyjnego elementu w iteracyjnych komponentach, takich jak <ui:repeat> i <h:dataTable>, takich jak więc:

<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że, odkąd Mojarra 2.2.5 <f:ajax> zaczęło ją wspierać (po prostu przestało ją zatwierdzać; w ten sposób nigdy nie spotkałbyś się już z wspomnianym wyjątkiem; kolejna poprawka ulepszenia jest planowana na to później).

To nie działa jeszcze w obecnych wersjach MyFaces 2.2.7 i PrimeFaces 5.2. Wsparcie może pojawić się w przyszłych wersjach. W międzyczasie najlepiej będzie zaktualizować komponent iteracyjny lub rodzica, jeśli nie Renderuj HTML, jak <ui:repeat>.

Używając PrimeFaces, rozważ szukanie wyrażeń lub selektorów

PrimeFaces Search Expressions pozwala na odwoływanie się do komponentów poprzez JSF component tree search expressions. JSF ma kilka wbudowanych:

  • @this: Składnik bieżący
  • @form: rodzic UIForm
  • @all: cały dokument
  • @none: nic

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

  • @parent: element nadrzędny
  • @namingcontainer: rodzic UINamingContainer
  • @widgetVar(name): Składnik określony przez dany widgetVar

Możesz także mieszać te słowa kluczowe w wyrażeniach złożonych, takich jak @form:@parent, @this:@parent:@parent, itd.

Selektory PrimeFaces (PFS) jak w @(.someclass) pozwalają na odwoływanie się do komponentów poprzez składnię selektora CSS jQuery. Np. odwoływanie się do komponentów mających wszystkie wspólne klasy stylu w HTML wyjście. Jest to szczególnie pomocne w przypadku, gdy trzeba odwołać się do "wielu" komponentów. To tylko wymaga, aby komponenty docelowe miały wszystkie ID klienta w wyjściu HTML (stałe lub autogenerowane, nie ma znaczenia). Zobacz także jak zrobić selektory PrimeFaces jak w update="@(.myClass) " praca?

 275
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:44

Po pierwsze: z tego, co wiem, umieszczanie okna dialogowego wewnątrz widoku kart jest złą praktyką... lepiej to wyjmij...

A teraz do pytania: Przepraszam, Zajęło mi trochę czasu, żeby dostać to, co chciałeś zaimplementować.]}

Zrobiłem w mojej aplikacji internetowej właśnie teraz, i to działa

Tak jak przed umieszczeniem okna p:Po stronie ' p: tabView,

Pozostaw okno dialogowe p: tak jak początkowo sugerowałeś:

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

I P: commandlink powinien wyglądać tak (wszystkie I did is to change the update atrybut)

<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 nie działa dla ciebie, to chyba coś jest nie tak w Twoim kodzie java bean...

 9
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-28 08:11:33

Dzieje się tak dlatego, że tab jest również kontenerem nazw... twoja aktualizacja powinna być update="Search:insTable:display" to, co możesz zrobić, to po prostu umieścić okno dialogowe poza formularzem i nadal wewnątrz karty, a następnie będzie to: update="Search:display"

 5
Author: Lyrion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-26 10:59:36

Spróbuj zmienić update="insTable:display" na update="display". Wierzę, że nie można przedrostka id z ID formularza w ten sposób.

 0
Author: Mr.J4mes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-26 09:25:56