Bagaimana cara mengirimkan baris yang dipilih ke commandLink di dalam dataTable atau ui: ulangi?

99

Saya menggunakan Primefaces dalam aplikasi JSF 2. Saya memiliki <p:dataTable>, dan alih-alih memilih baris, saya ingin pengguna dapat langsung menjalankan berbagai tindakan pada baris individual. Untuk itu, saya punya beberapa <p:commandLink>s di kolom terakhir.

Masalah saya: bagaimana saya bisa meneruskan ID baris ke tindakan yang dimulai oleh tautan perintah sehingga saya tahu baris mana yang harus ditindaklanjuti? Saya mencoba menggunakan <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>

Tetapi selalu menghasilkan 0 - tampaknya variabel baris ftidak tersedia ketika atribut diberikan (ini berfungsi ketika saya menggunakan nilai tetap).

Ada yang punya solusi alternatif?

Michael Borgwardt
sumber

Jawaban:

215

Mengenai penyebabnya, <f:attribute>ini khusus untuk komponen itu sendiri (diisi selama waktu pembuatan tampilan), bukan untuk baris yang diiterasi (diisi selama waktu perenderan tampilan).

Ada beberapa cara untuk mencapai kebutuhan tersebut.

  1. Jika servletcontainer Anda mendukung minimal Servlet 3.0 / EL 2.2, maka teruskan saja sebagai argumen metode aksi / pendengar dari UICommand komponen atau AjaxBehaviortag. Misalnya

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

    Dikombinasikan dengan:

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

    Ini hanya mengharuskan model data dipertahankan untuk permintaan pengiriman formulir. Yang terbaik adalah meletakkan kacang dalam lingkup tampilan dengan @ViewScoped.

    Anda bahkan dapat meneruskan seluruh objek item:

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

    dengan:

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

    Pada kontainer Servlet 2.5, ini juga dimungkinkan jika Anda menyediakan implementasi EL yang mendukung ini, seperti JBoss EL. Untuk detail konfigurasi, lihat jawaban ini .


  2. Gunakan <f:param>dalam UICommandkomponen. Ini menambahkan parameter permintaan.

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

    Jika bean Anda adalah request scoped, biarkan JSF mengaturnya @ManagedProperty

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

    Atau jika bean Anda memiliki cakupan yang lebih luas atau jika Anda menginginkan validasi / konversi yang lebih terperinci , gunakan <f:viewParam>pada tampilan target, lihat juga f: viewParam vs @ManagedProperty :

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

    Bagaimanapun, ini memiliki keuntungan bahwa model data tidak perlu dipertahankan untuk pengiriman formulir (untuk kasus kacang Anda adalah cakupan permintaan).


  3. Gunakan <f:setPropertyActionListener>dalam UICommandkomponen. Keuntungannya adalah ini menghilangkan kebutuhan untuk mengakses peta parameter permintaan ketika kacang memiliki cakupan yang lebih luas daripada ruang lingkup permintaan.

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

    Dikombinasikan dengan

     private Long id; // +setter

    Ini hanya akan tersedia dengan idmetode properti dalam tindakan. Ini hanya mengharuskan model data dipertahankan untuk permintaan pengiriman formulir. Yang terbaik adalah meletakkan kacang dalam lingkup tampilan dengan @ViewScoped.


  4. Ikat nilai datatable ke nilai DataModel<E>yang akan membungkus item.

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

    dengan

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

    (membuatnya transientdan secara malas membuat instance di getter adalah wajib saat Anda menggunakan ini pada kacang cakupan tampilan atau sesi karena DataModeltidak diimplementasikan Serializable)

    Kemudian Anda akan dapat mengakses baris saat ini DataModel#getRowData()tanpa melewatkan apa pun (JSF menentukan baris tersebut berdasarkan nama parameter permintaan dari tautan / tombol perintah yang diklik).

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

    Ini juga mensyaratkan bahwa model data dipertahankan untuk permintaan pengiriman formulir. Yang terbaik adalah meletakkan kacang dalam lingkup tampilan dengan @ViewScoped.


  5. Gunakan Application#evaluateExpressionGet()untuk mengevaluasi arus secara terprogram #{item}.

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

Cara mana yang dipilih tergantung pada persyaratan fungsional dan apakah yang satu atau yang lain menawarkan lebih banyak keuntungan untuk tujuan lain. Saya pribadi akan melanjutkan dengan # 1 atau, ketika Anda ingin mendukung kontainer servlet 2.5 juga, dengan # 2.

BalusC
sumber
1
+1, meskipun preferensi saya adalah # 2 (jika harus mendukung 2.5).
Bozho
Terima kasih atas jawaban lengkapnya. Sayangnya, saya harus melaporkan bahwa # 1 adalah satu-satunya hal yang bekerja dalam datatable primefaces yang difilter (yang persis seperti skenario yang saya butuhkan). Semua yang lain hanya mengerjakan meja tanpa filter. Saya melihat ini lebih sebagai bug di primefaces daripada di jawaban Anda.
Michael Borgwardt
Apakah permintaan kacang atau tampilan dibatasi?
BalusC
2
Dengan "difilter" yang Anda maksud seperti dalam contoh etalase ini ? Gejala menunjukkan bahwa tindakan filter hanya berlangsung di sisi klien dan model di sisi server tidak dipertahankan. Tidak yakin apakah ini disengaja. Anda selalu dapat meninggalkan laporan masalah.
BalusC
Posting Anda ada di antara postingan paling berguna yang pernah saya baca. Saya menggunakan metode 5 karena saya terpaksa menggunakan servlet 2.5. Pertanyaan saya sekarang adalah apakah mungkin mengirim parameter dengan commandLink (seperti dalam contoh Anda) tetapi menggunakan ajax?
Aditzu
11

Dalam JSF 1.2 ini dilakukan oleh <f:setPropertyActionListener>(dalam komponen perintah). Di JSF 2.0 (tepatnya EL 2.2, berkat BalusC) dimungkinkan untuk melakukannya seperti ini:action="${filterList.insert(f.id)}

Bozho
sumber
6
Fitur ini tidak khusus untuk JSF 2.0 (yang dengan sendirinya dapat berjalan di kontainer Servlet 2.5), tetapi untuk EL 2.2 (yang merupakan bagian dari Servlet 3.0).
BalusC
11

Di halaman tampilan saya:

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

kacang pendukung

 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;
        }
    }
}
Arfan J
sumber
-1

Berkat situs ini oleh Mkyong , satu-satunya solusi yang benar - benar berhasil bagi kami untuk memberikan parameter adalah ini

<h:commandLink action="#{user.editAction}">
    <f:param name="myId" value="#{param.id}" />
</h:commandLink>

dengan

public String editAction() {

  Map<String,String> params = 
            FacesContext.getExternalContext().getRequestParameterMap();
  String idString = params.get("myId");
  long id = Long.parseLong(idString);
  ...
}

Secara teknis, Anda tidak bisa meneruskan ke metode itu sendiri secara langsung, tetapi ke JSF request parameter map.

EpicPandaForce
sumber
1
Anda memiliki masalah yang berbeda dari yang ditanyakan di sini. Anda ingin mempertahankan parameter permintaan dari #{param}map untuk permintaan berikutnya, bukan meneruskan parameter arbitrer. T&J Anda dicakup oleh stackoverflow.com/questions/17734230
BalusC