Ubah event di pilih dengan knockout binding, bagaimana saya bisa tahu jika itu adalah perubahan nyata?

87

Saya membuat UI izin, saya memiliki daftar izin dengan daftar pilihan di samping setiap izin. Izin diwakili oleh larik objek yang dapat diamati yang terikat ke daftar pilihan:

<div data-bind="foreach: permissions">
     <div class="permission_row">
          <span data-bind="text: name"></span>
          <select data-bind="value: level, event:{ change: $parent.permissionChanged}">
                   <option value="0"></option>
                   <option value="1">R</option>
                   <option value="2">RW</option>
           </select>
      </div>
 </div>

Sekarang masalahnya adalah ini: peristiwa perubahan dimunculkan saat UI baru saja terisi untuk pertama kalinya. Saya memanggil fungsi ajax saya, mendapatkan daftar izin dan kemudian acara dimunculkan untuk setiap item izin. Ini sebenarnya bukan perilaku yang saya inginkan. Saya ingin itu dimunculkan hanya ketika pengguna benar-benar memilih nilai baru untuk izin di daftar pilih , bagaimana saya bisa melakukannya?

pria bodoh
sumber

Jawaban:

109

Sebenarnya Anda ingin mengetahui apakah peristiwa tersebut dipicu oleh pengguna atau program , dan jelas bahwa peristiwa tersebut akan memicu saat inisialisasi.

Pendekatan knockout menambahkan subscriptiontidak akan membantu dalam semua kasus, mengapa karena sebagian besar model akan diimplementasikan seperti ini

  1. init model dengan data yang tidak ditentukan, hanya struktur (actual KO initilization)
  2. perbarui model dengan data awal (logical init like load JSON , get data etc)
  3. Interaksi dan pembaruan pengguna

Langkah sebenarnya yang ingin kita tangkap adalah perubahan di 3, tetapi di langkah kedua subscriptionakan mendapat panggilan, Jadi cara yang lebih baik adalah menambahkan ke perubahan acara seperti

 <select data-bind="value: level, event:{ change: $parent.permissionChanged}">

dan mendeteksi acara dalam permissionChangedfungsi

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}
Sarath
sumber
5
Saya pikir ini harus ditandai sebagai jawaban yang benar. Saya memiliki skenario yang tepat seperti yang dijelaskan dalam pertanyaan, dan saya menguji melewati string sebagai Kunci ke selectelemen tersebut. Namun, meskipun nilai awal yang dipilih cocok dengan salah satu opsi, hal itu tetap memicu changeperistiwa. Ketika saya menerapkan ifblok sederhana ini di pawang, semuanya bekerja seperti yang diharapkan. Coba cara ini dulu. Terima kasih, @Sarath!
Pflugs
Saya suka konsep yang membedakan antara pengguna berubah dan program berubah. Ini berguna dalam kasus sehari-hari, terutama dengan sistem gugur di mana yang dapat diamati kemungkinan besar akan mengubah nilai tanpa interaksi pengguna.
rodiwa
Setuju dengan @Pflugs, membedakan antara jenis acara yang berhasil untuk saya dan ini harus menjadi jawaban yang diterima.
Emma Middlebrook
1
def harus menjadi jawaban yang diterima ... properti tipe acara dapat digunakan untuk membedakan pemicu
premissionChanged
1
Juga menggunakan metode ini untuk mendeteksi apakah pengguna mengubah pilihan daripada pemicu lain. Dalam kasus saya, memperbarui nilai opsi melalui pengikatan "opsi" memicu peristiwa "ubah".
nath
32

Ini hanya tebakan, tapi saya pikir ini terjadi karena levelini adalah angka. Dalam hal ini, valuepengikatan akan memicu changeperistiwa untuk diperbarui leveldengan nilai string. Oleh karena itu, Anda dapat memperbaikinya dengan memastikanlevel adalah string untuk memulai.

Selain itu, cara yang lebih "Knockout" untuk melakukan ini adalah dengan tidak menggunakan event handler, tetapi menggunakan observasi dan langganan. Buatlah levelobservasi dan kemudian tambahkan langganan padanya, yang akan dijalankan setiap kali ada levelperubahan.

Michael Best
sumber
setelah 2 jam menggali penyebab formulir saya memicu peristiwa 'perubahan' setelah pemuatan halaman, Anda menebak menyelamatkan saya.
Samih A
Jawaban Anda memberi saya ide ... Saya sengaja mengubah jenis yang berasal dari url, jadi fungsi berlangganan saya akan memicu pemuatan halaman (saya ingin itu memproses var url, bukan hanya ketika dropdown saya memicu perubahan). Apakah ada cara yang lebih baik untuk melakukan ini?
Jiffy
4

Berikut adalah solusi yang dapat membantu mengatasi perilaku aneh ini. Saya tidak dapat menemukan solusi yang lebih baik daripada meletakkan tombol untuk memicu peristiwa perubahan secara manual.

EDIT: Mungkin penjilidan khusus seperti ini dapat membantu:

ko.bindingHandlers.changeSelectValue = {

   init: function(element,valueAccessor){

        $(element).change(function(){

            var value = $(element).val();

            if($(element).is(":focus")){

                  //Do whatever you want with the new value
            }

        });

    }
  };

Dan di atribut data-bind pilih Anda tambahkan:

changeSelectValue: yourSelectValue
Ingro
sumber
oh ... maksudmu seperti tombol simpan? .. tidak baik untukku .. aku benar-benar hanya perlu ini untuk bekerja :)
guy schaller
Edit jawaban dengan solusi yang lebih baik (semoga).
Ingro
Hai Ingro, ini telah ditandai (salah) sebagai bukan jawaban. Saya mengubah kalimat awal Anda sehingga pelapor tidak akan secara tidak sengaja mengira Anda memposting pertanyaan sebagai jawaban. Semoga ini membantu! :)
jmort253
@ jmort253: maaf, itu salah saya. Lebih baik tidak menjawab dengan menggunakan "Saya memiliki masalah yang sama juga", karena ini sepertinya Anda menanyakan hal lain. Sebaliknya, berikan solusi secara langsung untuk sebuah jawaban dan jelaskan cara kerjanya.
Qantas 94 Heavy
1
Ya maaf, kesalahanku. Itu adalah salah satu jawaban pertama yang saya posting di stackoverflow dan tidak tahu semua aturannya. Terima kasih atas pengeditannya!
Ingro
3

Saya menggunakan penjilidan khusus ini (berdasarkan biola ini oleh RP Niemeyer, lihat jawabannya untuk ini pertanyaan ), yang memastikan nilai numerik dikonversi dengan benar dari string ke angka (seperti yang disarankan oleh solusi Michael Best):

Javascript:

ko.bindingHandlers.valueAsNumber = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function () {
                    var val = ko.utils.unwrapObservable(observable);
                    return (observable() ? observable().toString() : observable());
                },
                write: function (newValue) {
                    observable(newValue ? parseInt(newValue, 10) : newValue);
                },
                owner: this
            });
        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

Contoh HTML:

<select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }">
    <option value="0"></option>
    <option value="1">R</option>
    <option value="2">RW</option>
</select>
mhu
sumber
2

Jika Anda menggunakan observable dan bukan nilai primitif, pemilihan tidak akan memunculkan peristiwa perubahan pada pengikatan awal. Anda dapat terus terikat ke acara perubahan, daripada berlangganan langsung ke yang dapat diamati.

Stefan Smith
sumber
1

Cepat dan kotor, menggunakan bendera sederhana:

var bindingsApplied = false;

var ViewModel = function() {
    // ...

    this.permissionChanged = function() {
        // ignore, if flag not set
        if (!flag) return;

        // ...
    };
};

ko.applyBindings(new ViewModel());
bindingsApplied = true; // done with the initial population, set flag to true

Jika ini tidak berhasil, coba gabungkan baris terakhir dalam setTimeout () - peristiwa asinkron, jadi mungkin yang terakhir masih menunggu saat applyBindings () sudah dikembalikan.

Niko
sumber
hai terima kasih atas balasannya, ini tidak akan berfungsi untuk saya karena saya memanggil ko.applyBindings ketika dom sudah siap, tetapi izin saya sedang diisi ke model saya di tahap selanjutnya ... jadi benderanya akan benar tetapi masalahnya masih akan terjadi.
pria schaller
0

Saya memiliki masalah serupa dan saya baru saja memodifikasi event handler untuk memeriksa jenis variabel. Jenis ini hanya disetel setelah pengguna memilih nilai, bukan saat halaman pertama kali dimuat.

self.permissionChanged = function (l) {
    if (typeof l != 'undefined') {
        ...
    }
}

Sepertinya ini berhasil untuk saya.

fireydude
sumber
0

Gunakan ini:

this.permissionChanged = function (obj, event) {

    if (event.type != "load") {

    } 
}
AGuyCalledGerald
sumber
0

Coba yang ini:

self.GetHierarchyNodeList = function (data, index, event)
{
    debugger;
    if (event.type != "change") {
        return;
    }        
} 

event.type == "change"
event.type == "load" 
Chanaka Sampath
sumber
Parameter ke-2 bukan indeks, bagi saya itu peristiwa.
sairfan
@sairfan menambahkan beberapa parameter ke fungsi dan temukan dari parameter mana Anda mendapatkan peristiwa menggunakan debugger
Chanaka Sampath
-1

Jika Anda bekerja menggunakan Knockout, gunakan fungsi kunci dari fungsi knockout yang dapat diamati.
Gunakan ko.computed()metode dan lakukan serta picu panggilan ajax dalam fungsi itu.

vasu
sumber