Как узнать идентификатор клиента компонента для обновления / рендеринга ajax? Не удается найти компонент с выражением "foo", на которое ссылается "bar"
Следующий код вдохновлен PrimeFaces DataGrid + DataTable Tutorials и помещен в <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", на которое ссылаются "tabs: insTable: select".
Когда я пытаюсь сделать то же самое, используя <f:ajax>
, это терпит неудачу с другим сообщением, в основном говорящим то же самое:
<f:ajax>
содержит неизвестный идентификатор "insTable:display" не может найти его в контексте компонента "tabs: insTable: select"
Как это вызвано и как я могу решить это?
6 ответов
Посмотрите в выводе 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
позади <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 / составной
Если эта командная ссылка находится внутри 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">
Как это работает под одеялом
Это все указано как "поисковое выражение" в UIComponent#findComponent()
Javadoc:
Выражение поиска состоит из идентификатора (который точно совпадает со свойством id
UIComponent
или ряд таких идентификаторов, связанныхUINamingContainer#getSeparatorChar
значение символа. Алгоритм поиска должен работать следующим образом, хотя могут использоваться альтернативные аллогриты, если конечный результат одинаков:
- Определить
UIComponent
это будет основа для поиска, остановившись, как только будет выполнено одно из следующих условий:
- Если поисковое выражение начинается с символа-разделителя (называемого "абсолютным" поисковым выражением), основой будет корень
UIComponent
дерева компонентов. Символ начального разделителя будет удален, а остальная часть поискового выражения будет обрабатываться как "относительное" поисковое выражение, как описано ниже.- В противном случае, если это
UIComponent
этоNamingContainer
это послужит основой.- В противном случае найдите родителей этого компонента. Если
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" прерывает
Ссылка на конкретный итерационный раунд итерационных компонентов
В течение долгого времени было невозможно ссылаться на конкретный итеративный элемент в итерационных компонентах, таких как <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>
Однако, начиная с Мохарры 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)"?
Во -первых: насколько я знаю, размещение диалога внутри табуляции - это плохая практика... лучше его убрать...
а теперь к вашему вопросу:
извини, мне потребовалось некоторое время, чтобы понять, что именно ты хотел реализовать,
сделал в моем веб-приложении сам только сейчас, и это работает
как я уже говорил, поместите диалоговое окно 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-бина...
Это потому, что вкладка является также контейнером имен... Ваше обновление должно быть update="Search:insTable:display"
То, что вы можете сделать, это просто разместить диалог вне формы и по-прежнему внутри вкладки, тогда это будет: update="Search:display"
Обратите внимание, что начиная с PrimeFaces 10 и выше вы можете использовать наблюдателя и событие.
Это позволяет вам обновлять компоненты на основе пользовательского имени события, установленного@obs(event)
ключевое слово. Например:
<p:commandButton update="@obs(myEvent)"/>
<h:panelGroup>
<p:autoUpdate on="myEvent"/>
</h:panelGroup>
Видеть:
Я знаю, что BalusC уже дал хороший ответ, но вот небольшая хитрость, которую я использую, чтобы получить контейнер, сообщающий мне правильный clientId.
- Удалите обновление вашего компонента, который не работает
- Поместите временный компонент с поддельным обновлением в компонент, который вы пытались обновить
- нажмите на страницу, ошибка исключения сервлета скажет вам правильный идентификатор клиента, на который вам нужно сослаться.
- Удалите фиктивный компонент и поместите правильный 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
Попробуйте изменить update="insTable:display"
в update="display"
, Я полагаю, что вы не можете добавить префикс id к идентификатору формы.