Как узнать идентификатор клиента компонента для обновления / рендеринга ajax?Не удается найти компонент с выражением "foo", на которое ссылается "bar" - PullRequest
125 голосов
/ 26 декабря 2011

Следующий код основан на руководствах PrimeFaces DataGrid + DataTable и помещен в <p:tab> из <p:tabView>, находящегося в <p:layoutUnit> из <p:layout>. Вот внутренняя часть кода (начиная с компонента p:tab); внешняя часть тривиальна.

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

Когда я нажимаю <p:commandLink>, код перестает работать и выдает сообщение:

Не удается найти компонент с выражением «insTable: display», на которое ссылаются вкладки «: insTable: select».

Когда я пытаюсь сделать то же самое, используя <f:ajax>, происходит сбой с другим сообщением, в основном сообщающим то же самое:

<f:ajax> содержит неизвестный идентификатор «insTable: display» не может найти его в контексте компонента «tabs: insTable: select»

Как это вызвано и как я могу это решить?

Ответы [ 5 ]

284 голосов
/ 27 декабря 2011

Посмотрите в выводе HTML фактический идентификатор клиента

Вам необходимо просмотреть сгенерированный вывод HTML, чтобы узнать правильный идентификатор клиента. Откройте страницу в браузере, сделайте правый клик и Просмотр источника . Найдите HTML-представление интересующего компонента JSF и примите его id в качестве идентификатора клиента. Вы можете использовать его в абсолютном или относительном порядке в зависимости от текущего контейнера именования. См. Следующую главу.

Примечание: если он содержит индекс итерации, такой как :0:, :1: и т. Д. (Поскольку он находится внутри итеративного компонента), то вам необходимо понимать, что обновление определенного цикла итерации не всегда поддерживается. Подробнее об этом см. В нижней части ответа.

Запомните NamingContainer компоненты и всегда присваивайте им фиксированный идентификатор

Если компонент, на который вы хотите сослаться с помощью ajax-процесса / execute / update / render, находится в том же NamingContainer родительском элементе, просто укажите его собственный идентификатор.

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

Если это , а не внутри того же NamingContainer, то вам нужно сослаться на него, используя абсолютный идентификатор клиента. Абсолютный идентификатор клиента начинается с символа-разделителя NamingContainer, который по умолчанию :.

<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 компоненты, например, <h:form>, <h:dataTable>, <p:tabView>, <cc:implementation> (то есть, все составные компоненты) и т. Д. Вы легко узнаете их, посмотрев на сгенерированные Вывод HTML, их идентификатор будет добавлен к сгенерированному идентификатору клиента всех дочерних компонентов. Обратите внимание: если у них нет фиксированного идентификатора, JSF будет использовать автоматически созданный идентификатор в формате j_idXXX. Вы должны абсолютно избежать этого, дав им фиксированный идентификатор. OmniFaces NoAutoGeneratedIdViewHandler может помочь в этом во время разработки.

Если вы знаете, что нашли javadoc рассматриваемого UIComponent, то вы также можете просто проверить, реализует ли он интерфейс NamingContainer или нет. Например, HtmlForm (тег UIComponent позади <h:form>) показывает, что он реализует NamingContainer, но HtmlPanelGroup (UIComponent позади * 1059) * tag) не показывает его, поэтому он не реализует NamingContainer. Вот javadoc всех стандартных компонентов и Вот javadoc PrimeFaces .

Решение вашей проблемы

Итак, в вашем случае:

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

Сгенерированный HTML-вывод <h:panelGrid id="display"> выглядит следующим образом:

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

Вам нужно взять именно это id в качестве идентификатора клиента, а затем использовать префикс : для использования в update:

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

Ссылки на внешние ссылки включают / tagfile / смесь

Если эта командная ссылка находится внутри include / tagfile, а цель находится за ее пределами, и, таким образом, вы не обязательно знаете идентификатор родительского контейнера именования текущего контейнера именования, тогда вы можете динамически ссылаться на него через UIComponent#getNamingContainer() вот так:

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

Или, если эта командная ссылка находится внутри составного компонента, а цель находится за ее пределами:

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

Или, если командная ссылка и цель находятся внутри одного составного компонента:

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

См. Также Получить идентификатор родительского контейнера именования в шаблоне для атрибута render / update

Как это работает под одеялом

Это все указано как "поисковое выражение" в в UIComponent#findComponent() javadoc :

A поисковое выражение состоит либо из идентификатора (который точно совпадает со свойством id для UIComponent, либо из ряда таких идентификаторов, связанных значением символа UINamingContainer#getSeparatorChar. Алгоритм поиска следует действовать следующим образом, хотя могут использоваться альтернативные алогриты, если конечный результат одинаков:

  • Определите UIComponent, который будет основой для поиска, остановив его, как только будет выполнено одно из следующих условий:
    • Если поисковое выражение начинается с символа-разделителя (называемого «абсолютным» поисковым выражением), основой будет корень UIComponent дерева компонентов.Символ начального разделителя будет удален, а оставшаяся часть поискового выражения будет обрабатываться как «относительное» поисковое выражение, как описано ниже.
    • В противном случае, если это UIComponent является NamingContainer, оно будетслужить основой.
    • В противном случае найдите родителей этого компонента.Если встречается NamingContainer, это будет основание.
    • В противном случае (если не обнаружено NamingContainer) корень UIComponent будет основанием.
  • Поисковое выражение (возможно, измененное на предыдущем шаге) теперь является «относительным» поисковым выражением, которое будет использоваться для определения местоположения компонента (если есть), который имеет идентификатор, который совпадает, в области действия базового компонента.Соответствие выполняется следующим образом:
    • Если поисковое выражение является простым идентификатором, это значение сравнивается со свойством id, а затем рекурсивно проходит через фасеты и дочерние элементы базы UIComponent (за исключением того, что если aпотомок NamingContainer найден, его собственные фасеты и дочерние элементы не ищутся).
    • Если выражение поиска содержит более одного идентификатора, разделенного символом-разделителем, первый идентификатор используется для определения NamingContainer поправила в предыдущем пункте.Затем будет вызван метод findComponent() этого NamingContainer, передав остаток выражения поиска.

Обратите внимание, что PrimeFaces также придерживается спецификации JSF, но RichFaces использует «некоторые дополнительные исключения» .

"reRender" использует алгоритм UIComponent.findComponent() (с некоторыми дополнительными исключениями) для поиска компонента в дереве компонентов.

Эти дополнительные исключенияподробно нигде не описываются, но известно, что относительные идентификаторы компонентов (т. е. не начинающиеся с :) ищутся не только в контексте ближайшего родителя NamingContainer, но и во всех других компонентах NamingContainer в том жеview (кстати, это довольно дорогая работа).

Никогда не используйте prependId="false"

Если все это по-прежнему не работает, то убедитесь, что вы не используете <h:form prependId="false">,Это не удастся во время обработки ajax submit и render.Смотрите также этот связанный вопрос: UIForm с prependId = "false" разрывы.

Ссылка на конкретный цикл итерации итерационных компонентов

Долгое время было невозможно ссылаться на конкретный итеративный элемент в итерационных компонентах, таких как <ui:repeat> и <h:dataTable>, например, так:

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

Однако, так как Mojarra 2.2.5 <f:ajax> начал поддерживать его (он просто прекратил проверять его; таким образом, вы никогда больше не столкнетесь с упомянутым в вопросе исключением; другое исправление улучшения запланировано начто позже).

Это пока не работает только в текущих версиях MyFaces 2.2.7 и PrimeFaces 5.2.Поддержка может прийти в будущих версиях.В то же время, вам лучше всего обновить сам итеративный компонент или родительский элемент в случае, если он не отображает HTML, например <ui:repeat>.

При использовании PrimeFaces учитывайте выражения поиска или селекторы

Выражения поиска PrimeFaces позволяет ссылаться на компоненты через выражения поиска в дереве компонентов JSF.JSF имеет несколько встроенных функций:

  • @this: текущий компонент
  • @form: родительский UIForm
  • @all: весь документ
  • @none: ничего

В PrimeFaces это улучшено благодаря новым ключевым словам и поддержке составных выражений:

  • @parent: родительский компонент
  • @namingcontainer: родительский UINamingContainer
  • @widgetVar(name): компонент, определяемый данным widgetVar

Вы также можете смешивать эти ключевые слова в составных выражениях, таких как @form:@parent, @this:@parent:@parent и т. Д.

Селекторы PrimeFaces (PFS) , как в @(.someclass), позволяют ссылаться на компоненты с помощью синтаксиса селектора jQuery CSS.Например, ссылки на компоненты, имеющие общий класс стиля в выводе HTML.Это особенно полезно в случае, если вам нужно сослаться на «много» компонентов.Это только требует, чтобы целевые компоненты имели все идентификаторы клиента в выводе HTML (фиксированные или автоматически сгенерированные, не имеет значения).См. Также Как работают селекторы PrimeFaces, как в update = "@ (. MyClass)"?

9 голосов
/ 26 декабря 2011

Прежде всего: насколько я знаю, размещение диалога внутри табуляции - это плохая практика ... вам лучше убрать его ...

и теперь на ваш вопрос:

извините, у меня ушло некоторое время, чтобы понять, что именно вы хотели реализовать,

сделал в моем веб-приложении только сейчас, и он работает

, как я уже говорил, перед тем как поместить диалоговое окно p: наружу`p: tabView,

выходят из диалогового окна p: как вы изначально предложили:

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

, и p: commandlink должна выглядеть следующим образом (все, что я сделал, это изменил атрибут обновления)

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

то же самое работает в моем веб-приложении, и если оно не работает для вас, то я думаю, что-то не так в вашем коде Java-бина ...

5 голосов
/ 26 декабря 2011

Это потому, что вкладка также является контейнером именования ... ваше обновление должно быть update="Search:insTable:display" Что вы можете сделать, это просто разместить диалог вне формы и по-прежнему внутри вкладки, тогда это будет: update="Search:display"

0 голосов
/ 21 марта 2019

Я знаю, что BalusC уже дал отличный ответ, но вот небольшая хитрость, которую я использую, чтобы получить контейнер, сообщающий мне правильный clientId .

  1. Удалите обновление вашего компонента, который не работает
  2. Поместите временный компонент с поддельным обновлением в компонент, который вы пытались обновить
  3. попадание на страницу, ошибка исключения сервлета сообщит вам правильный идентификатор клиента, на который вам нужно сослаться.
  4. Удалите фиктивный компонент и установите правильный clientId в исходном обновлении

Вот пример кода, поскольку мои слова могут не описать его лучше всего.

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

Удалить сбой обновления в этом компоненте

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

Добавьте компонент в компоненте идентификатора, который вы пытаетесь обновить, используя обновление, которое не будет выполнено

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

Нажмите эту страницу и просмотрите ошибку. Ошибка: javax.servlet.ServletException: не удается найти компонент для выражения "BogusUpdate", на которое ссылается вкладок: insTable: BogusButton

Таким образом, в качестве правильного значения clientId будет использоваться жирный шрифт плюс идентификатор целевого контейнера (в данном случае отображается)

tabs:insTable:display
0 голосов
/ 26 декабря 2011

Попробуйте изменить update="insTable:display" на update="display". Я полагаю, что вы не можете поставить префикс идентификатором формы таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...