0

I'm building an ASP.NET MVC 5 app using jQuery Maskedinput plugin for phone numbers. However, the plugin causes the length validator to trigger.

Here's the C# property definition:

[StringLength(10)]
[DataType(DataType.PhoneNumber)]
public string Phone_Nbr { get; set; }

And here's the form field in the view:

<div class="form-group-sm clearfix">
    @Html.LabelFor(m => m.CustomerVm.Phone_Nbr, 
        htmlAttributes: new { @class = "control-label col-xs-5 col-md-5" })
    <div class="col-xs-2 col-md-2">
        @Html.EditorFor(m => m.CustomerVm.Phone_Nbr, 
            new { htmlAttributes = new { @class = "form-control phone" } })
        @Html.ValidationMessageFor(m => m.CustomerVm.Phone_Nbr, "", 
            new { @class = "text-danger" })
    </div>
</div>

And here's the HTML produced by the above Razor code:

<div class="form-group-sm clearfix">
    <label class="control-label col-xs-5 col-md-5" for="CustomerVm_Phone_Nbr">Phone #</label>
    <div class="col-xs-2 col-md-2">
        <input class="form-control phone text-box single-line input-validation-error" 
            data-val="true" data-val-length="The field Phone # must be a string with a maximum length of 10." 
            data-val-length-max="10" id="CustomerVm_Phone_Nbr" name="CustomerVm.Phone_Nbr" 
            type="tel" value="9103440700" aria-describedby="CustomerVm_Phone_Nbr-error" 
            aria-invalid="true">
        <span class="text-danger field-validation-error" data-valmsg-for="CustomerVm.Phone_Nbr" 
            data-valmsg-replace="true">
            <span id="CustomerVm_Phone_Nbr-error" class="">The field Phone # must be a string with a maximum length of 10.</span>
        </span>
    </div>
</div>

Using guidance from this answer, I wrote the mask and the "unmask" in this fashion and added them to the document ready function of _Layout.cshtml, so all views have access to them:

$('.phone')
    .mask('999-999-9999')
    .on('blur', function () {
        var frm = $(this).parents("form");
        // if form has a validator
        if ($.data(frm[0], 'validator')) {
            var validator = $(this).parents("form").validate();
            validator.settings.onfocusout.apply(validator, [this]);
        }
    });

// Unmask the phone number on-submit of the form.
$('form').submit(function () {
    $('.phone').each(function (index, element) {
        var value = $(element).val().replace(/-/g, '');
        $(element).val(value);
    });
});

Any form element that has the .phone class will have the mask applied. The blur handler was meant to prevent unnecessary validation when we clear a required field of the phone number; note that this field isn't required but has a max length of 10 requirement. When I try to submit, I get this validation error client side:

enter image description here

I was hoping that the "unmask" above, which, on submit of the form, removes all the dashes from the elements with class .phone, would prevent this issue. In fact, before the value goes to the database, the dashes need to be removed so it's just pure digits.

What am I doing wrong? Thanks.

Alex
  • 34,699
  • 13
  • 75
  • 158

1 Answers1

0

The solution was to create a custom validator, with some help from this post.

optionalPhoneValidator: function() {
    $.validator.addMethod('optionalPhone', function(value, element) {
        return this.optional(element) || pims.utilities.isNormalInteger(element.value);
    }, 'Please enter a string with a maximum length of 10.');
}

We call the above from the document ready of _Layout.cshtml. In addition, we throw the following into the same document ready:

$('.phone-optional').validate({ onkeyup: false });
$('.phone-optional').each(function () {
    $(this).rules('add', {
        optionalPhone: {
            depends: function () {
                $(this).val($(this).val().replace(/-/g, ''));
                return true;
            }
        }
    });
});

Note that in the depends function, we clean the phone number of any dashes. Also, the above code must come before the following, or the field won't get the mask.

The mask method was updated in the same file:

$('.phone, .phone-optional')
    .mask('999-999-9999')
    .on('blur', function () {
        var frm = $(this).parents("form");
        // if form has a validator
        if ($.data(frm[0], 'validator')) {
            var validator = $(this).parents("form").validate();
            validator.settings.onfocusout.apply(validator, [this]);
        }
    });

We then just throw .phone-optional class on a phone number field and we're good to go.

Alex
  • 34,699
  • 13
  • 75
  • 158
  • 1
    Much of that is hocus-pocus black magic, so congrats on working it out. A few things will tidy up; (1) `$(this).val()` is an expensive way to say `this.value`; (2) `$(this).parents("form");` will simplify to `$(this.form);`, and doesn't need to appear twice (use the assigned reference); (3) `if($.data(frm[0], 'validator'))` is a rather arcane way of saying `if(frm.data('validator'))`. – Roamer-1888 Jul 20 '17 at 22:16