您可以创建一个在内部调用绑定的自定义绑定,或者您可以在实际绑定之前将绑定value
覆盖为自动修剪(不推荐)。value
基本思想:
- 拦截
value
绑定
- 将传递的 observable 包装在一个
computed
- 进行绑定
read
并write
从计算的而不是从原始的 observable
- 当新输入到达时,在我们写之前修剪它
- 当模型值发生变化时,修剪它并根据需要更新模型和 UI
ko.bindingHandlers.trimmedValue = {
init: function(element, valueAccessor, allBindings) {
const ogValue = valueAccessor();
let newVa = valueAccessor;
// If this is a type="text" element and the data-bound value is observable,
// we create a new value accessor that returns an in-between layer to do
// our trimming
if (element.type === "text" && ko.isObservable(ogValue)) {
const trimmedValue = ko.observable().extend({"trim": true});
// Write to the model whenever we change
trimmedValue.subscribe(ogValue);
// Update when the model changes
ogValue.subscribe(trimmedValue);
// Initialize with model value
trimmedValue(ogValue());
// From now on, work with the trimmedValue
newVa = () => trimmedValue;
}
// Note: you can also use `ko.applyBindingsToNode`
return ko.bindingHandlers.value.init(element, newVa, allBindings)
}
}
// Our observable to check our results with
var myObs = ko.observable("test ");
myObs.subscribe(function(newValue) {
console.log("Change: \"" + newValue + "\"");
});
// The extender that does the actual trim
ko.extenders.trim = function(target, option) {
return ko.computed({
read: target,
write: function(val) {
target(
val && typeof val.trim === "function"
? val.trim()
: val
);
// This makes sure the trimming always resets the input UI
if (val !== target.peek()) {
target.valueHasMutated();
}
}
}).extend({notify: "always"});
};
ko.applyBindings({
myObs: myObs
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h4><code>type="text" trimmedValue</code></h4>
<input type="text" data-bind="trimmedValue: myObs">
如果你不关心valueHasMutated
模型中一些不需要的 s
棘手的部分是确定您希望在模型中接收哪些更新......下面的示例不会触发valueHasMutated
或改变模型的 observable。但是,如果您将模型值更改为未修剪的字符串,绑定处理程序将立即重置它。eg:myObs(" test ")
会触发
Change: " test "
, 和
Change: "test"
如果您只需要从 UI 修剪到模型,并且不介意一些额外的更新,您可以使用:
ko.bindingHandlers.value.init = function(element, valueAccessor, allBindings) {
const ogValue = valueAccessor();
const newVa = (element.type === "text" && ko.isObservable(ogValue))
? () => ogValue.extend({"trim": true})
: valueAccessor;
return ogValueInit(element, newVa, allBindings)
};
覆盖默认value
绑定
要将此行为用作标准行为(同样,不推荐),您可以执行以下操作:
const ogValueInit = ko.bindingHandlers.value.init;
ko.bindingHandlers.value.init = function( /*... */ ) {
// ...
return ogValueInit( /* ... */);
};
const ogValueInit = ko.bindingHandlers.value.init;
ko.bindingHandlers.value.init = function(element, valueAccessor, allBindings) {
const ogValue = valueAccessor();
let newVa = valueAccessor;
// If this is a type="text" element and the data-bound value is observable,
// we create a new value accessor that returns an in-between layer to do
// our trimming
if (element.type === "text" && ko.isObservable(ogValue)) {
const trimmedValue = ko.observable().extend({"trim": true});
// Write to the model whenever we change
trimmedValue.subscribe(ogValue);
// Update when the model changes
ogValue.subscribe(trimmedValue);
// Initialize with model value
trimmedValue(ogValue());
// From now on, work with the trimmedValue
newVa = () => trimmedValue;
}
return ogValueInit(element, newVa, allBindings)
};
// Our observable to check our results with
var myObs = ko.observable("test ");
myObs.subscribe(function(newValue) {
console.log("Change: \"" + newValue + "\"");
});
// The extender that does the actual trim
ko.extenders.trim = function(target, option) {
return ko.computed({
read: target,
write: function(val) {
target(
val && typeof val.trim === "function"
? val.trim()
: val
);
// This makes sure the trimming always resets the input UI
if (val !== target.peek()) {
target.valueHasMutated();
}
}
}).extend({notify: "always"});
};
ko.applyBindings({
myObs: myObs
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h4><code>type="text" value</code></h4>
<input type="text" data-bind="value: myObs">