Come scoprire l'ID client del componente per l'aggiornamento / il rendering Ajax? Impossibile trovare il componente con l'espressione "pippo" a cui fa riferimento la "barra"


140

Il codice seguente è ispirato alle esercitazioni PrimeFaces DataGrid + DataTable e inserite in a <p:tab>di un <p:tabView>residente in a <p:layoutUnit>di a <p:layout>. Ecco la parte interna del codice (a partire dal p:tabcomponente); la parte esterna è banale.

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

Quando faccio clic su <p:commandLink>, il codice smette di funzionare e visualizza il messaggio:

Impossibile trovare il componente con l'espressione "insTable: visualizza" a cui fa riferimento "tab: insTable: seleziona".

Quando provo lo stesso utilizzo <f:ajax>, non riesce con un messaggio diverso che dice sostanzialmente lo stesso:

<f:ajax> contiene un ID sconosciuto "insTable: display" impossibile localizzarlo nel contesto del componente "tab: insTable: seleziona"

Come viene causato e come posso risolverlo?

Risposte:


313

Cerca nell'output HTML l'ID client effettivo

Devi cercare nell'output HTML generato per scoprire l'ID client corretto. Apri la pagina nel browser, fai clic con il pulsante destro del mouse e Visualizza sorgente . Individua la rappresentazione HTML del componente JSF di interesse e prendila idcome ID client. Puoi usarlo in modo assoluto o relativo a seconda dell'attuale contenitore dei nomi. Vedi il capitolo seguente.

Nota: se capita di contenere un indice di iterazione come :0:, :1:ecc. (Perché si trova all'interno di un componente iterante), è necessario rendersi conto che l'aggiornamento di un ciclo di iterazione specifico non è sempre supportato. Vedi fondo della risposta per maggiori dettagli al riguardo.

Memorizza i NamingContainercomponenti e assegna loro sempre un ID fisso

Se un componente a cui desideri fare riferimento tramite ajax process / execute / update / render è all'interno dello stesso NamingContainergenitore, fai semplicemente riferimento al suo ID.

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

Se non si trova all'interno dello stesso NamingContainer, è necessario fare riferimento a esso utilizzando un ID client assoluto. Un ID client assoluto inizia con il NamingContainercarattere separatore, che è per impostazione predefinita :.

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

NamingContainercomponenti sono per esempio <h:form>,<h:dataTable> , <p:tabView>, <cc:implementation>(in tal modo, tutti i componenti in composito), ecc Li riconoscono facilmente osservando l'output HTML generato, il loro ID verrà anteposto al ID client generato di tutti i componenti figlio. Quando non hanno un ID fisso, JSF utilizzerà un ID generato automaticamente nel j_idXXXformato. Dovresti assolutamente evitarlo dando loro un ID fisso. I OmniFacesNoAutoGeneratedIdViewHandler possono essere utili in questa fase di sviluppo.

Se sai di trovare il javadoc UIComponentin questione, puoi anche controllare lì se implementa l' NamingContainerinterfaccia o meno. Ad esempio, il HtmlForm(il tag UIComponentdietro <h:form>) mostra che implementa NamingContainer, ma il HtmlPanelGroup(il UIComponentdietro<h:panelGroup> tag ) non lo mostra, quindi non lo implementa NamingContainer. Ecco il javadoc di tutti i componenti standard ed ecco il javadoc di PrimeFaces .

Risolvere il tuo problema

Quindi nel tuo caso di:

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

L'output HTML generato di <h:panelGrid id="display"> è simile al seguente:

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

Devi prenderlo esattamente idcome ID client e quindi aggiungere il prefisso :per l'utilizzo inupdate :

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

I riferimenti all'esterno includono / tagfile / composite

Se questo collegamento di comando si trova all'interno di un file include / tag e la destinazione si trova all'esterno di esso, pertanto non si conosce necessariamente l'ID del contenitore contenitore di denominazione del contenitore di denominazione corrente, quindi è possibile fare riferimento dinamicamente tramite UIComponent#getNamingContainer()così:

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

Oppure, se questo collegamento di comando si trova all'interno di un componente composito e la destinazione si trova all'esterno di esso:

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

Oppure, se sia il comando link che la destinazione sono all'interno dello stesso componente composito:

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

Guarda anche Ottieni ID del contenitore di denominazione principale nel modello per l'attributo in rendering / update

Come funziona sotto le coperte

Tutto questo è specificato come "ricerca espressione" nella UIComponent#findComponent()javadoc :

Un'espressione di ricerca è costituito da un identificatore (che corrisponde esattamente contro la proprietà id di una UIComponent, o una serie di tali identificatori collegate dal UINamingContainer#getSeparatorCharvalore di carattere. L'algoritmo ricerca deve opera come segue, se alogrithms alternativi possono essere utilizzati purché la il risultato finale è lo stesso:

  • Identifica quella UIComponentche sarà la base per la ricerca, fermandoti non appena viene soddisfatta una delle seguenti condizioni:
    • Se l'espressione di ricerca inizia con il carattere separatore (chiamato espressione di ricerca "assoluta"), la base sarà la radice UIComponentdell'albero dei componenti. Il carattere separatore principale verrà rimosso e il resto dell'espressione di ricerca verrà trattato come un'espressione di ricerca "relativa" come descritto di seguito.
    • Altrimenti, se questo UIComponentè un NamingContainer, servirà come base.
    • Altrimenti, cerca i genitori di questo componente. Se NamingContainersi incontra a, sarà la base.
    • Altrimenti (se non NamingContainersi incontra) la radice UIComponentsarà la base.
  • L'espressione di ricerca (eventualmente modificata nel passaggio precedente) è ora un'espressione di ricerca "relativa" che verrà utilizzata per individuare il componente (se presente) che ha un ID corrispondente, nell'ambito del componente di base. La partita si svolge come segue:
    • Se l'espressione di ricerca è un identificatore semplice, questo valore viene confrontato con la proprietà id, quindi ricorsivamente attraverso le sfaccettature e i figli della base UIComponent(tranne che se NamingContainerviene trovato un discendente , le sue sfaccettature e i suoi figli non vengono cercati).
    • Se l'espressione di ricerca include più di un identificatore separato dal carattere separatore, il primo identificatore viene utilizzato per individuare a NamingContainersecondo le regole nel punto elenco precedente. Quindi, verrà chiamato il findComponent()metodo di questo NamingContainer, passando il resto dell'espressione di ricerca.

Si noti che anche PrimeFaces aderisce alle specifiche JSF, ma RichFaces utilizza "alcune eccezioni aggiuntive" .

"reRender" utilizza l' UIComponent.findComponent()algoritmo (con alcune eccezioni aggiuntive) per trovare il componente nella struttura dei componenti.

Queste ulteriori eccezioni non sono descritte in alcun dettaglio, ma è noto che gli ID dei componenti relativi (ovvero quelli che non iniziano con :) non vengono cercati solo nel contesto del genitore più vicino NamingContainer, ma anche in tutti gli altri NamingContainercomponenti nella stessa vista (che è relativamente lavoro costoso a proposito).

Non usare mai prependId="false"

Se tutto continua a non funzionare, verifica se non lo stai utilizzando <h:form prependId="false">. Ciò fallirà durante l'elaborazione dell'invio e del rendering di Ajax. Vedi anche questa domanda correlata: UIForm con prependId = "false" si rompe <f: ajax render> .

Facendo riferimento al ciclo di iterazione specifico dei componenti iteranti

Per molto tempo non è stato possibile fare riferimento a un elemento iterato specifico in componenti iteranti simili <ui:repeat>e <h:dataTable>simili:

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

Tuttavia, dal momento che Mojarra 2.2.5 ha <f:ajax>iniziato a supportarlo (ha semplicemente smesso di convalidarlo; quindi non dovresti mai più affrontare l'eccezione nella domanda menzionata; un'altra correzione del miglioramento è prevista in seguito).

Questo non funziona ancora nelle attuali versioni di MyFaces 2.2.7 e PrimeFaces 5.2. Il supporto potrebbe venire nelle versioni future. Nel frattempo, la soluzione migliore è aggiornare il componente iterante stesso o un genitore nel caso in cui non esegua il rendering HTML, come <ui:repeat>.

Quando si utilizzano PrimeFaces, considerare le espressioni di ricerca o i selettori

PrimeFaces Search Expressions consente di fare riferimento ai componenti tramite le espressioni di ricerca dell'albero dei componenti JSF. JSF ha diversi builtin:

  • @this: componente corrente
  • @form: genitore UIForm
  • @all: intero documento
  • @none: Niente

PrimeFaces ha migliorato questo aspetto con nuove parole chiave e supporto per espressioni composite:

  • @parent: componente principale
  • @namingcontainer: genitore UINamingContainer
  • @widgetVar(name): componente identificato da dato widgetVar

È inoltre possibile combinare tali parole chiave nelle espressioni compositi come @form:@parent, @this:@parent:@parent, etc.

PrimeFaces Selectors (PFS) come in @(.someclass)consente di fare riferimento ai componenti tramite la sintassi del selettore CSS jQuery. Ad esempio, i componenti di riferimento che hanno una classe di stile comune nell'output HTML. Ciò è particolarmente utile nel caso in cui sia necessario fare riferimento a "molti" componenti. Ciò presuppone solo che i componenti di destinazione abbiano tutti un ID client nell'output HTML (fisso o generato automaticamente, non importa). Vedi anche Come funzionano i selettori PrimeFaces come in update = "@ (. MyClass)"?


@jack: basta leggere javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/… Da JSF 2.0 è diventato configurabile anziché una costante.
BalusC,

SEPARATOR_CHAR non è deprecato? Puoi fare un esempio su come chiamare un componente nidificato, ad esempio: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );includi anche il codice xhtml.
jacktrades

1
Grazie, nota del fallimento di Ajax Rerender all'interno del modulo con la prependId="false"mia giornata salvata.
Gaim,

qual è il significato esatto dell'ID cliente come indicato nella tua spiegazione? È lo stesso di JSF -> "L'identificatore lato client per questo componente". saluti + grazie per il tuo lavoro.
Steve Oh,

1
@ antonu17: come indicato nella risposta, è supportato solo in f: ajax di Mojarra.
BalusC

9

prima di tutto: per quanto ne so, inserire una finestra di dialogo all'interno di una scheda è una cattiva pratica ... faresti meglio a toglierla ...

e ora alla tua domanda:

scusa, mi ci è voluto un po 'di tempo per ottenere esattamente quello che volevi implementare,

ho fatto la mia app Web solo ora, e funziona

come ho detto prima di posizionare la finestra di dialogo p: fuori dal lato `p: tabView,

lascia la finestra di dialogo p: come inizialmente suggerito:

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

e il p: commandlink dovrebbe apparire così (tutto quello che ho fatto è stato modificare l'attributo di aggiornamento)

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

lo stesso funziona nella mia app Web, e se non funziona per te, allora suppongo che ci sia qualcosa di sbagliato nel tuo codice java bean ...


Ti consiglio di provare le altre modifiche che ho scritto nella mia risposta (con la rilegatura e la configurazione dei volti e altro ...) questo supponiamo di risolvere il tuo "INFO: Impossibile trovare il comp ..."
Daniel,

Ho di nuovo provato ad attuare il tuo secondo suggerimento, ma non funziona ancora. La finestra di dialogo si apre ma non contiene i dati dell'elemento selezionato. Il registro visualizza "Impossibile trovare il componente con identificativo" j_idt31 "in vista" e non riesco più a eseguire il debug.
perissf, il

5

È perché la scheda è anche un contenitore di nomi ... il tuo aggiornamento dovrebbe essere update="Search:insTable:display"Quello che puoi fare è solo posizionare la finestra di dialogo all'esterno del modulo e ancora all'interno della scheda, quindi sarebbe:update="Search:display"


0

Prova a cambiare update="insTable:display"in update="display". Credo che non sia possibile aggiungere un prefisso all'ID con l'ID del modulo in questo modo.


2
Risposta molto vecchia ma fuorviante. Fare riferimento al post di BalusC sopra, che mostra chiaramente il prefisso dell'ID di un componente con l'ID del modulo allegato: <h: form id = "form"> <p: commandLink update = ": otherform: result"> <! - OK! -> </ h: form> <h: form id = "otherform"> <h: panelGroup id = "risultato" /> </ h: form>
J Slick

0

So che questa ha già un'ottima risposta da parte di BalusC, ma ecco un piccolo trucco che uso per convincere il container a dirmi il clientId corretto .

  1. Rimuovere l'aggiornamento sul componente che non funziona
  2. Inserisci un componente temporaneo con un aggiornamento fasullo all'interno del componente che stavi tentando di aggiornare
  3. colpito la pagina, l'errore di eccezione servlet ti dirà l'ID client corretto che devi fare riferimento.
  4. Rimuovere il componente fasullo e inserire l'ID client corretto nell'aggiornamento originale

Ecco un esempio di codice poiché le mie parole potrebbero non descriverlo al meglio.

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

Rimuovere l'aggiornamento non riuscito all'interno di questo componente

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

Aggiungi un componente all'interno del componente dell'ID che stai tentando di aggiornare usando un aggiornamento che fallirà

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

Hit questa pagina e visualizzare l'errore. L'errore è: javax.servlet.ServletException: impossibile trovare il componente per l'espressione "BogusUpdate" a cui si fa riferimento dalle schede: insTable: BogusButton

Quindi il clientId corretto da usare sarebbe quindi il grassetto più l'id del contenitore di destinazione (visualizzato in questo caso)

tabs:insTable:display
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.