如何使Knockout JS在按键时进行数据绑定而不是失去焦点时?

192

这个来自 knockout js 的例子是当你编辑一个字段并按下TAB时,视图模型数据和因此在字段下方的文本将被更新。

我该如何更改此代码,以便在每次键入时都更新视图模型数据?

alt text

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

10
在KO 3.2中,不需要进行所有这些解决方法:https://dev59.com/Vm855IYBdhLWcg3wXTAI#25493265 - Salvador Dali
萨尔瓦多是正确的 - 在最新版本中它会自动完成。 - vapcguy
4个回答

301
<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 应该使用哪个浏览器事件来检测变化。以下字符串值是最常用的选择:

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

    • "keyup" - 当用户释放键盘上的按键时更新你的视图模型

    • "keypress" - 在用户键入按键时更新你的视图模型。与 keyup 不同,这会在用户按住按键时重复更新

    • "afterkeydown" - 当用户开始输入字符时立即更新你的视图模型。它通过捕获浏览器的 keydown 事件并异步处理该事件来实现。

在这些选项中,"afterkeydown" 是如果你想实时保持你的视图模型更新的最佳选择。


9
来自 knockout.js 文档:// 语法 "after<eventname>" 表示 "在事件之后异步运行处理程序" // 这很有用,例如,在浏览器更新控件后捕获"keydown"事件 - John Wright
4
有没有办法将valueUpdate:'afterkeydown'设置为默认值? - Aaron Shafovaloff
2
对于某些输入框,浏览器会显示“自动填充”值(例如,如果输入字段名为“firstname”)。使用“afterkeydown”无法从自动填充中选择。有没有办法捕获这个问题? - Anton
2
@Anton 除了自动填充值之外,还应该捕获屏幕键盘点击和鼠标粘贴事件。使用 afterkeydown 是一个不好的解决方案。 - John Kurlak
2
回答的作者,请更新以包括Salvador Dali的答案,textInput从3.2版本开始添加,它具有更好的移动浏览器兼容性(例如自动完成功能更好)。 - Michael Crook
显示剩余6条评论

87

3.2版本中,你可以简单地使用textinput绑定

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

它有两个重要作用:

  • 进行即时更新
  • 处理剪切、拖拽、自动完成功能等浏览器之间的差异

因此,无需额外的模块、自定义控件和其他东西。


这个非常好用,正是我需要的来替换我的应用程序中到处都有的"value: field, valueUpdate: 'input change'"... :) 谢谢。 - Tod Thomson
有没有一种方法可以捕获用户单击新的HTML5搜索输入字段上的“x”图标时的事件,并像输入为空的情况一样进行操作? - Codrina Valo

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 团队通过 Knockout 3 及更高版本中的 textInput 绑定,为文本输入类似的输入框提供了更完整的绑定。它被设计用于处理所有文本输入方法,包括文本输入框和 textarea。它甚至可以进行实时更新,从而有效地使这种方法变得过时。


2
这种方式可以让我的标记更整洁。此外,除了“afterkeydown”事件之外,“input”和“paste”事件也可以添加以处理粘贴/拖放操作。 - bob
在上面的解决方案中,我认为“typeof valueUpdated ==='array'”需要替换为“Object.prototype.toString.call(valueUpdate)==='[object Array]'”。请参见https://dev59.com/HW445IYBdhLWcg3wnrki。 - daveywc

20

Jeff Mercado的回答很棒,但是在使用Knockout 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);
    };
}());

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

编辑 Ko 3.2.0现在有一个更完整的解决方案,使用新的"textInput"绑定。请参见SalvidorDali的答案。


1
感谢您将答案更新至3.0! - Graham T
@GrahamT 在 KO 3.2 中,你甚至不需要它。这只是一行代码 - Salvador Dali
对于那些使用 KO 3.2 的人来说非常好,因为当我们开始时不知道 TextInput,并且无法更新整个系统中的每个输入! - cscott530

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接