Jak sprawić, by Knockout JS powiązał dane po naciśnięciu klawisza zamiast stracić koncentrację?

191

Ten przykład knockout js działa, więc gdy edytujesz pole i naciśniesz TAB, dane viewmodel, a zatem tekst pod polami, zostanie zaktualizowany.

Jak mogę zmienić ten kod, aby dane viewmodel były aktualizowane przy każdym naciśnięciu klawisza?

alternatywny tekst

<!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
źródło
9
W KO 3.2 nie trzeba wykonywać wszystkich tych obejść: stackoverflow.com/a/25493265/1090562
Salvador Dali
Salvador ma rację - robi to automatycznie w najnowszej wersji.
vapcguy

Odpowiedzi:

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>

Z dokumentacji

Dodatkowe parametry

  • valueUpdate

    Jeśli twoje powiązanie zawiera również parametr o nazwie valueUpdate, określa to, które zdarzenie przeglądarki KO powinno użyć do wykrycia zmian. Następujące wartości ciągu są najczęściej użytecznymi opcjami:

    • „zmiana” (domyślnie) - aktualizuje model widoku, gdy użytkownik przenosi fokus do innej kontrolki lub, w przypadku elementów, natychmiast po każdej zmianie

    • „keyup” - aktualizuje model widoku, gdy użytkownik zwolni klucz

    • „naciśnięcie klawisza” - aktualizuje model widoku, gdy użytkownik wpisze klawisz. W przeciwieństwie do keyup, ta aktualizacja jest aktualizowana wielokrotnie, gdy użytkownik przytrzymuje klawisz

    • „afterkeydown” - aktualizuje model widoku, gdy tylko użytkownik zacznie wpisywać znak. Działa to poprzez przechwytywanie zdarzenia keydown przeglądarki i obsługę tego zdarzenia asynchronicznie.

Spośród tych opcji „afterkeydown” jest najlepszym wyborem, jeśli chcesz aktualizować model widoku w czasie rzeczywistym.

Siergiej Golos
źródło
9
z dokumentów w knockout.js: // Składnia „po <nazwa_wydania>” oznacza „uruchomić program obsługi asynchronicznie po zdarzeniu” // Jest to przydatne, na przykład, do przechwytywania zdarzeń „keydown” po zaktualizowaniu formantu przez przeglądarkę
John Wright,
4
Czy mimo to uzyskać valueUpdate: „afterkeydown” być domyślną ?
Aaron Shafovaloff
2
w przypadku niektórych pól wejściowych przeglądarka wyświetla wartości „automatycznie wypełniające” (np. jeśli pole wejściowe ma nazwę „imię”). wybranie z automatycznego wypełniania nie działa z „afterkeydown”. czy istnieje sposób, aby to złapać?
Anton
2
@Anton Oprócz autouzupełniania wartości istnieją również kliknięcia na klawiaturze ekranowej i zdarzenia wklejania myszy. Używanie afterkeydownjest złym rozwiązaniem.
John Kurlak
2
Autor tej odpowiedzi, zaktualizuj ją, aby zawierała również odpowiedź Salvadora Dali, textInput dodany od wersji 3.2 jest lepszym rozwiązaniem, ponieważ ma lepszą kompatybilność z przeglądarkami mobilnymi (rzeczy takie jak autouzupełnianie działają lepiej)
Michael Crook
85

W wersji 3.2 możesz po prostu użyć wiązania textinput. :

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

Robi dwie ważne rzeczy:

  • dokonywać natychmiastowych aktualizacji
  • obsługuje różnice przeglądarki dla wycinania, przeciągania, autouzupełniania ...

Więc nie ma potrzeby stosowania dodatkowych modułów, niestandardowych elementów sterujących i innych rzeczy.

Salvador Dali
źródło
Działa to naprawdę dobrze i jest dokładnie tym, czego potrzebowałem, aby zastąpić wartość: pole, wartośćUpdate: „zmiana danych wejściowych”, którą miałem przez całą aplikację ... :) dzięki.
Tod Thomson
Czy istnieje sposób na uchwycenie zdarzenia, gdy użytkownik kliknie ikonę „x” w nowym polu wejściowym wyszukiwania HTML5 i zachowuje się tak, jakby dane wejściowe były puste?
Codrina Valo,
35

Jeśli chcesz, aby aktualizacje były wykonywane afterkeydown„domyślnie”, możesz wstrzyknąć valueUpdatepowiązanie w module valueobsługi powiązań. Po prostu podaj nowy allBindingsAccessordla obsługi, który obejmuje 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
    };
} ());

Jeśli nie masz ochoty „zastępować” valuepowiązania, możesz nadać niestandardowemu powiązaniu inną nazwę i użyć tej procedury obsługi powiązania.

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

próbny

Takie rozwiązanie byłoby odpowiednie dla Knockout w wersji 2.x. Zespół Knockout opracował bardziej kompletne wiązanie dla tekstowych danych wejściowych poprzez wiązanie textInput w Knockout w wersji 3 i nowszych. Został zaprojektowany do obsługi wszystkich metod wprowadzania tekstu dla wprowadzania tekstu i textarea. Będzie nawet obsługiwał aktualizację w czasie rzeczywistym, co skutecznie czyni to podejście przestarzałym.

Jeff Mercado
źródło
2
W ten sposób moje znaczniki są trochę bardziej uporządkowane. Oprócz zdarzeń „afterkeydown” można także dodawać zdarzenia „input” i „paste”, aby obsłużyć akcje wklejania / przeciągania i upuszczania.
Bob
W powyższym rozwiązaniu myślę, że „typeof valueUpdated ===„ array ”” należy zastąpić „Object.prototype.toString.call (valueUpdate) ===” [obiekt Array] ”. Zobacz stackoverflow.com/questions/4775722/…
daveywc
20

Odpowiedź Jeffa Mercado jest fantastyczna, ale niestety złamana w Knockout 3.

Ale znalazłem odpowiedź sugerowaną przez twórców ko podczas pracy nad zmianami w Knockout 3. Zobacz dolne komentarze na https://github.com/knockout/knockout/pull/932 . Ich kod:

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

Edycja Ko 3.2.0 ma teraz pełniejsze rozwiązanie z nowym wiązaniem „textInput”. Zobacz odpowiedź SalvidorDali

RockResolve
źródło
1
Dziękujemy za aktualizację odpowiedzi do wersji 3.0!
Graham T
@GrahamT w KO 3.2 nawet go nie potrzebujesz. To tylko jedna linia
Salvador Dali
Bardzo miło dla tych z nas na KO 3.2, którzy nie wiedzieli o TextInput, kiedy zaczynaliśmy i nie mogą zaktualizować wszystkich danych wejściowych w całym systemie!
cscott530,