Bagaimana saya bisa mendapatkan Knockout JS ke data-bind pada penekanan tombol dan bukannya kehilangan fokus?

191

Contoh knockout js ini berfungsi saat Anda mengedit bidang dan menekan TAB, viewmodel data dan karenanya teks di bawah bidang diperbarui.

Bagaimana saya bisa mengubah kode ini agar data model view diperbarui setiap kali tombol ditekan?

teks alternatif

<!doctype html>
<html>
    <title>knockout js</title>
    <head>
        <script type="text/javascript" src="js/knockout-1.1.1.debug.js"></script>
        <script type="text/javascript">
        window.onload= function() {

            var viewModel = {
                firstName : ko.observable("Jim"),
                lastName : ko.observable("Smith")
            };
            viewModel.fullName = ko.dependentObservable(function () {
                return viewModel.firstName() + " " + viewModel.lastName();
            });

            ko.applyBindings(viewModel);
       }
        </script>
    </head>
    <body>
        <p>First name: <input data-bind="value: firstName" /></p>
        <p>Last name: <input data-bind="value: lastName" /></p>
        <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
    </body>
</html>
Edward Tanguay
sumber
9
Dalam KO 3.2 tidak perlu melakukan semua penyelesaian ini: stackoverflow.com/a/25493265/1090562
Salvador Dali
Salvador benar - ia melakukannya secara otomatis dalam versi terbaru.
vapcguy

Jawaban:

299
<body>
        <p>First name: <input data-bind="value: firstName, valueUpdate: 'afterkeydown'" /></p>
        <p>Last name: <input data-bind="value: lastName, valueUpdate: 'afterkeydown'" /></p>
        <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
</body>

Dari dokumentasi

Parameter tambahan

  • valueUpdate

    Jika penjilidan Anda juga menyertakan parameter yang disebut valueUpdate, ini menentukan peristiwa browser mana yang harus digunakan KO untuk mendeteksi perubahan. Nilai string berikut adalah pilihan paling umum yang berguna:

    • "change" (default) - memperbarui model tampilan Anda ketika pengguna memindahkan fokus ke kontrol yang berbeda, atau dalam hal elemen, segera setelah perubahan apa pun

    • "keyup" - memperbarui model tampilan Anda ketika pengguna merilis kunci

    • "keypress" - memperbarui model tampilan Anda ketika pengguna telah mengetik kunci. Tidak seperti keyup, pembaruan ini berulang kali saat pengguna menekan tombol

    • "afterkeydown" - memperbarui model tampilan Anda segera setelah pengguna mulai mengetik karakter. Ini bekerja dengan menangkap acara keydown browser dan menangani acara secara tidak sinkron.

Dari opsi-opsi ini, "afterkeydown" adalah pilihan terbaik jika Anda ingin menjaga model tampilan Anda diperbarui secara real-time.

Sergei Golos
sumber
9
dari docs di knockout.js: // Sintaksnya "setelah <eventname>" berarti "jalankan pawang secara tidak sinkron setelah acara" // Ini berguna, misalnya, untuk menangkap peristiwa "keydown" setelah browser memperbarui kontrol.
John Wright
4
Apakah ada cara untuk mendapatkan valueUpdate: 'afterkeydown' sebagai default ?
Aaron Shafovaloff
2
untuk beberapa kotak input, browser menampilkan nilai "auto-populate" (seperti jika bidang input bernama "firstname"). memilih dari populasi otomatis tidak berfungsi dengan "afterkeydown". apakah ada cara untuk menangkap ini?
Anton
2
@Anton Selain nilai pengisian otomatis, ada juga klik keyboard di layar dan acara tempel mouse yang harus ditangkap. Menggunakan afterkeydownadalah solusi yang buruk.
John Kurlak
2
Penulis jawaban ini, harap perbarui untuk juga menyertakan jawaban Salvador Dali, textInput ditambahkan pada 3.2 adalah solusi yang lebih baik karena memiliki kompatibilitas yang lebih baik dengan browser seluler (hal-hal seperti auto complete work better)
Michael Crook
85

Dalam versi 3.2 Anda cukup menggunakan pengikatan input teks. :

<input data-bind="textInput: userName" />

Ia melakukan dua hal penting:

  • segera lakukan pembaruan
  • menangani perbedaan browser untuk potong, seret, lengkapi otomatis ...

Jadi tidak perlu modul tambahan, kontrol khusus dan hal-hal lain.

Salvador Dali
sumber
Ini bekerja sangat baik dan persis apa yang saya butuhkan untuk mengganti nilai: bidang, valueUpdate: 'perubahan input' yang saya miliki semua melalui aplikasi saya ... :) terima kasih.
Tod Thomson
Apakah ada cara untuk menangkap peristiwa ketika pengguna mengklik ikon 'x' pada bidang input pencarian HTML5 baru dan bertindak seperti dalam kasus input kosong?
Codrina Valo
35

Jika Anda ingin melakukan pembaruan pada afterkeydown"secara default," Anda bisa menyuntikkan valueUpdatepengikatan dalam valuepengikat yang mengikat. Cukup sediakan yang baru allBindingsAccessoruntuk digunakan pawang yang menyertakannya afterkeydown.

(function () {
    var valueHandler = ko.bindingHandlers.value;
    var getInjectValueUpdate = function (allBindingsAccessor) {
        var AFTERKEYDOWN = 'afterkeydown';
        return function () {
            var allBindings = ko.utils.extend({}, allBindingsAccessor()),
                valueUpdate = allBindings.valueUpdate;

            if (valueUpdate === undefined) {
                return ko.utils.extend(allBindings, { valueUpdate: AFTERKEYDOWN });
            } else if (typeof valueUpdate === 'string' && valueUpdate !== AFTERKEYDOWN) {
                return ko.utils.extend(allBindings, { valueUpdate: [valueUpdate, AFTERKEYDOWN] });
            } else if (typeof valueUpdate === 'array' && ko.utils.arrayIndexOf(valueUpdate, AFTERKEYDOWN) === -1) {
                valueUpdate = ko.utils.arrayPushAll([AFTERKEYDOWN], valueUpdate);
                return ko.utils.extend(allBindings, {valueUpdate: valueUpdate});
            }
            return allBindings;
        };
    };
    ko.bindingHandlers.value = {
        // only needed for init
        'init': function (element, valueAccessor, allBindingsAccessor) {
            allBindingsAccessor = getInjectValueUpdate(allBindingsAccessor);
            return valueHandler.init(element, valueAccessor, allBindingsAccessor);
        },
        'update': valueHandler.update
    };
} ());

Jika Anda tidak nyaman "mengesampingkan" valuepengikatan, Anda bisa memberikan pengubah pengikatan nama yang berbeda dan menggunakan penangan yang mengikat itu.

ko.bindingHandlers.realtimeValue = { 'init':..., 'update':... };

demo

Solusi seperti ini akan cocok untuk versi Knockout 2.x. Tim Knockout telah menyempurnakan pengikatan untuk input seperti teks melalui pengikatan teksInput di Knockout versi 3 dan lebih tinggi. Itu dirancang untuk menangani semua metode input teks untuk input teks dan textarea. Bahkan akan menangani pembaruan waktu nyata yang secara efektif menjadikan pendekatan ini usang.

Jeff Mercado
sumber
2
Cara ini membuat markup saya sedikit lebih rapi. Selain itu, di samping 'afterkeydown', acara 'input' dan 'paste' dapat ditambahkan untuk menangani tindakan tempel / seret dan lepas.
bob
Dalam solusi di atas saya berpikir bahwa "typeof valueUpdated === 'array'" perlu diganti dengan "Object.prototype.toString.call (valueUpdate) === '[object Array]'". Lihat stackoverflow.com/questions/4775722/…
daveywc
20

Jawaban Jeff Mercado luar biasa, tetapi sayangnya rusak dengan Knockout 3.

Tapi saya menemukan jawaban yang disarankan oleh ko devs saat bekerja melalui perubahan Knockout 3. Lihat komentar bawah di https://github.com/knockout/knockout/pull/932 . Kode mereka:

//automatically add valueUpdate="afterkeydown" on every value binding
(function () {
    var getInjectValueUpdate = function (allBindings) {
        return {
            has: function (bindingKey) {
                return (bindingKey == 'valueUpdate') || allBindings.has(bindingKey);
            },
            get: function (bindingKey) {
                var binding = allBindings.get(bindingKey);
                if (bindingKey == 'valueUpdate') {
                    binding = binding ? [].concat(binding, 'afterkeydown') : 'afterkeydown';
                }
                return binding;
            }
        };
    };

    var valueInitHandler = ko.bindingHandlers.value.init;
    ko.bindingHandlers.value.init = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        return valueInitHandler(element, valueAccessor, getInjectValueUpdate(allBindings), viewModel, bindingContext);
    };
}());

http://jsfiddle.net/mbest/GKJnt/

Edit Ko 3.2.0 sekarang memiliki solusi yang lebih lengkap dengan ikatan "textInput" yang baru. Lihat jawaban SalvidorDali

RockResolve
sumber
1
Terima kasih telah memperbarui jawaban ke 3.0!
Graham T
@ GrahamT di KO 3.2 Anda bahkan tidak membutuhkannya. Itu hanya satu baris
Salvador Dali
Sangat bagus bagi kita di KO 3.2 yang tidak tahu tentang TextInput ketika kita mulai, dan tidak dapat memperbarui setiap input di seluruh sistem!
cscott530