KnockoutJS binding messes with input mask

2019-04-16 04:42发布

问题:

I'm trying to apply an input mask to a telephone field. It works until I apply KnockoutJS binding, which removes the mask until the field receives focus.

Does not work: http://jsfiddle.net/8r6fe/

$('[data-mask]').each(function () {
    console.log('mask');
    $this = $(this);
    var mask = $this.attr('data-mask') || 'error...', mask_placeholder = $this.attr('data-mask-placeholder') || 'X';

    $this.mask(mask, {
        placeholder: mask_placeholder
    });
})


var ViewModel = function() {
    this.firstName = ko.observable("");
    this.lastName = ko.observable("");
    this.phone = ko.observable("");

    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);

    this.firstName('John');
    this.lastName('Doe');
    this.phone('1231231234');
}; 
ko.applyBindings(new ViewModel());

Works: http://jsfiddle.net/gxhjn/

var ViewModel = function(first, last) {
    this.firstName = ko.observable(first);
    this.lastName = ko.observable(last);

    this.fullName = ko.computed(function() {
        // Knockout tracks dependencies automatically. It knows that fullName depends on firstName and lastName, because these get called when evaluating fullName.
        return this.firstName() + " " + this.lastName();
    }, this);
};

ko.applyBindings(new ViewModel("Planet", "Earth")); // This makes Knockout get to work

$('[data-mask]').each(function () {
    console.log('mask');
    $this = $(this);
    var mask = $this.attr('data-mask') || 'error...', mask_placeholder = $this.attr('data-mask-placeholder') || 'X';

    $this.mask(mask, {
        placeholder: mask_placeholder
    });
})

回答1:

I don't think this is a knockout issue, but an issue with the way the masked input plugin is designed: Your initial value must match the mask criteria. Even if you get rid of knockout and just use jQuery's val() function to set the value to "1231231234", you'll see the same behavior.

UPDATE

Sorry, missed your "works" link. I was going to suggest a custom binding handler in the first place. Looks like this is the way to go. It applies the masking after the text has been updated by knockout, and then updates the view model with the new, masked value (if that's what you want):

ko.bindingHandlers.maskedInput = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        ko.bindingHandlers.value.init(element, valueAccessor, allBindings, viewModel, bindingContext);
    },
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        ko.bindingHandlers.value.update(element, valueAccessor, allBindings, viewModel, bindingContext);
        $(element).mask(allBindings.get('mask'));
        valueAccessor()($(element).val());
    }
};

Here's your updated fiddle: http://jsfiddle.net/8r6fe/3/