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

Следующий код вдохновлен учебными материалами PrimeMaces 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" не может найти его в контексте вкладки компонента: insTable: select "

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

Ответ 1

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

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

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

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

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

<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 за тегом <h:panelGroup>) не отображает его, поэтому он не реализует 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/composite

Если эта командная ссылка находится внутри файла 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:

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

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

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

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

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

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

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

Ссылка на конкретную итерацию раунда итерирующих компонентов

В течение долгого времени невозможно было ссылаться на конкретный итерированный элемент при итерации таких компонентов, как <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 Search Expressions позволяет ссылаться на компоненты через выражения поиска дерева компонентов JSF. JSF имеет несколько встроенных функций:

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

PrimeFaces улучшил это с помощью новых ключевых слов и поддержки композитных выражений:

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

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

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

Ответ 2

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

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

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

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

как я сказал раньше, поставьте сторону p: dialog вне поля `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 bean...

Ответ 3

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

Ответ 4

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