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>
は(ビューのビルド時に表示される)コンポーネント自体に固有であり、ビューのレンダリング時に表示される反復行ではありません。
要件を達成するにはいくつかの方法があります。
代わりに <f:param>
を使用してください。要求パラメーターを追加します。
<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がスコープの要求である場合)。
代わりに <f:setPropertyActionListener>
を使用してください。利点は、Beanのスコープがリクエストスコープよりも広い場合に、リクエストパラメータマップにアクセスする必要がなくなることです。
<h:commandLink action="#{bean.insert}" value="insert">
<f:setPropertyActionListener target="#{bean.id}" value="#{item.id}" />
</h:commandLink>
と組み合わせて
private Long id; // +setter
アクションメソッドのプロパティid
で使用できるようになります。これには、フォーム送信要求のためにデータモデルが保存されることのみが必要です。最善の方法は、@ViewScoped
によってBeanをビュースコープに配置することです。
ServletContainerがServlet 3.0/EL 2.2をサポートしている場合、メソッド引数として渡すだけです。また、これには、フォーム送信要求のためにデータモデルが保持される必要があります。最善の方法は、@ViewScoped
によってBeanをビュースコープに配置することです。
<h:commandLink action="#{bean.insert(item.id)}" value="insert" />
と組み合わせて:
public void insert(Long id) {
// ...
}
アイテムオブジェクト全体を渡すこともできます。
<h:commandLink action="#{bean.insert(item)}" value="insert" />
で:
public void insert(Item item) {
// ...
}
Servlet 2.5コンテナでは、これをサポートするEL実装(JBoss ELなど)を提供する場合にも可能です。構成の詳細については、 この回答 を参照してください。
データテーブル値を 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
がDataModel
を実装していないため、ビューまたはセッションスコープのBeanでこれを使用する場合は、Serializable
にし、getterで遅延インスタンス化することが必須です)
その後、 DataModel#getRowData()
で現在の行にアクセスできるようになります(JSFは、クリックされたコマンドリンク/ボタンのリクエストパラメーター名に基づいて行を決定します)。
public void insert() {
Item item = model.getRowData();
Long id = item.getId();
// ...
}
また、これには、フォーム送信要求のためにデータモデルが保持される必要があります。最善の方法は、@ViewScoped
によってBeanをビュースコープに配置することです。
Application#evaluateExpressionGet()
を使用して、現在の#{item}
をプログラムで評価できます。
public void insert() {
FacesContext context = FacesContext.getCurrentInstance();
Item item = context.getApplication().evaluateExpressionGet(context, "#{item}", Item.class);
Long id = item.getId();
// ...
}
どの方法を選択するかは、機能要件と、どちらが他の目的に対してより多くの利点を提供するかによって異なります。個人的には、#3を使用するか、サーブレット2.5コンテナーもサポートしたい場合は、#2を使用します。
JSF 1.2では、これは<f:setPropertyActionListener>
(コマンドコンポーネント内)によって行われました。 JSF 2.0(正確にはEL 2.2、BalusCのおかげ)では、次のようにすることができます:action="${filterList.insert(f.id)}
私の表示ページで:
<p:dataTable ...>
<p:column>
<p:commandLink actionListener="#{inquirySOController.viewDetail}"
process="@this" update=":mainform:dialog_content"
oncomplete="dlg2.show()">
<h:graphicImage library="images" name="view.png"/>
<f:param name="trxNo" value="#{item.map['trxNo']}"/>
</p:commandLink>
</p:column>
</p:dataTable>
バッキングビーン
public void viewDetail(ActionEvent e) {
String trxNo = getFacesContext().getRequestParameterMap().get("trxNo");
for (DTO item : list) {
if (item.get("trxNo").toString().equals(trxNo)) {
System.out.println(trxNo);
setSelectedItem(item);
break;
}
}
}