Perbedaan antara tindakan dan daftar tindakan

393

Apa perbedaan antara actiondan actionListener, dan kapan saya harus menggunakan actionversus actionListener?

Murat Güzel
sumber

Jawaban:

583

actionListener

Gunakan actionListenerjika Anda ingin memiliki pengait sebelum tindakan bisnis nyata dijalankan, misalnya untuk mencatatnya, dan / atau untuk mengatur properti tambahan (oleh <f:setPropertyActionListener>), dan / atau untuk memiliki akses ke komponen yang meminta tindakan (yang tersedia oleh ActionEventargumen). Jadi, murni untuk menyiapkan tujuan sebelum aksi bisnis nyata dipanggil.

The actionListenermetode memiliki secara default tanda tangan berikut:

import javax.faces.event.ActionEvent;
// ...

public void actionListener(ActionEvent event) {
    // ...
}

Dan itu seharusnya dinyatakan sebagai berikut, tanpa metode kurung:

<h:commandXxx ... actionListener="#{bean.actionListener}" />

Perhatikan bahwa Anda tidak dapat melewati argumen tambahan oleh EL 2.2. Namun Anda dapat mengesampingkan ActionEventargumen secara keseluruhan dengan meneruskan dan menentukan argumen khusus. Contoh-contoh berikut ini valid:

<h:commandXxx ... actionListener="#{bean.methodWithoutArguments()}" />
<h:commandXxx ... actionListener="#{bean.methodWithOneArgument(arg1)}" />
<h:commandXxx ... actionListener="#{bean.methodWithTwoArguments(arg1, arg2)}" />
public void methodWithoutArguments() {}
public void methodWithOneArgument(Object arg1) {}
public void methodWithTwoArguments(Object arg1, Object arg2) {}

Perhatikan pentingnya tanda kurung dalam ekspresi metode tanpa argumen. Jika mereka tidak ada, JSF masih akan mengharapkan metode dengan ActionEventargumen.

Jika Anda menggunakan EL 2.2+, maka Anda dapat mendeklarasikan beberapa metode pendengar tindakan melalui <f:actionListener binding>.

<h:commandXxx ... actionListener="#{bean.actionListener1}">
    <f:actionListener binding="#{bean.actionListener2()}" />
    <f:actionListener binding="#{bean.actionListener3()}" />
</h:commandXxx>
public void actionListener1(ActionEvent event) {}
public void actionListener2() {}
public void actionListener3() {}

Perhatikan pentingnya tanda kurung dalam bindingatribut. Jika mereka tidak ada, EL akan membingungkan melemparkan javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean, karena bindingatribut secara default diartikan sebagai ekspresi nilai, bukan sebagai ekspresi metode. Menambahkan tanda kurung gaya EL 2.2+ secara transparan mengubah ekspresi nilai menjadi ekspresi metode. Lihat juga ao. Mengapa saya bisa mengikat <f: actionListener> ke metode arbitrer jika tidak didukung oleh JSF?


tindakan

Gunakan actionjika Anda ingin menjalankan aksi bisnis dan jika perlu menangani navigasi. The actionMetode dapat (dengan demikian, tidak harus) mengembalikan Stringyang akan digunakan sebagai kasus navigasi hasil (tampilan target). Nilai pengembalian nullatau voidakan membiarkannya kembali ke halaman yang sama dan menjaga lingkup tampilan saat ini tetap hidup. Nilai kembali dari string kosong atau ID tampilan yang sama juga akan kembali ke halaman yang sama, tetapi membuat ulang ruang lingkup tampilan dan dengan demikian menghancurkan setiap scoped bean yang saat ini aktif dilihat dan, jika ada, membuatnya kembali.

The actionMetode dapat berupa valid MethodExpression, juga orang-orang yang menggunakan EL 2.2 argumen seperti di bawah ini:

<h:commandXxx value="submit" action="#{bean.edit(item)}" />

Dengan metode ini:

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

Perhatikan bahwa ketika metode tindakan Anda hanya mengembalikan string, maka Anda juga bisa menentukan string tersebut di actionatribut. Jadi, ini benar-benar canggung:

<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />

Dengan metode tidak masuk akal ini mengembalikan string yang dikodekan:

public String goToNextpage() {
    return "nextpage";
}

Sebagai gantinya, cukup masukkan string hardcoded secara langsung di atribut:

<h:commandLink value="Go to next page" action="nextpage" />

Harap perhatikan bahwa ini pada gilirannya menunjukkan desain yang buruk: menavigasi dengan POST. Ini bukan pengguna atau SEO friendly. Ini semua dijelaskan di Kapan saya harus menggunakan h: outputLink bukan h: commandLink? dan seharusnya diselesaikan sebagai

<h:link value="Go to next page" outcome="nextpage" />

Lihat juga Bagaimana cara menavigasi di JSF? Cara membuat URL mencerminkan halaman saat ini (dan bukan yang sebelumnya) .


f: pendengar ajax

Karena JSF 2.x ada cara ketiga, the <f:ajax listener>.

<h:commandXxx ...>
    <f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>

The ajaxListenermetode memiliki secara default tanda tangan berikut:

import javax.faces.event.AjaxBehaviorEvent;
// ...

public void ajaxListener(AjaxBehaviorEvent event) {
    // ...
}

Di Mojarra, AjaxBehaviorEventargumennya opsional, di bawah ini berfungsi dengan baik.

public void ajaxListener() {
    // ...
}

Tapi di MyFaces, itu akan melempar MethodNotFoundException. Di bawah ini berfungsi di kedua implementasi JSF ketika Anda ingin menghilangkan argumen.

<h:commandXxx ...>
    <f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>

Pendengar Ajax tidak benar-benar berguna pada komponen perintah. Mereka lebih berguna pada input dan pilih komponen <h:inputXxx>/ <h:selectXxx>. Dalam komponen perintah, tetap berpegang pada actiondan / atau actionListeneruntuk kejelasan dan kode pendokumentasian diri yang lebih baik. Selain itu, seperti actionListener, yang f:ajax listenertidak mendukung mengembalikan hasil navigasi.

<h:commandXxx ... action="#{bean.action}">
    <f:ajax execute="@form" render="@form" />
</h:commandXxx>

Untuk penjelasan executedan renderatribut, pergilah ke Memahami proses / pembaruan PrimeFaces dan JSF f: ajax execute / render atribut .


Pesanan doa

Itu actionListener s selalu dipanggil sebelum itu actiondalam urutan yang sama seperti yang dinyatakan dalam tampilan dan melekat pada komponen. Itu f:ajax listenerselalu dipanggil sebelum pendengar tindakan. Jadi, contoh berikut:

<h:commandButton value="submit" actionListener="#{bean.actionListener}" action="#{bean.action}">
    <f:actionListener type="com.example.ActionListenerType" />
    <f:actionListener binding="#{bean.actionListenerBinding()}" />
    <f:setPropertyActionListener target="#{bean.property}" value="some" />
    <f:ajax listener="#{bean.ajaxListener}" />
</h:commandButton>

Akan memohon metode dalam urutan berikut:

  1. Bean#ajaxListener()
  2. Bean#actionListener()
  3. ActionListenerType#processAction()
  4. Bean#actionListenerBinding()
  5. Bean#setProperty()
  6. Bean#action()

Penanganan pengecualian

Itu actionListener mendukung pengecualian khusus: AbortProcessingException. Jika pengecualian ini dilemparkan dari suatu actionListenermetode, maka JSF akan melewatkan pendengar tindakan yang tersisa dan metode tindakan dan melanjutkan untuk memberikan tanggapan secara langsung. Anda tidak akan melihat halaman kesalahan / pengecualian, namun JSF akan mencatatnya. Ini juga secara implisit akan dilakukan setiap kali ada pengecualian lain yang dilemparkan dari actionListener. Jadi, jika Anda bermaksud untuk memblokir halaman dengan halaman kesalahan sebagai akibat dari pengecualian bisnis, maka Anda pasti harus melakukan pekerjaan dalam actionmetode tersebut.

Jika satu-satunya alasan untuk menggunakan actionListeneradalah untuk memilikivoid metode untuk kembali ke halaman yang sama, maka itu adalah metode yang buruk. actionMetode - metode ini juga dapat kembali dengan sempurna void, sebaliknya apa yang Anda yakini oleh beberapa IDE melalui validasi EL. Perhatikan bahwa contoh showcase PrimeFaces dikotori dengan jenis ini actionListenerdi semua tempat. Ini memang salah. Jangan gunakan ini sebagai alasan untuk melakukannya sendiri.

Namun dalam permintaan ajax, penangan pengecualian khusus diperlukan. Ini terlepas dari apakah Anda menggunakan listeneratribut <f:ajax>atau tidak. Untuk penjelasan dan contoh, buka penanganan Pengecualian dalam permintaan ajax JSF .

BalusC
sumber
1
Anda benar bahwa pengecualian dalam actionListeners ditelan secara default, tetapi di JSF 2.0 perilaku ini dapat diubah. Lihat jawaban saya di bawah untuk detailnya.
Arjan Tijms
3
@arjan: Anda benar bahwa JSF 2.0 memungkinkan Anda untuk mengubah penanganan default pengecualian yang dilakukan actionListener, tetapi itu masih tidak menjadikannya alasan yang baik untuk penyalahgunaan actionListeneruntuk tindakan bisnis .
BalusC
1
Memang, tindakan bisnis berada dalam "aliran" utama dari siklus permintaan / respons dan hanya actionsesuai dengan itu. actionListeneradalah untuk hal-hal sekunder. Hanya ingin mengklarifikasi bahwa pengecualian dari actionListeners dapat diperbanyak jika diperlukan;)
Arjan Tijms
2
@ Kohy: nama metode bebas untuk dipilih saat digunakan dalam actionListeneratribut dan itu harus publicjuga. The processActionNama hanya wajib ketika Anda menggunakan <f:actionListener type>, hanya karena jenis harus mengimplementasikan ActionListenerantarmuka yang memiliki tepat bahwa nama metode processActiondefinied.
BalusC
2
@Muhammed: pendengar tindakan ajax dipanggil sebelum semua pendengar tindakan biasa. Perhatikan bahwa bahkan ketika menggunakan <f:ajax>, Anda dalam kasus komponen perintah lebih suka menggunakan actionatribut untuk tindakan bisnis. Misalnya <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>.
BalusC
47

Seperti yang ditunjukkan BalusC, actionListenersecara default menelan pengecualian, tetapi di JSF 2.0 ada sedikit lebih dari ini. Yaitu, itu tidak hanya menelan dan mencatat, tetapi sebenarnya menerbitkan pengecualian.

Ini terjadi melalui panggilan seperti ini:

context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,                                                          
    new ExceptionQueuedEventContext(context, exception, source, phaseId)
);

Pendengar default untuk acara ini adalah ExceptionHandleryang diatur untuk Mojarracom.sun.faces.context.ExceptionHandlerImpl . Implementasi ini pada dasarnya akan memikirkan kembali pengecualian apa pun, kecuali jika menyangkut AbortProcessingException, yang dicatat. ActionListeners membungkus pengecualian yang dilemparkan oleh kode klien sedemikian AbortProcessingException yang menjelaskan mengapa ini selalu dicatat.

Namun ini ExceptionHandlerbisa diganti di wajah-config.xml dengan implementasi khusus:

<exception-handlerfactory>
   com.foo.myExceptionHandler
</exception-handlerfactory>

Alih-alih mendengarkan secara global, satu kacang juga dapat mendengarkan acara ini. Berikut ini adalah bukti konsep ini:

@ManagedBean
@RequestScoped
public class MyBean {

    public void actionMethod(ActionEvent event) {

        FacesContext.getCurrentInstance().getApplication().subscribeToEvent(ExceptionQueuedEvent.class, new SystemEventListener() {

        @Override
        public void processEvent(SystemEvent event) throws AbortProcessingException {
            ExceptionQueuedEventContext content = (ExceptionQueuedEventContext)event.getSource();
            throw new RuntimeException(content.getException());
        }

        @Override
        public boolean isListenerForSource(Object source) {
            return true;
        }
        });

        throw new RuntimeException("test");
    }

}

(perhatikan, ini bukan bagaimana seharusnya kode pendengar, ini hanya untuk tujuan demonstrasi!)

Memanggil ini dari Facelet seperti ini:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">
    <h:body>
        <h:form>
            <h:commandButton value="test" actionListener="#{myBean.actionMethod}"/>
        </h:form>
    </h:body>
</html>

Akan menghasilkan halaman kesalahan yang ditampilkan.

Tijms Arjan
sumber
43

ActionListener dipecat terlebih dahulu, dengan opsi untuk mengubah respons, sebelum Aksi dipanggil dan menentukan lokasi halaman berikutnya.

Jika Anda memiliki beberapa tombol pada halaman yang sama yang harus pergi ke tempat yang sama tetapi melakukan hal-hal yang sedikit berbeda, Anda dapat menggunakan Aksi yang sama untuk setiap tombol, tetapi menggunakan ActionListener yang berbeda untuk menangani fungsionalitas yang sedikit berbeda.

Berikut ini tautan yang menggambarkan hubungan:

http://www.java-samples.com/showtutorial.php?tutorialid=605

Erick Robertson
sumber
3
ditambah satu, Huruf-huruf tebal mengatakan hampir semuanya.
Shirgill Farhan
0

TL; DR :

The ActionListeners (bisa ada beberapa) mengeksekusi dalam urutan mereka terdaftar SEBELUMaction

Jawaban panjang :

Suatu bisnis actionbiasanya meminta layanan EJB dan jika perlu juga menetapkan hasil akhir dan / atau menavigasi ke tampilan yang berbeda jika itu bukan apa yang Anda lakukan actionListeneradalah lebih tepat yaitu ketika ketika pengguna berinteraksi dengan komponen, seperti h:commandButtonatau h:linkmereka dapat ditangani dengan meneruskan nama metode kacang terkelola dalam actionListeneratribut Komponen UI atau untuk mengimplementasikan ActionListenerantarmuka dan meneruskan nama kelas implementasi ke actionListeneratribut Komponen UI.

Yehuda Schwartz
sumber