Как я могу заставить Knockout JS связываться с данными при нажатии клавиш вместо потерянного фокуса?

191

Этот пример нокаута js работает так, что когда вы редактируете поле и нажимаете клавишу TAB, данные модели представления и, следовательно, текст под полями обновляются.

Как я могу изменить этот код так, чтобы данные viewmodel обновлялись при каждом нажатии клавиши?

альтернативный текст

<!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>
Эдвард Тангей
источник
9
В KO 3.2 нет необходимости делать все эти обходные пути: stackoverflow.com/a/25493265/1090562
Сальвадор Дали
Сальвадор прав - он делает это автоматически в последней версии.
vapcguy

Ответы:

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>

Из документации

Дополнительные параметры

  • valueUpdate

    Если ваша привязка также включает параметр с именем valueUpdate, это определяет, какое событие браузера KO следует использовать для обнаружения изменений. Следующие строковые значения являются наиболее полезными вариантами:

    • «изменить» (по умолчанию) - обновляет модель представления, когда пользователь перемещает фокус на другой элемент управления или, в случае элементов, сразу после любого изменения

    • «keyup» - обновляет модель представления, когда пользователь отпускает ключ

    • «нажатие клавиши» - обновляет модель представления, когда пользователь набрал клавишу. В отличие от keyup, он обновляется многократно, пока пользователь удерживает клавишу нажатой

    • «afterkeydown» - обновляет модель представления, как только пользователь начинает вводить символ. Это работает, перехватывая событие keydown браузера и обрабатывая событие асинхронно.

Из этих вариантов «afterkeydown» - лучший выбор, если вы хотите, чтобы ваша модель представления обновлялась в режиме реального времени.

Сергей Голос
источник
9
из документов в knockout.js: // Синтаксис «after <eventname>» означает «запускать обработчик асинхронно после события» // Это полезно, например, для отлова событий «keydown» после того, как браузер обновил элемент управления
Джон Райт
4
В любом случае, чтобы значение valueUpdate: 'afterkeydown' было значением по умолчанию ?
Аарон Шафовалов
2
для некоторых полей ввода браузер отображает «автоматически заполненные» значения (например, если поле ввода называется «имя»). выбор из автозаполнения не работает с "afterkeydown". Есть ли способ поймать это?
Антон
2
@Anton В дополнение к автоматическому заполнению значений, на экранной клавиатуре есть также щелчки и события вставки мышью, которые нужно перехватить. Использование afterkeydownэто плохое решение.
Джон Курлак
2
Автор этого ответа, пожалуйста, обновите его, чтобы включить также ответ Сальвадора Дали, добавление textInput с версии 3.2 является лучшим решением, поскольку оно лучше совместимо с мобильными браузерами (такие вещи, как автоматическое завершение работы)
Майкл Крук
85

В версии 3.2 вы можете просто использовать привязку textinput. :

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

Это делает две важные вещи:

  • сделать немедленные обновления
  • обрабатывает различия браузера для вырезания, перетаскивания, автозаполнения ...

Поэтому нет необходимости в дополнительных модулях, пользовательских элементах управления и прочем.

Сальвадор Дали
источник
Это работает очень хорошо, и это именно то, что мне нужно, чтобы заменить value: field, valueUpdate: 'input change', которое у меня было в моем приложении ... :) спасибо.
Тод Томсон
Есть ли способ зафиксировать событие, когда пользователь нажимает на значок «х» в новом поле ввода поиска HTML5 и действует так, как если бы ввод был пустым?
Кодрина Вало
35

Если вы хотите, чтобы он обновлял afterkeydown«по умолчанию», вы можете вставить valueUpdateпривязку в valueобработчик привязки. Просто предоставьте новый allBindingsAccessorобработчик для использования, который включает в себя 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
    };
} ());

Если вам неудобно «переопределять» valueпривязку, вы можете присвоить переопределенной пользовательской привязке другое имя и использовать этот обработчик привязки.

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

демонстрация

Такое решение подойдет для Knockout версии 2.x. Команда Knockout разработала более полную привязку для текстовых вводов через привязку textInput в Knockout версии 3 и выше. Он был разработан для обработки всех методов ввода текста для ввода текста и textarea. Он даже будет обрабатывать обновления в реальном времени, что делает этот подход устаревшим.

Джефф Меркадо
источник
2
Этот способ делает мою разметку немного аккуратнее. Кроме того, помимо 'afterkeydown', могут быть добавлены события 'input' и 'paste' для обработки действий вставки / перетаскивания.
Боб
В приведенном выше решении я думаю, что "typeof valueUpdated === 'array'" необходимо заменить на "Object.prototype.toString.call (valueUpdate) === '[object Array]'". См stackoverflow.com/questions/4775722/...
daveywc
20

Ответ Джеффа Меркадо фантастический, но, к сожалению, он не работает с нокаутом 3.

Но я нашел ответ, предложенный разработчиками ко при работе с изменениями Knockout 3. Смотрите нижние комментарии на https://github.com/knockout/knockout/pull/932 . Их код:

//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 теперь имеет более полное решение с новой привязкой textInput. Смотрите ответ SalvidorDali

RockResolve
источник
1
Спасибо за обновление ответа до 3.0!
Грэм Т,
@GrahamT в KO 3.2 это даже не нужно. Это всего лишь одна строка
Сальвадор Дали
Очень хорошо для тех из нас, кто использует KO 3.2, кто не знал о TextInput, когда мы начинали, и не может обновить каждый ввод во всей системе!
cscott530