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


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

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

私が同じことをしようとすると<f:ajax>、基本的に同じことを伝える別のメッセージで失敗します:

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

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

回答:


313

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

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

注:それはのような反復指数含まれて発生した場合:0::1:などを、(それが反復コンポーネント内だから)、あなたは、特定の反復ラウンドの更新は常にサポートされていないことを認識する必要があります。詳細については、回答の下部を参照してください。

NamingContainerコンポーネントを記憶し、常に固定IDを与える

ajax process / execute / update / renderで参照するコンポーネントが同じ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を与えることで、絶対にそれを回避する必要があります。OmniFacesは、NoAutoGeneratedIdViewHandler開発中に、この中に有用であろう。

UIComponent問題のJavadocを見つけることがわかっている場合は、NamingContainerインターフェースが実装されているかどうかをチェックインすることもできます。たとえば、HtmlFormUIComponent後ろの<h:form>タグ)はそれを実装していることを示していますNamingContainerが、HtmlPanelGroupUIComponent後ろの<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を取得するもご覧ください。

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

これは、すべてのように指定された「検索式」のJavadocUIComponent#findComponent()

検索式は、のidプロパティに対して正確に一致している識別子(いずれかで構成されUIComponent、またはによって連結され、そのような識別子の一連のUINamingContainer#getSeparatorChar文字値。以下のように代替alogrithmsが長いほど使用されてもよい探索アルゴリズムは、動作しなければなりません最終結果は同じです:

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

PrimeFacesもJSF仕様に準拠していますが、RichFacesは「いくつかの追加の例外」を使用しています。

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

これらの追加の例外はどこにも詳しく説明されていませんが、相対コンポーネントID(つまり、で始まらないもの:)は、最も近いparentのコンテキストで検索されるだけでなく、同じビュー内の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バージョンではまだ機能しません。サポートは将来のバージョンで提供される可能性があります。その間、あなたの最善の策は、反復コンポーネント自体、またはのように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 Selectors(PFS)では、@(.someclass)jQuery CSSセレクター構文を介してコンポーネントを参照できます。たとえば、HTML出力にすべての共通のスタイルクラスを持つ参照コンポーネント。これは、「多くの」コンポーネントを参照する必要がある場合に特に役立ちます。これは、ターゲットコンポーネントにすべてのクライアントIDがHTML出力に含まれていることを前提としています(固定または自動生成のどちらでも構いません)。「PrimeFaces Selectors as update = "@(。myClass)"どのように機能するか」も参照してください


@jack:ちょうどはJavadocを読む:docs.oracle.com/javaee/6/api/javax/faces/component/... JSF 2.0以降では、それは、定数の代わりに設定可能となっています。
BalusC 2013年

SEPARATOR_CHARは非推奨ではありませんか?たとえば、ネストされたコンポーネントを呼び出す方法の例を教えてcontext.getViewRoot().findComponent(":inputform" + UINamingContainer.getSeparatorChar(context) + "inputtext" );ください。また、xhtmlコードも含めてください。
jacktrades

1
ありがとう、prependId="false"私の日を保存して、フォーム内でajaxの再レンダリングに失敗したことに注意してください。
Gaim 2013

説明で概説されているクライアントIDの正確な意味は何ですか?JSF->「このコンポーネントのクライアント側の識別子」と同じですか。よろしくお願いいたします。
スティーブOh

1
@ antonu17:回答で述べたように、Mojarraのf:ajaxでのみサポートされています。
BalusC、2015

9

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

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

すみません、あなたが実装したいものを正確に取得するのに少し時間がかかりました、

たった今、自分の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コードに問題があると思います...


私が回答に書き込んだ他の変更を試してみることをお勧めします(バインディングとfaces-configなどで...)これは、「情報:componeが見つかりません...」を解決することを想定しています
Daniel

私はもう一度2番目の提案を実装しようとしましたが、それでも機能しません。ダイアログが開きますが、選択したアイテムのデータは含まれていません。ログに「IDが "j_idt31"のコンポーネントがビューに見つかりません」と表示され、これ以上デバッグできません。
perissf 2011

5

これは、タブも名前付けコンテナーであるためです。更新は次のようになりupdate="Search:insTable:display"ます。ダイアログをフォームの外側に配置し、タブの内側に配置するだけでも、次のようになります。update="Search:display"


0

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


2
非常に古いが誤解を招く答え。上記のBalusCの投稿を参照して、コンポーネントのIDの前にフォームのIDが前に付いていることを明確に示しています。<h:form id = "form"> <p:commandLink update = ":otherform:result"> <!-OK!-> </ h:form> <h:form id = "otherform"> <h:panelGroup id = "result" /> </ h:form>
J Slick

0

私はこれがすでに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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.