Knockout自定义绑定解析数值相同时无法更新的问题求助
嘿,我明白你遇到的这个头疼的问题了——当输入的浮点数解析后和现有整数数值一样时,输入框死活不更新,比如输11.11能转成11,但再输11.20就停在11.20不动了对吧?我来帮你拆解下原因,然后给你两个靠谱的解决办法。
问题到底出在哪?
核心原因是Knockout的Observable默认只有当值真正发生变化时才会触发变更通知。举个例子:
- 第一次输11.11,解析后是11,你的
EffectObservable从初始的0变成11,这时候会触发通知,绑定的计算属性重新计算,输入框就显示成格式化后的11; - 第二次输11.20,解析后还是11,和Observable当前值一模一样,这时候Knockout就觉得“没必要通知,值没改啊”,结果绑定的计算属性就不重新运行,输入框自然就停留在你输入的11.20,没更新成11。
你之前加的.extend({notify: 'always'})没生效,是因为这个扩展是加在ViewModel的Effect上,但你的自定义绑定里用了interceptor这个计算属性当中间层,而且赋值的时候直接用observable(v),没触发那个强制通知的逻辑。
两个可行的解决方案
方案1:强制Observable触发通知
最简单的办法就是在给Observable赋值后,不管值变没变,都强制它触发变更通知。修改你自定义绑定里的write方法就行:
write: function (newValue) { newValue = newValue.replace(/\s/g, ''); var v = BindingHelpers.reverseFormat(newValue, defaultValue); v = (v*1).toFixed(0); if (v > maxInt) v = maxInt; // 先赋值,再强制触发变更通知 observable(v); if (ko.isObservable(observable)) { observable.valueHasMutated(); } }
valueHasMutated()是Knockout Observable的内置方法,不管当前值和新值是否相等,调用它都会让所有依赖这个Observable的计算属性重新计算,这样输入框就会乖乖更新成格式化后的整数了。
方案2:给计算属性加个“触发开关”
如果不想改动Observable的默认通知行为,也可以给你的interceptor计算属性加一个额外的依赖,每次用户输入就触发这个依赖,让计算属性重新运行:
// 新增一个用来触发更新的Observable,初始值随便设 var updateTrigger = ko.observable(0); var interceptor = ko.computed({ read: function () { updateTrigger(); // 把这个触发器加入依赖,每次它变了就重新执行read var value = ko.isObservable(observable) ? observable() : observable; if (value === null || value === undefined) return defaultValue; return BindingHelpers.formatWithThousandSeparators(value); }, write: function (newValue) { newValue = newValue.replace(/\s/g, ''); var v = BindingHelpers.reverseFormat(newValue, defaultValue); v = (v*1).toFixed(0); if (v > maxInt) v = maxInt; observable(v); updateTrigger(updateTrigger() + 1); // 更新触发器的值,触发read重新执行 } });
这个思路就是用一个独立的Observable当开关,每次用户输入后就把开关拨一下,让计算属性的read方法必须重新跑一遍,自然就更新输入框了。
小建议
如果想优化一下,可以在write方法里先判断解析后的v和当前Observable值是否相等,只有相等的时候才触发强制通知,不过这点性能影响几乎可以忽略,看你自己需求啦。另外,你的BindingHelpers里的格式化方法最好加个空值/非数值的判断,避免出现意外报错。
内容的提问来源于stack exchange,提问作者RoarG

