Reputation: 803
I created a computed observable that works well formatting phone numbers. It will take a string of numbers and strip it down to this format xxx-xxx-xxxx when focus is off the form field. then I a have a controller action stripping it down to format xxxxxxxxxx before it persists to the database. Then my computed observable re-formats it to the xxx-xxx-xxxx format.
I now want the to create a reusable custom binding handler that can be implemented across our application. The problem is that I cannot get it to do the last part where it re-formats it back in the form field. So, the issue is when I click update, the form field displays the number as xxxxxxxxxx (the same way as it is in the DB) Does anyone know what I need to change to make by custom binding work like my current computed observable?
Observable:
self.Phone = ko.observable(model.MainPhone ? model.MainPhone : "").extend({ maxLength: 20, minLength: 10 });
Computed Observable working correctly:
self.PhoneFormat = ko.computed(function () {
var phoneFormatting = self.Phone()
.replace(/\D+/g, "")
.replace(/^[01]/, "")
.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3")
.substring(0, 12);
return self.Phone() ? self.Phone(phoneFormatting) : "";
}, self);
Custom binding not working correctly:
ko.bindingHandlers.formatPhone = {
init: function (element, valueAccessor, allBindings) {
var source = valueAccessor();
var formatter = function () {
return ko.computed({
read: function() { return source(); },
write: function(newValue) {
source(newValue.replace(/\D+/g, "")
.replace(/^[01]/, "")
.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3")
.substring(0, 12));
}
});
};
ko.bindingHandlers.value.init(element, formatter, allBindings);
},
update: function(element, valueAccessor, allBindings) {
var source = valueAccessor();
var formatter = function() {
return ko.computed({
read: function() { return source(); },
write: function(newValue) {
source(newValue.replace(/\D+/g, "")
.replace(/^[01]/, "")
.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3")
.substring(0, 12));
}
});
};
ko.bindingHandlers.value.update(element, formatter, allBindings);
}
};
Upvotes: 0
Views: 134
Reputation: 803
I am not sure why this works but the custom binding did not. :(
HTML:
<input data-bind="value: Phone" />
KO Observable:
self.Phone = ko.observable(model.Phone ? model.Phone : "").trimmed();
KO Subscribable:
ko.subscribable.fn.trimmed = function () {
return ko.computed({
read: function () {
return this().replace(/\D+/g, "")
.replace(/^[01]/, "")
.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3")
.substring(0, 12);
},
write: function (value) {
this(value.replace(/\D+/g, "")
.replace(/^[01]/, "")
.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3")
.substring(0, 12));
this.valueHasMutated();
},
owner: this
}).extend({ notify: 'always' });
};
Upvotes: 0
Reputation: 23372
It feels like the read
and write
should be swapped around...
I'd say you want your "source" data to be just the numbers. The formatted data is mainly for display purposes.
I'd expect a computed
layer added in your binding, that does two things:
source
I'm not sure if I broke stuff that you had working previously, but it could be something like:
ko.bindingHandlers.formatPhone = {
init: function (element, valueAccessor, allBindings) {
var source = valueAccessor();
var formatter = ko.computed({
write: function(newValue) {
source(newValue.split("-").join(""));
},
read: function() {
return source().replace(/\D+/g, "")
.replace(/^[01]/, "")
.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3")
.substring(0, 12);
}
});
ko.bindingHandlers.value.init(element, function() { return formatter; }, allBindings);
}
};
var vm = { phoneNr: ko.observable("3456546512") };
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
Via custom binding:
<input data-bind="formatPhone: phoneNr">
<br/>
Mimic external update:
<input data-bind="textInput: phoneNr">
Upvotes: 1