1. ホーム
  2. jsf

[解決済み] dataTableやui:repeatのコマンドリンクに選択した行を渡すにはどうしたらいいですか?

2022-11-30 17:55:09

質問

JSF 2のアプリケーションでPrimefacesを使っています。私は <p:dataTable> があり、行を選択する代わりに、ユーザが個々の行に対して様々なアクションを直接実行できるようにしたいのです。そのために、私はいくつかの <p:commandLink> を最後のカラムに追加しました。

私の問題点は、コマンドリンクによって開始されるアクションに行IDを渡して、どの行に対してアクションを行うかを知るにはどうしたらよいかということです。私は <f:attribute> :

<p:dataTable value="#{bean.items}" var="item">
    ...
    <p:column>
        <p:commandLink actionListener="#{bean.insert}" value="insert">
            <f:attribute name="id" value="#{item.id}" />
        </p:commandLink>
    </p:column>
</p:dataTable>

しかし、常に 0 を返します。どうやら、行変数 f は、属性がレンダリングされるときに利用できないようです (固定値を使用するときは動作します)。

どなたか代替の解決策をお持ちですか?

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

原因としては <f:attribute> は、反復された行 (ビューのレンダリング時に移入される) ではなく、コンポーネント自体 (ビューの構築時に移入される) に固有のものです。

要件を達成するためにいくつかの方法があります。

  1. もしあなたのservletcontainerが最低でもServlet 3.0 / EL 2.2をサポートしているならば、単にそれを UICommand コンポーネントか AjaxBehavior タグを使用することができます。例

     <h:commandLink action="#{bean.insert(item.id)}" value="insert" />
    
    

    との組み合わせで。

     public void insert(Long id) {
         // ...
     }
    
    

    これは、フォーム送信リクエストのためにデータモデルを保持することだけを必要とします。最良の方法は、ビュースコープに Bean を @ViewScoped .

    アイテムオブジェクト全体を渡すこともできます。

     <h:commandLink action="#{bean.insert(item)}" value="insert" />
    
    

    を使っています。

     public void insert(Item item) {
         // ...
     }
    
    

    Servlet 2.5コンテナでは、JBoss ELのようにこれをサポートするEL実装を提供する場合にも、これは可能です。設定の詳細については この回答 .


  2. 使用方法 <f:param> UICommand コンポーネントの中に これはリクエスト・パラメータを追加します。

     <h:commandLink action="#{bean.insert}" value="insert">
         <f:param name="id" value="#{item.id}" />
     </h:commandLink>
    
    

    Beanがリクエストスコープされている場合、JSFはそれを @ManagedProperty

     @ManagedProperty(value="#{param.id}")
     private Long id; // +setter
    
    

    また、Bean の範囲が広い場合や、より細かい検証や変換が必要な場合は <f:viewParam> をターゲットビュー上で使用することもできます。 f:viewParam vs @ManagedPropertyを参照して下さい。 :

     <f:viewParam name="id" value="#{bean.id}" required="true" />
    
    

    いずれにせよ、これはデータモデルが必ずしもフォーム送信のために保存される必要がないという利点があります(Beanがリクエストスコープである場合のために)。


  3. 使用方法 <f:setPropertyActionListener> UICommand コンポーネントを使用することができます。利点は、Beanがリクエストスコープよりも広い範囲を持つ場合に、 リクエストパラメータマップにアクセスする必要性を排除できることです。

     <h:commandLink action="#{bean.insert}" value="insert">
         <f:setPropertyActionListener target="#{bean.id}" value="#{item.id}" />
     </h:commandLink>
    
    

    との組み合わせで

     private Long id; // +setter
    
    

    プロパティで利用できるようになるだけです id をアクションメソッドで使用できるようになります。これはデータモデルがフォーム送信リクエストのために保存されることを必要とするだけです。最良の方法は、ビーンをビューのスコープに @ViewScoped .


  4. データテーブルの値を DataModel<E> にバインドし、その代わりにアイテムをラップします。

     <h:dataTable value="#{bean.model}" var="item">
    
    

     private transient DataModel<Item> model;
    
     public DataModel<Item> getModel() {
         if (model == null) {
             model = new ListDataModel<Item>(items);
         }
         return model;
     }
    
    

    (それを transient を作成し、ゲッターで遅延的にインスタンス化することは、ビューまたはセッションスコープのBeanでこれを使用している場合、必須です。 DataModel を実装していないためです。 Serializable )

    そうすると、現在の行にアクセスするために DataModel#getRowData() によって、何も渡すことなく(JSFは、クリックされたコマンドリンク/ボタンのリクエストパラメータ名に基づいて行を決定します)、現在の行にアクセスすることができます。

     public void insert() {
         Item item = model.getRowData();
         Long id = item.getId();
         // ...
     }
    
    

    これはまた、データモデルがフォーム送信リクエストのために保存されることを必要とします。最良の方法は、ビュースコープに Bean を @ViewScoped .


  5. 使用方法 Application#evaluateExpressionGet() を使用して、プログラムによって現在の #{item} .

     public void insert() {
         FacesContext context = FacesContext.getCurrentInstance();
         Item item = context.getApplication().evaluateExpressionGet(context, "#{item}", Item.class);
         Long id = item.getId();
         // ...
     }
    
    

どちらの方法を選択するかは、機能的な要件と、他の目的に対してどちらか一方がより多くの利点を提供するかどうかによって決まります。私個人としては、#1か、サーブレット2.5コンテナもサポートしたい場合は、#2を選択します。