Como descobrir o ID do cliente do componente para atualização / renderização do ajax? Não foi possível encontrar o componente com a expressão "foo" referenciada em "bar"

140

O código a seguir é inspirado nos Tutoriais do PrimeFaces DataGrid + DataTable e colocado em um <p:tab>de um <p:tabView>residente em um <p:layoutUnit>de a <p:layout>. Aqui está a parte interna do código (iniciando no p:tabcomponente); a parte externa é 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>

Quando clico no <p:commandLink>, o código para de funcionar e dá a mensagem:

Não foi possível encontrar o componente com a expressão "insTable: display" referenciada em "guias: insTable: select".

Quando tento o mesmo uso <f:ajax>, ele falha com uma mensagem diferente dizendo o mesmo:

<f:ajax> contém um ID desconhecido "insTable: display" não pode localizá-lo no contexto do componente "guias: insTable: select"

Como isso é causado e como posso resolvê-lo?

perissf
fonte

Respostas:

313

Procure na saída HTML o ID do cliente real

Você precisa procurar na saída HTML gerada para descobrir o ID do cliente correto. Abra a página no navegador, clique com o botão direito e em Exibir fonte . Localize a representação HTML do componente JSF de interesse e use-o idcomo ID do cliente. Você pode usá-lo de maneira absoluta ou relativa, dependendo do contêiner de nomenclatura atual. Veja o capítulo seguinte.

Nota: se tiver um índice de iteração como :0:, :1:etc (porque está dentro de um componente de iteração), será necessário perceber que a atualização de uma rodada de iteração específica nem sempre é suportada. Veja a parte inferior da resposta para mais detalhes.

Memorize NamingContainercomponentes e sempre dê a eles um ID fixo

Se um componente que você gostaria de referenciar por ajax process / execute / update / render estiver dentro do mesmo NamingContainerpai, basta fazer referência ao seu próprio ID.

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

Se é não dentro da mesma NamingContainer, então você precisa fazer referência a ela usando um ID do cliente absoluta. Um ID de cliente absoluto começa com o NamingContainercaractere separador, que é por padrão :.

<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 são, por exemplo <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation>(assim, todos os componentes em materiais compósitos), etc. Você reconhecê-los facilmente, olhando para a saída HTML gerado, seu ID será anexado ao ID do cliente gerado de todos os componentes filhos. Observe que quando eles não têm um ID fixo, o JSF usará um ID gerado automaticamente no j_idXXXformato. Você deve absolutamente evitar isso, dando a eles um ID fixo. O OmniFacesNoAutoGeneratedIdViewHandler pode ser útil nisso durante o desenvolvimento.

Se você souber encontrar o javadoc do UIComponentem questão, também poderá verificar se ele implementa a NamingContainerinterface ou não. Por exemplo, a HtmlForm(a marca de UIComponenttrás <h:form>) mostra que ela é implementada NamingContainer, mas a HtmlPanelGroup(a marca de UIComponenttrás <h:panelGroup>) não a mostra e, portanto, não é implementada NamingContainer. Aqui está o javadoc de todos os componentes padrão e o javadoc do PrimeFaces .

Resolvendo seu problema

Então, no seu 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">

A saída HTML gerada se <h:panelGrid id="display">parece com isso:

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

Você precisa usar exatamente isso idcomo ID do cliente e prefixar com o :uso em update:

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

Referenciar fora include / tagfile / composite

Se esse link de comando estiver dentro de um include / tagfile e o destino estiver fora dele, você não conhecerá necessariamente o ID do pai do contêiner de nomeação do contêiner de nomeação atual, poderá fazer referência dinâmica a ele da seguinte UIComponent#getNamingContainer()maneira:

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

Ou, se este link de comando estiver dentro de um componente composto e o destino estiver fora dele:

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

Ou, se o link de comando e o destino estiverem dentro do mesmo componente composto:

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

Consulte também Obter identificação do contêiner de nomeação pai no modelo para no atributo de renderização / atualização

Como funciona sob as cobertas

Isso tudo é especificado como "expressão de pesquisa" no UIComponent#findComponent()javadoc :

Uma expressão de pesquisa consiste em um identificador (que corresponde exatamente à propriedade id de a UIComponentou em uma série desses identificadores vinculados pelo UINamingContainer#getSeparatorCharvalor do caractere. O algoritmo de pesquisa deve operar da seguinte maneira, embora alogritmos alternativos possam ser usados ​​desde que resultado final é o mesmo:

  • Identifique UIComponentqual será a base da pesquisa, parando assim que uma das seguintes condições for atendida:
    • Se a expressão de pesquisa começar com o caractere separador (chamado de expressão de pesquisa "absoluta"), a base será a raiz UIComponentda árvore de componentes. O caractere separador principal será retirado e o restante da expressão de pesquisa será tratado como uma expressão de pesquisa "relativa", conforme descrito abaixo.
    • Caso contrário, se este UIComponenté um NamingContainerque vai servir de base.
    • Caso contrário, procure os pais deste componente. Se um NamingContainerfor encontrado, será a base.
    • Caso contrário (se não NamingContainerfor encontrado), a raiz UIComponentserá a base.
  • A expressão de pesquisa (possivelmente modificada na etapa anterior) agora é uma expressão de pesquisa "relativa" que será usada para localizar o componente (se houver) que possui um ID correspondente, no escopo do componente base. A partida é realizada da seguinte maneira:
    • Se a expressão de pesquisa for um identificador simples, esse valor será comparado à propriedade id e, recursivamente, através das facetas e filhos da base UIComponent(exceto que, se um descendente NamingContainerfor encontrado, suas próprias facetas e filhos não serão pesquisados).
    • Se a expressão de pesquisa incluir mais de um identificador separado pelo caractere separador, o primeiro identificador será usado para localizar a NamingContainerpelas regras no marcador anterior. Em seguida, o findComponent()método NamingContainerserá chamado, passando o restante da expressão de pesquisa.

Observe que o PrimeFaces também adere à especificação JSF, mas o RichFaces usa "algumas exceções adicionais" .

"reRender" usa o UIComponent.findComponent()algoritmo (com algumas exceções adicionais) para encontrar o componente na árvore de componentes.

Essas exceções adicionais não são descritas em nenhum lugar em detalhes, mas sabe-se que os IDs de componentes relativos (ou seja, aqueles que não estão começando com :) não são apenas pesquisados ​​no contexto do pai mais próximo NamingContainer, mas também em todos os outros NamingContainercomponentes na mesma exibição (o que é relativamente trabalho caro a propósito).

Nunca use prependId="false"

Se tudo isso ainda não funcionar, verifique se você não está usando <h:form prependId="false">. Isso falhará durante o processamento do envio e renderização do ajax. Consulte também esta pergunta relacionada: UIForm com prependId = "false" quebra <f: ajax render> .

Fazendo referência à rodada de iteração específica dos componentes de iteração

Foi por muito tempo, não é possível fazer referência a um específico iterated item na iteração componentes como <ui:repeat>e <h:dataTable>assim:

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

No entanto, desde o Mojarra 2.2.5, o <f:ajax>começou a apoiá-lo (ele simplesmente parou de validá-lo; portanto, você nunca mais enfrentaria a exceção mencionada na pergunta; outra correção de aprimoramento está planejada para isso posteriormente).

Isso ainda não funciona nas versões atuais do MyFaces 2.2.7 e PrimeFaces 5.2. O suporte pode vir nas versões futuras. Enquanto isso, sua melhor aposta é atualizar o componente de iteração, ou um pai, caso ele não renderize HTML, como <ui:repeat>.

Ao usar o PrimeFaces, considere Pesquisar expressões ou seletores

O PrimeFaces Search Expressions permite que você faça referência a componentes via expressões de pesquisa em árvore de componentes JSF. O JSF possui vários embutidos:

  • @this: componente atual
  • @form: pai UIForm
  • @all: documento inteiro
  • @none: nada

O PrimeFaces aprimorou isso com novas palavras-chave e suporte à expressão composta:

  • @parent: componente pai
  • @namingcontainer: pai UINamingContainer
  • @widgetVar(name): componente conforme identificado por widgetVar

Você também pode misturar essas palavras-chave em expressões compostas, tais como @form:@parent, @this:@parent:@parent, etc.

O PrimeFaces Selectors (PFS), como em, @(.someclass)permite que você faça referência a componentes por meio da sintaxe do seletor jQuery CSS. Por exemplo, referenciando componentes com todas as classes de estilo comuns na saída HTML. Isso é particularmente útil caso você precise fazer referência a "muitos" componentes. Isso requer apenas que os componentes de destino tenham todo um ID do cliente na saída HTML (fixo ou gerado automaticamente, não importa). Consulte também Como funcionam os seletores do PrimeFaces como em update = "@ (. MyClass)"?

BalusC
fonte
@jack: Basta ler javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/… Desde o JSF 2.0, ele se tornou configurável em vez de uma constante.
precisa saber é o seguinte
SEPARATOR_CHAR não está obsoleto? Você pode dar um exemplo de como chamar um componente aninhado, por exemplo: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );Inclua também o código xhtml.
jacktrades
1
Obrigado, observe sobre a falha na renderização do ajax dentro do formulário com prependId="false"salvou meu dia.
Gaim
qual é o significado exato do ID do cliente, conforme descrito em sua explicação? É o mesmo que em JSF -> "O identificador do lado do cliente para este componente". cumprimentos + obrigado pelo seu trabalho.
Steve Oh
1
@ antonu17: Como indicado na resposta, ele é suportado apenas no f: ajax de Mojarra.
precisa saber é o seguinte
9

Em primeiro lugar: até onde eu sei, colocar a caixa de diálogo dentro de uma tabview é uma prática ruim ... é melhor retirá-la ...

e agora a sua pergunta:

desculpe, demorei um pouco para conseguir o que exatamente você queria implementar,

fiz no meu aplicativo da web agora mesmo, e funciona

como eu disse antes, coloque a caixa de diálogo p: ao lado do `p: tabView,

deixe a caixa de diálogo p: como você sugeriu 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>   

e o link p: commandlink deve ficar assim (tudo o que fiz foi mudar o atributo update)

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

o mesmo funciona no meu aplicativo da web e, se não funcionar para você, acho que há algo errado no seu código java bean ...

Daniel
fonte
Eu recomendo que você tente as outras mudanças que eu escrevi na minha resposta (com a ligação e faces-config e outros ...) esta supor para resolver o seu "INFO: Não é possível encontrar compone ..."
Daniel
Tentei novamente implementar sua segunda sugestão, mas ela ainda não está funcionando. A caixa de diálogo é aberta, mas não contém os dados do item selecionado. O log exibe "Não é possível encontrar o componente com o identificador" j_idt31 "na exibição" e não consigo depurar mais do que isso.
perissf
5

É porque a guia também é um contêiner de nomes ... sua atualização deve ser update="Search:insTable:display"O que você também pode fazer é colocar sua caixa de diálogo fora do formulário e ainda dentro da guia, então seria:update="Search:display"

Lyrion
fonte
0

Tente mudar update="insTable:display"para update="display". Eu acredito que você não pode prefixar o ID com o ID do formulário assim.

Mr.J4mes
fonte
2
Resposta muito antiga, mas enganosa. Consulte a publicação do BalusC acima, mostrando claramente o prefixo do ID de um componente com o ID do formulário anexo: <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

Eu sei que isso já tem uma ótima resposta da BalusC, mas aqui está um pequeno truque que eu uso para fazer com que o contêiner me diga o clientId correto .

  1. Remova a atualização do seu componente que não está funcionando
  2. Coloque um componente temporário com uma atualização falsa no componente que você estava tentando atualizar
  3. Na página, o erro de exceção do servlet informará o ID do cliente correto que você precisa fazer referência.
  4. Remova o componente falso e coloque o clientId correto na atualização original

Aqui está um exemplo de código, pois minhas palavras podem não descrevê-lo melhor.

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

Remova a atualização com falha neste 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">

Adicione um componente ao componente do ID que você está tentando atualizar usando uma atualização que falhará

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

Acesse esta página e veja o erro. O erro é: javax.servlet.ServletException: Não foi possível encontrar o componente da expressão "BogusUpdate" referenciado nas guias: insTable: BogusButton

Portanto, o clientId correto a ser usado seria o negrito mais o ID do contêiner de destino (exibido neste caso)

tabs:insTable:display
jeff
fonte