Knockout.js如何捕获值更新前事件并阻止非法值更新视图模型
Knockout框架下拦截输入框值更新前置操作实现问题
核心需求
- 原生JS可在输入框
keydown事件处理函数中返回false,直接阻止按键内容写入输入框。希望基于Knockout绑定在更高层级实现同等能力:- 校验到输入值非法时,直接拦截即将发生的更新,禁止非法值先写入视图模型后再手动回滚,要求在视图模型产生任何变更前就阻断更新操作
- 支持在输入值正式应用到视图模型前执行前置操作,例如更新前保存当前状态的副本
- 不希望采用监听
mousedown、focus事件提前存值、更新后按需回滚的绕路方案,目标是直接拦截更新流程本身。
已尝试的实现与测试结论
初始测试代码如下:
<html> <head> <title>Initial Write Protect Example</title> <script src="knockout-3.5.0.debug.js" type="text/javascript"></script> <script type="text/javascript"> const data = { value: ko.computed({ owner: this, read: function() { return "foo"; }, write: function(x) { console.log("writing: " + x); debugger; } }), }; let allowKey = true; const changeFn = function(x) { console.log("changed: ", x, data.value()); debugger; return false; }; const beforeChangeFn = function(x) { console.log("about to change: ", x, data.value()); debugger; return false; }; const keydownFn = function(x,event) { console.log("keydown: " + event.key + " allow? " + allowKey); return allowKey; }; const keyupFn = function(x,event) { console.log("keyupn: " + event.key); return false; }; </script> </head> <body> <p> <span>Current Value:</span> <span data-bind="text: value"></span> <br/> <input data-bind="value: value, event:{ change: changeFn, beforeChange: beforeChangeFn, keydown: keydownFn, keyup: keyupFn }"></input> </p> <script type="text/javascript">ko.applyBindings(data);</script> </body> </html>
测试得到的结果:
- 仅通过控制
allowKey变量可以阻止按键生效,但无法从Knockout绑定层面拦截值变更流程 - 绑定的
beforeChange事件不会触发(仅在Knockout源码中提及过该事件,因此尝试绑定) change事件触发时,ko.observable的写入操作已经完成,此时视图模型已经发生变更,拦截已经为时过晚
曾考虑修改Knockout源码,自定义一个返回false即可终止后续处理、同时自动将输入框值回滚为更新前原值的beforeChange事件,但需要确认框架本身是否已经提供对应的原生实现,无需自行修改源码。
已知方案的不足
目前已知可以为observable订阅beforeChange事件,示例代码如下:
observable.subscribe((newValue, eventName) => handle(newValue, eventName, 'beforeChange'));
该方案存在明显缺陷:这类处理函数中无法获取到即将写入的新值,无法满足拦截需求——拦截逻辑需要拿到新值做合法性校验,拿不到新值就无法判断是否要阻断更新。
内容的提问来源于stack exchange,提问作者Gunther Schadow
相关产品推荐
相关产品推荐

