web-dev-qa-db-ja.com

ajax更新/レンダリング用のコンポーネントのクライアントIDを見つける方法は? 「bar」から参照されている式「foo」を持つコンポーネントが見つかりません

次のコードは、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>をクリックすると、コードが機能しなくなり、次のメッセージが表示されます。

「tabs:insTable:select」から参照されている式「insTable:display」を持つコンポーネントが見つかりません。

<f:ajax>を使用して同じことを試みると、基本的に同じことを伝える別のメッセージで失敗します。

<f:ajax>に不明なID「insTable:display」が含まれていますが、コンポーネント「tabs:insTable:select」のコンテキストでそれを見つけることができません

これはどのように引き起こされ、どうすれば解決できますか?

125
perissf

実際のクライアントIDのHTML出力を調べます

生成されたHTML出力を調べて、適切なクライアントIDを見つける必要があります。ブラウザでページを開き、右クリックしてView Sourceを実行します。対象のJSFコンポーネントのHTML表現を見つけ、そのidをクライアントIDとして使用します。現在の命名コンテナに応じて、絶対的または相対的な方法で使用できます。次の章を参照してください。

注::0::1:などの反復インデックスが含まれている場合(反復コンポーネント内にあるため)、特定の反復ラウンドの更新が常にサポートされているわけではないことを認識する必要があります。詳細については、回答の最後をご覧ください。

NamingContainerコンポーネントを記憶し、常に固定IDを付与します

Ajax process/execute/update/renderで参照したいコンポーネントが同じ NamingContainer parent内にある場合、独自のIDを参照するだけです。

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

同じNamingContainer内にnotがある場合、絶対クライアントIDを使用して参照する必要があります。絶対クライアントIDは、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が生成されたクライアントに付加されますすべての子コンポーネントのID。固定IDがない場合、JSFはj_idXXX形式の自動生成IDを使用することに注意してください。それらに固定IDを与えることで、絶対に回避する必要があります。 OmniFaces NoAutoGeneratedIdViewHandler は、開発中にこれに役立つ場合があります。

問題のUIComponentのjavadocを見つけることがわかっている場合は、 NamingContainer インターフェースを実装しているかどうかを確認することもできます。たとえば、 HtmlForm<h:form>タグの後ろのUIComponent)はNamingContainerの実装を示していますが、 HtmlPanelGroup<h:panelGroup>タグの後ろのUIComponent)は表示されないため、表示されませんNamingContainerを実装します。 ここにすべての標準コンポーネントのjavadoc および ここにPrimeFacesのjavadoc があります。

問題を解決する

あなたの場合:

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

<h:panelGrid id="display">の生成されたHTML出力は次のようになります。

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

そのidをクライアントIDとして正確に受け取り、updateで使用するために:をプレフィックスとして付ける必要があります。

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

Include/tagfile/composite外部の参照

このコマンドリンクがinclude/tagfile内にあり、ターゲットがその外部にあるため、現在のネーミングコンテナのネーミングコンテナの親のIDが必ずしもわからない場合は、次のように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">

レンダリング/更新属性のテンプレートの親ネーミングコンテナのIDを取得 も参照してください

カバーの下でどのように機能しますか

これはすべて、 UIComponent#findComponent() javadoc "search expression"として指定されます。

検索式は、識別子(UIComponentのidプロパティと完全に一致する識別子、またはUINamingContainer#getSeparatorChar文字値でリンクされた一連の識別子で構成されます。検索アルゴリズムは次のように動作しますが、最終結果が同じである限り、代替のアルゴリズムを使用できます。

  • 次の条件のいずれかが満たされたらすぐに停止することにより、検索のベースとなるUIComponentを特定します。
    • 検索式が区切り文字(「絶対」検索式と呼ばれる)で始まる場合、ベースはコンポーネントツリーのルートUIComponentになります。先頭の区切り文字は削除され、検索式の残りの部分は、以下で説明するように「相対」検索式として扱われます。
    • それ以外の場合、このUIComponentNamingContainerである場合、それが基礎として機能します。
    • それ以外の場合は、このコンポーネントの親を検索します。 NamingContainerが検出されると、それがベースになります。
    • それ以外の場合(NamingContainerが見つからない場合)、ルートUIComponentがベースになります。
  • 検索式(前のステップで変更された可能性があります)は、ベースコンポーネントのスコープ内で、一致するIDを持つコンポーネント(ある場合)を見つけるために使用される「相対」検索式になりました。一致は次のように実行されます。
    • 検索式が単純な識別子の場合、この値はidプロパティと比較され、ベースUIComponentのファセットと子を再帰的に比較します(ただし、子孫NamingContainerが見つかった場合、そのファセットと子は検索されません)。
    • 検索式に区切り文字で区切られた複数の識別子が含まれる場合、最初の識別子を使用して、前の箇条書きの規則に従ってNamingContainerを見つけます。次に、このNamingContainerfindComponent()メソッドが呼び出され、検索式の残りを渡します。

PrimeFacesもJSF仕様に準拠していますが、RichFacesでは "some additional exceptions" を使用しています。

"reRender"は、UIComponent.findComponent()アルゴリズム(いくつかの追加の例外を含む)を使用して、コンポーネントツリー内のコンポーネントを見つけます。

これらの追加の例外は詳細に説明されていませんが、相対コンポーネントID(つまり、:で始まっていないもの)は、最も近い親NamingContainerのコンテキストだけでなく、同じビューの他のすべてのNamingContainerコンポーネントでも検索されることがわかっていますちなみに比較的高価な仕事です)。

prependId="false"を使用しないでください

それでもうまくいかない場合は、<h:form prependId="false">を使用していないか確認してください。これは、ajaxの送信とレンダリングの処理中に失敗します。この関連質問も参照してください: prependId = "false"のあるUIFormは<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バージョンではまだ機能しません。サポートは将来のバージョンで提供される可能性があります。一方、最善の方法は、反復コンポーネント自体、または<ui:repeat>のようなHTMLをレンダリングしない場合の親を更新することです。

PrimeFacesを使用するときは、検索式またはセレクターを検討してください

PrimeFaces Search Expressions を使用すると、JSFコンポーネントツリー検索式を介してコンポーネントを参照できます。 JSFにはいくつかのビルトインがあります:

  • @this:現在のコンポーネント
  • @form:親UIForm
  • @all:文書全体
  • @none:なし

PrimeFacesは、新しいキーワードと複合式のサポートによりこれを強化しました。

  • @parent:親コンポーネント
  • @namingcontainer:親UINamingContainer
  • @widgetVar(name):指定されたwidgetVarで識別されるコンポーネント

@form:@parent@this:@parent:@parentなどの複合式でこれらのキーワードを混在させることもできます。

PrimeFaces Selectors(PFS)@(.someclass)と同様に、jQuery CSSセレクター構文を介してコンポーネントを参照できます。例えば。 HTML出力ですべての共通スタイルクラスを持つコンポーネントを参照します。これは、「多くの」コンポーネントを参照する必要がある場合に特に役立ちます。これは、ターゲットコンポーネントがHTML出力にすべてのクライアントIDを持っていることのみを必要とします(固定または自動生成、重要ではありません)。 pdate = "@(。myClass)"のようなPrimeFaces Selectorsの仕組み も参照してください。

284
BalusC

まず第一に:私が知る限り、タブビュー内にダイアログを配置することは悪い習慣です...あなたはそれを取り除く方が良い...

そして今あなたの質問に:

申し訳ありませんが、実装したかったものを正確に取得するのに時間がかかりました。

自分のWebアプリで今やったのですが、うまくいきます

p:dialogを `p:tabViewの外側に配置する前に言ったように、

最初に提案したようにp:dialogを残します。

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

同じことが私のWebアプリでも機能しますが、うまく機能しない場合は、Java Beanコードに何か問題があると思います...

9
Daniel

update="insTable:display"update="display"に変更してみてください。そのようなフォームIDをidの前に付けることはできないと思います。

0
Mr.J4mes

これにはすでにBalusCによる素晴らしい回答がありますが、コンテナに正しいclientIdを伝えるために使用するちょっとしたトリックがありますです。

  1. 動作していないコンポーネントのアップデートを削除します
  2. 更新しようとしたコンポーネント内に偽の更新を含む一時的なコンポーネントを配置します
  3. ページをヒットすると、サーブレット例外エラーにより、参照する必要がある正しいクライアントIDが通知されます。
  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">

失敗する更新を使用して更新しようとしているIDのコンポーネント内にコンポーネントを追加します

   <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:tabs:insTable: BogusButtonから参照される式 "BogusUpdate"のコンポーネントが見つかりません

したがって、使用する正しいclientIdは、太字とターゲットコンテナーのID(この場合は表示)になります。

tabs:insTable:display
0
jeff