¿Cómo encontrar el ID de cliente del componente para la actualización / renderización de ajax? No se puede encontrar el componente con la expresión "foo" referenciada desde "bar"

140

El siguiente código está inspirado en PrimeFaces DataGrid + DataTable Tutorials y se coloca en a <p:tab>de un <p:tabView>residente en a <p:layoutUnit>de a <p:layout>. Aquí está la parte interna del código (a partir del p:tabcomponente); La parte exterior es 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>

Cuando hago clic en <p:commandLink>, el código deja de funcionar y muestra el mensaje:

No se puede encontrar el componente con la expresión "insTable: display" al que se hace referencia en "tabs: insTable: select".

Cuando intento lo mismo usando <f:ajax>, falla con un mensaje diferente que básicamente dice lo mismo:

<f:ajax> contiene una identificación desconocida "insTable: display" no puede ubicarlo en el contexto del componente "pestañas: insTable: select"

¿Cómo se causa esto y cómo puedo resolverlo?

perissf
fuente

Respuestas:

313

Busque en la salida HTML la identificación real del cliente

Debe buscar en la salida HTML generada para encontrar la ID de cliente correcta. Abra la página en el navegador, haga clic derecho y Ver código fuente . Localice la representación HTML del componente JSF de interés y tómelo idcomo ID de cliente. Puede usarlo de manera absoluta o relativa, dependiendo del contenedor de nombres actual. Ver el siguiente capítulo.

Nota: si pasa a contener iteración índice como :0:, :1:, etc (porque es un componente dentro de la iteración), entonces usted necesita para darse cuenta de que la actualización de una ronda específica iteración no siempre es compatible. Vea la parte inferior de la respuesta para obtener más detalles al respecto.

Memorice NamingContainercomponentes y siempre déles una identificación fija

Si un componente al que le gustaría hacer referencia mediante el proceso / ejecución / actualización / renderización de ajax está dentro del mismo NamingContainerpadre, simplemente haga referencia a su propia ID.

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

Si se trata de no dentro de la misma NamingContainer, entonces es necesario hacer referencia a ella mediante un ID de cliente absoluta. Un ID de cliente absoluto comienza con el NamingContainercarácter separador, que es el predeterminado :.

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

NamingContainercomponentes son, por ejemplo <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(por lo tanto, todos los componentes de material compuesto), etc. Se les reconocen fácilmente mirando en la salida HTML generado, su identificación se antepondrá a la ID de cliente generado de todos los componentes hijos. Tenga en cuenta que cuando no tienen una ID fija, JSF usará una ID autogenerada en j_idXXXformato. Debería evitarlo absolutamente dándoles una identificación fija. Los OmniFacesNoAutoGeneratedIdViewHandler pueden ser útiles en este durante el desarrollo.

Si sabe encontrar el javadoc de la UIComponentpregunta en cuestión, también puede verificar si implementa la NamingContainerinterfaz o no. Por ejemplo, HtmlForm(la etiqueta UIComponentdetrás <h:form>) muestra que se implementa NamingContainer, pero HtmlPanelGroup(la etiqueta UIComponentdetrás <h:panelGroup>) no lo muestra, por lo que no se implementa NamingContainer. Aquí está el javadoc de todos los componentes estándar y aquí está el javadoc de PrimeFaces .

Resolviendo tu problema

Entonces en tu caso de:

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

El resultado HTML generado de se <h:panelGrid id="display">ve así:

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

Debe tomar exactamente eso idcomo ID de cliente y luego prefijar con :para su uso en update:

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

Referencias externas incluyen / tagfile / composite

Si este enlace de comando está dentro de un archivo de inclusión / etiqueta, y el destino está fuera de él, y por lo tanto no necesariamente conoce la ID del contenedor de nombres principal del contenedor de nombres actual, puede hacer referencia dinámica de esta UIComponent#getNamingContainer()manera:

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

O, si este enlace de comando está dentro de un componente compuesto y el objetivo está fuera de él:

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

O, si tanto el enlace de comando como el objetivo están dentro del mismo componente compuesto:

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

Consulte también Obtener ID del contenedor de nombres principal en la plantilla para el atributo de render / actualización

¿Cómo funciona debajo de las sábanas?

Todo esto se especifica como "expresión de búsqueda" en el UIComponent#findComponent()javadoc :

Una expresión de búsqueda consiste en un identificador (que coincide exactamente con la propiedad id de a UIComponent, o una serie de identificadores vinculados por el UINamingContainer#getSeparatorCharvalor del carácter. El algoritmo de búsqueda debe funcionar de la siguiente manera, aunque se pueden usar algoritmos alternativos siempre que El resultado final es el mismo:

  • Identifique lo UIComponentque será la base para la búsqueda, deteniéndose tan pronto como se cumpla una de las siguientes condiciones:
    • Si la expresión de búsqueda comienza con el carácter separador (llamado expresión de búsqueda "absoluta"), la base será la raíz UIComponentdel árbol de componentes. El carácter separador inicial se eliminará y el resto de la expresión de búsqueda se tratará como una expresión de búsqueda "relativa" como se describe a continuación.
    • De lo contrario, si esto UIComponentes un NamingContainerservirá de base.
    • De lo contrario, busque los padres de este componente. Si NamingContainerse encuentra un, será la base.
    • De lo contrario (si no NamingContainerse encuentra), la raíz UIComponentserá la base.
  • La expresión de búsqueda (posiblemente modificada en el paso anterior) ahora es una expresión de búsqueda "relativa" que se usará para ubicar el componente (si lo hay) que tiene una identificación que coincide, dentro del alcance del componente base. El partido se realiza de la siguiente manera:
    • Si la expresión de búsqueda es un identificador simple, este valor se compara con la propiedad id, y luego recursivamente a través de las facetas y los elementos secundarios de la base UIComponent(excepto que si NamingContainerse encuentra un descendiente , no se buscan sus propias facetas y elementos secundarios).
    • Si la expresión de búsqueda incluye más de un identificador separado por el carácter separador, el primer identificador se utiliza para ubicar a NamingContainerpor las reglas en el punto anterior. Luego, se llamará al findComponent()método de esta NamingContainer, pasando el resto de la expresión de búsqueda.

Tenga en cuenta que PrimeFaces también cumple con la especificación JSF, pero RichFaces usa "algunas excepciones adicionales" .

"reRender" usa un UIComponent.findComponent()algoritmo (con algunas excepciones adicionales) para encontrar el componente en el árbol de componentes.

Esas excepciones adicionales no se describen en detalle en ninguna parte, pero se sabe que los ID de componentes relativos (es decir, aquellos que no comienzan con :) no solo se buscan en el contexto del padre más cercano NamingContainer, sino también en todos los demás NamingContainercomponentes en la misma vista (que es relativamente trabajo costoso por cierto).

Nunca usar prependId="false"

Si todo esto aún no funciona, verifique si no lo está utilizando <h:form prependId="false">. Esto fallará durante el procesamiento del envío y el procesamiento de ajax. Consulte también esta pregunta relacionada: UIForm with prependId = "false" rompe <f: ajax render> .

Hacer referencia a ronda de iteración específica de componentes iterativos

Durante mucho tiempo no fue posible hacer referencia a un elemento iterado específico en componentes iterativos como <ui:repeat>y <h:dataTable>así:

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

Sin embargo, desde Mojarra 2.2.5 <f:ajax>comenzó a admitirlo (simplemente dejó de validarlo; por lo tanto, nunca más se enfrentaría a la excepción mencionada en la pregunta; se planea otra solución de mejora para eso más adelante).

Esto solo no funciona aún en las versiones actuales de MyFaces 2.2.7 y PrimeFaces 5.2. El soporte podría venir en futuras versiones. Mientras tanto, su mejor opción es actualizar el componente iterativo en sí, o un padre en caso de que no represente HTML, como <ui:repeat>.

Cuando use PrimeFaces, considere Expresiones de búsqueda o Selectores

PrimeFaces Search Expressions le permite hacer referencia a componentes a través de expresiones de búsqueda de árbol de componentes JSF. JSF tiene varios incorporados:

  • @this: componente actual
  • @form: padre UIForm
  • @all: documento completo
  • @none: nada

PrimeFaces ha mejorado esto con nuevas palabras clave y compatibilidad con expresiones compuestas:

  • @parent: componente principal
  • @namingcontainer: padre UINamingContainer
  • @widgetVar(name): componente identificado por dado widgetVar

También se pueden mezclar las palabras clave en expresiones compuestas, tales como @form:@parent, @this:@parent:@parent, etc.

PrimeFaces Selectors (PFS) como en le @(.someclass)permite hacer referencia a componentes a través de la sintaxis del selector CSS jQuery. Por ejemplo, hacer referencia a componentes que tienen toda una clase de estilo común en la salida HTML. Esto es particularmente útil en caso de que necesite hacer referencia a "muchos" componentes. Esto solo requiere que los componentes de destino tengan una ID de cliente en la salida HTML (fija o autogenerada, no importa). Consulte también ¿Cómo funcionan los selectores PrimeFaces como en update = "@ (. MyClass)"?

BalusC
fuente
@jack: solo lea javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/… Desde JSF 2.0 se ha vuelto configurable en lugar de una constante.
BalusC
¿ SEPARATOR_CHAR no está en desuso? ¿Puede dar un ejemplo sobre cómo llamar a un componente anidado, por ejemplo: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );También incluya el código xhtml.
Jacktrades
1
Gracias, nota sobre la falla en la remisión ajax dentro del formulario con prependId="false"salvado mi día.
Gaim
¿Cuál es el significado exacto de la identificación del cliente como se describe en su explicación? ¿Es lo mismo que en JSF -> "El identificador del lado del cliente para este componente". saludos + gracias por tu trabajo.
Steve Oh
1
@ antonu17: Como se indica en la respuesta, solo se admite en f: ajax de Mojarra.
BalusC
9

en primer lugar: por lo que sé, colocar el diálogo dentro de una vista de pestaña es una mala práctica ... será mejor que lo elimines ...

y ahora a tu pregunta:

lo siento, me tomó un tiempo obtener lo que querías implementar exactamente,

lo hice en mi aplicación web hace un momento, y funciona

como dije antes, coloca el p: diálogo fuera del `p: tabView,

deje el diálogo p: como sugirió inicialmente:

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

y el enlace de comando p: debería verse así (todo lo que hice fue cambiar el atributo de actualización)

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

Lo mismo funciona en mi aplicación web, y si no funciona para usted, entonces supongo que hay algo mal en su código de Java Bean ...

Daniel
fuente
Te recomiendo que pruebes los otros cambios que escribí en mi respuesta (con el enlace y la configuración de caras y otros ...) esto supone resolver tu "INFORMACIÓN: No se pueden encontrar componentes ..."
Daniel
Intenté nuevamente implementar su segunda sugerencia, pero aún no funciona. El cuadro de diálogo se abre pero no contiene los datos del elemento seleccionado. El registro muestra "No se puede encontrar el componente con el identificador" j_idt31 "a la vista", y no puedo depurar más que esto.
perissf
5

Es porque la pestaña también es un contenedor de nombres ... su actualización debería ser update="Search:insTable:display"Lo que puede hacer también es colocar su cuadro de diálogo fuera del formulario y dentro de la pestaña, entonces sería:update="Search:display"

Lyrion
fuente
0

Intenta cambiar update="insTable:display"a update="display". Creo que no puede prefijar la identificación con la identificación del formulario de esa manera.

Mr.J4mes
fuente
2
Muy antigua pero engañosa respuesta. Consulte la publicación de BalusC arriba, que muestra claramente el prefijo de la ID de un componente con la ID del formulario adjunto: <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

Sé que esto ya tiene una gran respuesta por parte de BalusC, pero aquí hay un pequeño truco que utilizo para obtener el contenedor que me indica el ID de cliente correcto .

  1. Elimine la actualización en su componente que no funciona
  2. Coloque un componente temporal con una actualización falsa dentro del componente que estaba intentando actualizar
  3. presione la página, el error de excepción de servlet le indicará el ID de cliente correcto al que debe hacer referencia.
  4. Elimine el componente falso y coloque el ID de cliente correcto en la actualización original

Aquí hay un ejemplo de código, ya que mis palabras pueden no describirlo mejor.

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

Elimine la actualización que falla dentro de este 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">

Agregue un componente dentro del componente de la identificación que está intentando actualizar utilizando una actualización que fallará

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

Haga clic en esta página y vea el error. El error es: javax.servlet.ServletException: no se puede encontrar el componente para la expresión "BogusUpdate" al que se hace referencia en las pestañas: insTable: BogusButton

Por lo tanto, el ID de cliente correcto para usar sería la negrita más la identificación del contenedor de destino (mostrar en este caso)

tabs:insTable:display
jeff
fuente