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


140

Наступний код натхненний 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"

Як це спричинено і як це можна вирішити?

Відповіді:


313

Подивіться у вихідному коді 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, їх ID буде випереджати згенерований ідентифікатор клієнта всіх дочірніх компонентів. Зауважте, що коли у них немає фіксованого ідентифікатора, тоді JSF буде використовувати автогенерований ідентифікатор у j_idXXXформаті. Вам слід абсолютно уникати цього, надаючи їм фіксований ідентифікатор. У OmniFacesNoAutoGeneratedIdViewHandler може бути корисний в цьому процесі розробки.

Якщо ви знаєте, що потрібно знайти 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 / tag, а ціль знаходиться поза ним, і, таким чином, ви не обов'язково знаєте ідентифікатор батьківського контейнера іменування поточного контейнера імен, то ви можете динамічно посилатися на нього через 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 :

Вираз пошуку складається або з ідентифікатора (який узгоджений точно проти власності ідентифікатора UIComponentабо серії таких ідентифікаторів , пов'язаних з UINamingContainer#getSeparatorCharзначенням символів. Алгоритм пошуку повинен працює наступним чином , хоча альтернативні alogrithms можуть бути використані до тих пір , як кінцевий результат той самий:

  • Визначте, UIComponentщо буде базою для пошуку, зупинившись, як тільки буде виконано одну з наступних умов:
    • Якщо пошуковий вираз починається з символу роздільника (називається "абсолютним" пошуковим виразом), базою буде корінь UIComponentдерева компонентів. Провідний символ роздільника буде знятий, а решта пошукового вираження буде розглянута як "відносний" пошуковий вираз, як описано нижче.
    • В іншому випадку, якщо це UIComponentє, NamingContainerце послужить основою.
    • В іншому випадку шукайте батьків цього компонента. Якщо NamingContainerвиникне a , це буде базою.
    • Інакше (якщо цього не NamingContainerзустрічається), коренем UIComponentбуде база.
  • Пошуковий вираз (можливо, модифікований на попередньому кроці) тепер є "відносним" пошуковим виразом, який буде використовуватися для пошуку компонента (за наявності), який має ідентифікатор, що відповідає, в межах базового компонента. Матч проводиться так:
    • Якщо пошуковий вираз є простим ідентифікатором, це значення порівнюється з властивістю id, а потім рекурсивно через грані та діти бази UIComponent(за винятком випадків, якщо знайдено нащадка NamingContainer, його власні грані та діти не шукаються).
    • Якщо пошуковий вираз включає більше одного ідентифікатора, розділеного символом роздільника, перший ідентифікатор використовується для пошуку a NamingContainerза правилами в попередній точці кулі. Тоді findComponent()метод цього NamingContainerбуде викликаний, передавши залишок пошукового вираження.

Зверніть увагу, що PrimeFaces також дотримується специфікації JSF, але RichFaces використовує "деякі додаткові винятки" .

"reRender" використовує UIComponent.findComponent()алгоритм (за деякими додатковими винятками) для пошуку компонента в дереві компонентів.

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

Ніколи не використовуйте prependId="false"

Якщо це все ще не працює, перевірте, чи не використовуєте ви <h:form prependId="false">. Це не вдасться під час обробки надсилання та рендерингу ajax. Дивіться також це пов'язане питання: UIForm з prependId = "помилковими" перервами <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 дозволяють посилатися на компоненти через пошукові вирази компонентів 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)"?


@jack: Просто прочитайте javadoc: docs.oracle.com/javaee/6/api/javax/faces/component/… З JSF 2.0 він став налаштовуватися замість константи.
BalusC

Чи не застаріло SEPARATOR_CHAR ? Чи можете ви навести приклад, як викликати вкладений компонент, наприклад: context.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );Також, будь ласка, включіть код xhtml.
коктейлі

1
Дякую, зверніть увагу на те, що не вдалося ajax передати всередину форми із prependId="false"збереженим моїм днем.
Гейм

яке саме значення ідентифікатора клієнта викладено у вашому поясненні? Це те саме, що в JSF -> "Ідентифікатор клієнтської сторони для цього компонента". з повагою + дякую за вашу роботу.
Стів Ой

1
@ antonu17: Як зазначено у відповіді, він підтримується лише у f: ajax у Mojarra.
BalusC

9

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

а тепер до вашого питання:

вибачте, мені знадобилося трохи часу, щоб отримати те, що саме ти хотів здійснити,

я зробив у своєму веб-додатку сам зараз, і це працює

як я вже говорив, перед тим, як розмістити діалогове вікно 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: має виглядати так (все, що я зробила, це змінити атрибут оновлення)

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

те саме працює в моєму веб-додатку, і якщо це не працює для вас, то я думаю, що у вашому коді Java Bean щось не так ...


Я рекомендую вам спробувати інші зміни, які я написав у своїй відповіді (із прив'язкою та обличчям-конфігурацією та іншими ...), це припустимо вирішити "ІНФО: Неможливо знайти компонент ..."
Даніель

Я знову спробував реалізувати вашу другу пропозицію, але вона все ще не працює. Діалогове вікно відкриється, але не містить даних вибраного елемента. У журналі відображається "Неможливо знайти компонент з ідентифікатором" j_idt31 "у перегляді", і я не в змозі налагоджувати більше цього.
перисф

5

Це тому, що вкладка також є контейнером з іменами ... ваше оновлення повинно бути update="Search:insTable:display"те, що ви також можете зробити, це просто розмістити діалогове вікно поза формою і все ще знаходиться на вкладці, тоді це буде:update="Search:display"


0

Спробуйте змінити update="insTable:display"на update="display". Я вважаю, що ви не можете перефіксувати ідентифікатор з таким ідентифікатором форми


2
Дуже стара, але оманлива відповідь. Перегляньте публікацію BalusC вище, чітко відображаючи префіксацію ідентифікатора компонента з ідентифікатором форми, що додається: <h: form id = "form"> <p: commandLink update = ": otherform: result"> <! - ОК! -> </ h: form> <h: form id = "otherform"> <h: panelGroup id = "результат" /> </h: form>
J Slick

0

Я знаю, що це вже чудова відповідь від 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

Тож правильним клієнтським ідентифікатором буде тоді жирний плюс ідентифікатор цільового контейнера (відображати в цьому випадку)

tabs:insTable:display
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.