All of the sudden, the following code no longer works when targeting KnockoutJS 3.0. How can I work around this?
JavaScript:
ko.bindingHandlers.limitCharacters = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel)
{
element.value = element.value.substr(0, valueAccessor());
allBindingsAccessor().value(element.value.substr(0, valueAccessor()));
}
};
HTML:
<textarea data-bind="value: comment, valueUpdate: 'afterkeydown', limitCharacters: 20"></textarea>
See fiddle: http://jsfiddle.net/ReQrz/1/
In KO 3.0 the bindings are Independent and ordered. You can read more about this here, and this should be considered a "breaking change", from the above linked example:
v2.x’s behavior about dependencies between bindings (described in the section about “Independent and ordered bindings” above), is an undocumented internal implementation detail so hopefully you aren’t relying on it. But if you are relying on that then obviously you will see a change of behavior because bindings are independent in v3 by design. You’ll need to stop relying on cross-binding dependencies, which by the way will make your code much cleaner and easier to understand.
So you binding is not working anymore because it assumed that when your comment
property is changed it fires also your limitCharacters
binging altough your limitCharacters
binding has nothing to do with the comment
property.
One possible solution for fixing this that you need to explicitly declare a dependency on the value
binding in your update
handler by acessing its value with allBindingsAccessor().value();
:
ko.bindingHandlers.limitCharacters = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel)
{
var val = allBindingsAccessor().value();
allBindingsAccessor().value(val.substr(0, valueAccessor()));
}
};
Demo JSFiddle.
nemesv is completely right.
His modification is short and easy to read. One drawback of that though is the observable is called twice when in exceeds the limit.
If you don't want that, a solution is to create a custom value binder which derives from the original.
(function() {
var limitValueBindingHandler = {};
var valueBindingHandler = ko.bindingHandlers.value;
for(var attr in valueBindingHandler) {
if (valueBindingHandler.hasOwnProperty(attr)) {
limitValueBindingHandler[attr] = valueBindingHandler[attr];
}
}
limitValueBindingHandler.init = function(element, valueAccessor, allBindings) {
var limitCharacters = allBindings.get("limitCharacters");
element.addEventListener("keydown", function() {
setTimeout(function() {
//this is called after the element's value is updated
//but before value binding event handler
element.value = element.value.substr(0, limitCharacters);
}, 0);
}, false);
valueBindingHandler.init(element, valueAccessor, allBindings);
}
ko.bindingHandlers['limitValue'] = limitValueBindingHandler;
})();
Sample: http://jsfiddle.net/DAFN6/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With