1. ホーム
  2. ajax

[解決済み] ajax更新/レンダーのためのコンポーネントのクライアントIDを見つける方法?bar "から "foo "を参照する式でコンポーネントを見つけることができません。

2022-04-29 19:54:52

質問

以下のコードは、PrimeFaces DataGrid + DataTable Tutorialsからインスピレーションを受け、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" のコンテキスト内でそれを見つけることができません。

別のAjaxポストバック中に発生し、JSFプロジェクトステージが Development というJavaScriptのアラートが表示され、失敗します。 メッセージ :

malformedXMLです。更新中:insTable:display が見つかりません。

この現象はどのように発生し、どのように解決すればよいのでしょうか?

どのように解決するのですか?

HTML出力で実際のクライアントIDを確認する

正しいクライアントIDを見つけるには、生成されたHTML出力を見る必要があります。ブラウザでページを開き、右クリックをして ソースを見る . 目的の JSF コンポーネントの HTML 表現を探し、その id をクライアントIDとして使用します。現在のネーミングコンテナに応じて、絶対的または相対的な方法で使用することができます。次の章を参照してください。

注意: もし、その中に :0: , :1: など(反復処理コンポーネント内にあるため)、特定の反復ラウンドの更新が常にサポートされているわけではないことを認識する必要があります。その詳細については、回答の最後を参照してください。

記憶する NamingContainer コンポーネントは常に固定されたIDを付与してください。

ajax処理/実行/更新/レンダリングで参照したいコンポーネントが同じ NamingContainer の親は、それ自身のIDを参照すればよい。

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

もし、それが ではない の中に、同じ NamingContainer の場合、絶対クライアント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は自動生成されたIDを j_idXXX 形式を使用します。固定IDを与えることによって、絶対にそれを避けるべきです。その オムニフェーズ NoAutoGeneratedIdViewHandler は、開発中にこれを参考にすることができるかもしれません。

のjavadocを見つけることが分かっている場合。 UIComponent を実装しているかどうかを確認することができます。 NamingContainer インターフェイスを持つかどうか。例えば HtmlForm (その UIComponent 背後 <h:form> タグ) を実装していることがわかります。 NamingContainer を指定する必要がありますが HtmlPanelGroup (その UIComponent 背後 <h:panelGroup> タグ)は表示されないので、実装しない。 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">

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

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

参照 render / update 属性で、テンプレート内の親ネームコンテナの id を取得する。

裏側の仕組み

これはすべて "検索式" その UIComponent#findComponent() ジャバドック :

A 検索式 は、識別子 (この識別子は、id プロパティと正確にマッチします。 UIComponent によってリンクされた一連の識別子、または UINamingContainer#getSeparatorChar の文字値を指定します。検索アルゴリズムは以下の通りであるが、最終結果が同じであれば、他のアルゴリズムを用いてもよい。

  • を識別します。 UIComponent を、以下のいずれかの条件を満たした時点で停止し、検索のベースとする。
    • 検索式が区切り文字で始まる場合("absolute"検索式と呼ばれる)、ベースはルート UIComponent コンポーネントツリーの 先頭のセパレータ文字は削除され、残りの検索式は以下のように"相対検索式として扱われます。
    • そうでない場合は、この UIComponentNamingContainer が基本になります。
    • そうでなければ、このコンポーネントの親を検索してください。もし NamingContainer に遭遇した場合、それがベースとなる。
    • それ以外(もし NamingContainer に遭遇した場合、ルート UIComponent がベースとなります。
  • 検索式(前のステップで変更された可能性があります)は、ベースコンポーネントの範囲内で、一致するIDを持つコンポーネント(もしあれば)を見つけるために使用される、"相対的" 検索式になりました。マッチは次のように行われます。
    • 検索式が単純な識別子の場合、この値は id プロパティと比較され、その後、ファセットおよびベースの子プロパティを再帰的に検索します。 UIComponent (ただし、子孫の NamingContainer が見つかった場合、それ自身のファセットと子は検索されない)。
    • 検索式がセパレータ文字で区切られた複数の識別子を含む場合、最初の識別子を用いて NamingContainer は、前の箇条書きにある規則によって そして、その findComponent() メソッドで、この NamingContainer が呼び出され、検索式の残りが渡されます。

PrimeFacesもJSFの仕様に準拠していますが、RichFacesでは "いくつかの追加の例外"。 .

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

これらの追加的な例外はどこにも詳しく説明されていませんが、相対的なコンポーネントID(つまり、そのIDが : のコンテキストで検索されるだけでなく、最も近い親である NamingContainer のみならず、他のすべての NamingContainer コンポーネントを作成します (ちなみにこれは比較的コストのかかる作業です)。

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

これでもまだうまくいかない場合は、次の手順で <h:form prependId="false"> . これは、ajax送信とレンダリングの処理中に失敗します。この関連する質問も参照してください。 UIForm with prependId="false" breaks <f:ajax render>.UIForm with 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の検索式 を使用すると、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出力においてすべてのクライアントID(固定または自動生成、問題なし)を持っていることのみを前提条件とします。以下も参照してください。 update="@(.myClass)" のようなPrimeFacesセレクタはどのように機能するのですか?