如何让Knockout JS在按键上进行数据绑定而不是丢失焦点?

Edw*_*uay 190 javascript knockout.js

这个淘汰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>
Run Code Online (Sandbox Code Playgroud)

Ser*_*los 297

<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>
Run Code Online (Sandbox Code Playgroud)

文档中

附加参数

  • valueUpdate

    如果绑定还包含一个名为valueUpdate的参数,则会定义KO应使用哪个浏览器事件来检测更改.以下字符串值是最常用的选项:

    • "更改"(默认) - 当用户将焦点移动到不同的控件时更新视图模型,或者在元素的情况下,在任何更改后立即更新视图模型

    • "keyup" - 在用户释放密钥时更新您的视图模型

    • "keypress" - 在用户键入密钥时更新您的视图模型.与keyup不同,当用户按住键时,它会反复更新

    • "afterkeydown" - 一旦用户开始输入字符,就会更新您的视图模型.这可以通过捕获浏览器的keydown事件并异步处理事件来实现.

在这些选项中,如果您希望实时更新视图模型,"afterkeydown"是最佳选择.

  • 来自knockout.js中的文档://语法"在<eventname>之后"意味着"在事件之后异步运行处理程序"//这有用,例如,在浏览器更新控件后捕获"keydown"事件 (9认同)
  • 无论如何得到valueUpdate:'afterkeydown'是_default_? (4认同)
  • 对于某些输入框,浏览器显示"自动填充"值(如果输入字段名为"firstname").从自动填充中选择并不适用于"afterkeydown".有没有办法抓住这个? (2认同)
  • @Anton除了自动填充值之外,还有屏幕键盘点击和鼠标粘贴事件应该被捕获.使用`afterkeydown`是一个糟糕的解决方案. (2认同)
  • 这个答案的作者,请更新到还包括萨尔瓦多·达利的答案,从3.2开始添加textInput是一个更好的解决方案,因为它与移动浏览器具有更好的兼容性(比如自动完成工作更好) (2认同)

Sal*_*ali 85

3.2版中,您可以简单地使用textinput绑定.:

<input data-bind="textInput: userName" />
Run Code Online (Sandbox Code Playgroud)

它做了两件重要的事情:

  • 立即更新
  • 处理浏览器差异以进行剪切,拖动,自动完成......

所以不需要额外的模块,自定义控件和其他东西.

  • 这应该是新接受的答案 (9认同)
  • 这是一个很棒的方法来做到这一点 (3认同)

Jef*_*ado 35

如果您希望它在afterkeydown"默认情况下" 进行更新,您可以valueUpdatevalue绑定处理程序中注入绑定.只需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
    };
} ());
Run Code Online (Sandbox Code Playgroud)

如果您不习惯"覆盖" value绑定,则可以为覆盖的自定义绑定指定不同的名称并使用该绑定处理程序.

ko.bindingHandlers.realtimeValue = { 'init':..., 'update':... };
Run Code Online (Sandbox Code Playgroud)

演示

像这样的解决方案适用于Knockout版本2.x. Knockout团队通过Knockout版本3及更高版本中的textInput绑定充实了更完整的文本输入绑定.它旨在处理文本输入和文本输入的所有文本输入方法textarea.它甚至可以处理实时更新,这有效地使这种方法过时了.

  • 这种方式使我的标记更整洁.此外,除了'afterkeydown'之外,还可以添加'input'和'paste'事件来处理粘贴/拖放操作. (2认同)

Roc*_*lve 20

杰夫·梅尔卡多的回答太棒了,但不幸的是被淘汰赛3淘汰了.

但是我通过Knockout 3的变化找到了ko开发人员提出的答案.请参阅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);
    };
}());
Run Code Online (Sandbox Code Playgroud)

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

编辑 Ko 3.2.0现在有一个更完整的解决方案与新的"textInput"绑定.见SalvidorDali的回答