Kapan menggunakan valueChangeListener atau f: ajax listener?

88

Apa perbedaan antara dua kode berikut - berkenaan dengan listenerpenempatan?

<h:selectOneMenu ...>
    <f:selectItems ... />
    <f:ajax listener="#{bean.listener}" />
</h:selectOneMenu>

dan

<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
    <f:selectItems ... />
</h:selectOneMenu>
Danijel
sumber

Jawaban:

184

Ini valueChangeListenerhanya akan dipanggil ketika formulir dikirimkan dan nilai yang dikirimkan berbeda dari nilai awal. Oleh karena itu, ini tidak dipanggil ketika hanyachange event HTML DOM yang diaktifkan. Jika Anda ingin mengirimkan formulir selama changeacara DOM HTML , Anda harus menambahkan <f:ajax/>formulir lain tanpa listener (!) Ke komponen input. Ini akan menyebabkan pengiriman formulir yang hanya memproses komponen saat ini (seperti di execute="@this").

<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
    <f:selectItems ... />
    <f:ajax />
</h:selectOneMenu>

Saat menggunakan, <f:ajax listener>bukan valueChangeListener, secara default akan dieksekusi selama changeperistiwa DOM HTML . Di dalam UICommandkomponen dan komponen masukan yang mewakili kotak centang atau tombol radio, ini akan dijalankan secara default selama clickperistiwa DOM HTML saja.

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

Perbedaan utama lainnya adalah bahwa valueChangeListenermetode tersebut dipanggil selama akhir PROCESS_VALIDATIONSfase. Saat itu, nilai yang dikirimkan belum diperbarui dalam model. Jadi Anda tidak bisa mendapatkannya hanya dengan mengakses properti kacang yang terikat ke komponen masukan value. Anda harus melakukannya ValueChangeEvent#getNewValue(). Nilai lama dengan cara juga tersedia oleh ValueChangeEvent#getOldValue().

public void changeListener(ValueChangeEvent event) {
    Object oldValue = event.getOldValue();
    Object newValue = event.getNewValue();
    // ...
}

The <f:ajax listener>Metode dipanggil selama INVOKE_APPLICATIONfase. Saat itu, nilai yang dikirimkan sudah diperbarui dalam model. Anda bisa mendapatkannya dengan langsung mengakses properti kacang yang terikat ke komponen masukan value.

private Object value; // +getter+setter.

public void ajaxListener(AjaxBehaviorEvent event) {
    System.out.println(value); // Look, (new) value is already set.
}

Selain itu, jika Anda perlu memperbarui properti lain berdasarkan nilai yang dikirimkan, maka itu akan gagal saat Anda menggunakan valueChangeListenerkarena properti yang diperbarui dapat diganti dengan nilai yang dikirimkan selama UPDATE_MODEL_VALUESfase berikutnya . Itulah mengapa Anda melihat di aplikasi / tutorial / sumber JSF 1.x lama bahwa a valueChangeListenerdalam konstruksi seperti itu telah digunakan dalam kombinasi dengan immediate="true"dan FacesContext#renderResponse()untuk mencegah hal itu terjadi. Bagaimanapun, menggunakan valueChangeListeneruntuk mengeksekusi tindakan bisnis sebenarnya selalu merupakan peretasan / solusi.

Diringkas: Gunakan valueChangeListenerhanya jika Anda perlu menghentikan perubahan nilai aktual itu sendiri. Yaitu Anda benar-benar tertarik baik yang lama dan nilai baru (misalnya untuk login mereka).

public void changeListener(ValueChangeEvent event) {
    changeLogger.log(event.getOldValue(), event.getNewValue());
}

Gunakan <f:ajax listener>hanya jika Anda perlu melakukan tindakan bisnis pada nilai yang baru diubah. Yaitu Anda sebenarnya hanya tertarik pada nilai baru (misalnya untuk mengisi dropdown kedua).

public void ajaxListener(AjaxBehaviorEvent event) {
    selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}

Jika Anda sebenarnya juga tertarik dengan nilai lama saat menjalankan tindakan bisnis, kembali ke valueChangeListener, tetapi antrekan ke INVOKE_APPLICATIONfase.

public void changeListener(ValueChangeEvent event) {
    if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
        event.setPhaseId(PhaseId.INVOKE_APPLICATION);
        event.queue();
        return;
    }

    Object oldValue = event.getOldValue();
    Object newValue = event.getNewValue();
    System.out.println(newValue.equals(value)); // true
    // ...
}
BalusC
sumber
@BalusC, apakah ada alasan untuk tidak mendapatkan nilai lama dari objek pendukung di penyetel sebelum menyetelnya ke nilai baru yang diteruskan? Sesuatu seperti ini: logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Sepertinya Anda dapat menggunakan nilai lama dan baru yang diperoleh dengan cara itu untuk melakukan logika bisnis secara langsung di setter serta logging sederhana, tetapi saya tidak tahu apakah ini akan menyebabkan efek samping ...
Lucas
Jawaban yang sempurna dan lengkap! Terima kasih telah membagikan pengetahuan Anda!
hbobenicio
@BalusC apakah mungkin juga mendapatkan nilai yang diubah melalui AjaxBehaviorEvent? Saya memiliki <h: selectManyListbox yang dirantai ke komponen lain dan ingin menggunakan yang diubah (Ditambahkan / Dihapus) untuk melakukan beberapa tindakan
Paullo
Terima kasih @BalusC Tapi saya rasa ValueChangeListener tidak akan cocok dengan kasus saya karena saya memiliki beberapa nilai eksekusi dan render seperti yang ditunjukkan <f: ajax event = "valueChange" render = "tests" execute = "@ this" listener = "# {testController. processTimeTable} "/>
Paullo
9

untuk fragmen pertama (atribut pendengar ajax):

Atribut "pendengar" dari tag ajax adalah metode yang dipanggil di sisi server setiap kali fungsi ajax terjadi di sisi klien. Misalnya, Anda dapat menggunakan atribut ini untuk menentukan fungsi sisi server yang akan dipanggil setiap kali pengguna menekan tombol

tetapi fragmen kedua (valueChangeListener):

ValueChangeListener hanya akan dipanggil saat formulir dikirim, bukan saat nilai masukan diubah

* Anda mungkin ingin melihat jawaban praktis ini

aur
sumber