0

I have these inputs inside a form which I'm validating with angular's ng-pattern, ng-required and ng-model directives:

<div class="form-group">
  <div style="padding-left:0px;" ng-class="{'has-error':personalDataForm.phone.$invalid && personalDataForm.phone.$dirty}" class="col-md-6">
    <label>Teléfono</label>
    <input type="text" name="phone" ng-pattern="/[0-9]{3}-[0-9]{7}/" ng-required="true" ng-trim="true" ng-model="phone" class="form-control phoneRegexInput"/>
  </div>
  <div style="padding-left:0px;" ng-class="{'has-error':personalDataForm.mobile.$invalid && personalDataForm.mobile.$dirty}" class="col-md-6">
    <label>Celular</label>
    <input type="text" name="mobile" ng-pattern="/[0-9]{3}-[0-9]{7}/" ng-required="true" ng-trim="true" ng-model="mobile" class="form-control phoneRegexInput"/>
  </div>
</div>

So the ng-pattern directive will look for strings in the format of 7 digits prefixed by 3 digits and a hyphen, for example: 287-8719423.

As I don't want to force user to input the hyphen sign, I got a jQuery function which will put that hyphen when the input has completed the 3 required digits, this is my function:

$(".phoneRegexInput").keyup(function() {

    var phone = $(this).val();
    console.log(phone + " didnt match");
    if (phone.match(/\d{10}/)) {

        phone = phone.substr(0, 3) + "" + '-' + phone.substr(3);
        console.log(phone);
        $(this).val(phone);
    }

});

It's working properly and it actually changes the input value to a correctly formated one but it won't validate it as correct. If I delete and type again a correct input it will.

So I think is is caused because validation is fired from an actual user input, how can I make it listen from any change?

Related question.

I know this might be related to this question, but the problem is that the model won't change neither as it's not valid.

How should I fix it?

Workaround

I thought, "change the model", so I tried:

$(".phoneRegexInput").keyup(function() {

    var phone = $(this).val();
    console.log(phone + " didnt match");
    if (phone.match(/\d{10}/)) {

        phone = phone.substr(0, 3) + "" + '-' + phone.substr(3);
        console.log(phone);

        // Attempt to change model
        if ($scope.personalData)
            $scope.personalData.phone = phone;
        else {
            $scope.personalData = {
                phone: phone
            }
        }

        console.log($scope.personalData);
    }

});

But now something even more strange happens, I can see an actual object as $scope.personalData when loggin but if I try to debug by {{personalData}} at markup, it won't seem to have the property.

Nevermind, I have to make the validation background watch or listen to changes at the model.

Community
  • 1
  • 1
diegoaguilar
  • 8,179
  • 14
  • 80
  • 129

1 Answers1

1

I would suggest creating your own validation for phone numbers using a directive with a parser and formatter

'use strict';

angular
    .module('MyApp',[])
    .directive('input', inputTel);

var NANP_REGEXP = /^([0-9]{3})([0-9]{7})$/;
function inputTel() {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, elem, attr, ctrl) {
            if (ctrl && attr.type === 'tel') {
                ctrl.$parsers.push(function (viewValue) {
                    if(ctrl.$isEmpty(viewValue)){
                        ctrl.$setValidity('tel', true);
                        return viewValue;
                    }
                    var numbers = viewValue? viewValue.replace(/\D/g, "") : '';
                    if (NANP_REGEXP.test(numbers)) {
                        var formatted = numbers.replace(NANP_REGEXP, '$1-$2');
                        if (formatted !== viewValue) {
                            ctrl.$setViewValue(formatted);
                            ctrl.$render();
                        }
                        ctrl.$setValidity('tel', true);
                        return numbers.replace(NANP_REGEXP, '$1$2');
                    } else {
                        ctrl.$setValidity('tel', false);
                        return undefined;
                    }
                });

                ctrl.$formatters.push(function (viewValue) {
                    if(ctrl.$isEmpty(viewValue)){
                        ctrl.$setValidity('tel', true);
                        return viewValue;
                    }
                    var numbers = ctrl.$modelValue? ctrl.$modelValue.replace(/\D/g, "") : '';
                     if (NANP_REGEXP.test(numbers)) {
                        ctrl.$setValidity('tel', true);
                        return numbers.replace(NANP_REGEXP, '$1-$2');
                    } else {
                        ctrl.$setValidity('tel', false);
                        return undefined;
                    }
                });
            }
        }
    };
}

Demo http://jsfiddle.net/TheSharpieOne/qhphg0ax/1/

Note: I use the HTML5 input type=tel to help determine which inputs this validation applies to. Also this validation lets the user see the formatted input while keeping the internal value just the numbers.

This also exposes the custom validation error of 'tel' at formName.fieldName.$error.tel

TheSharpieOne
  • 25,646
  • 9
  • 66
  • 78
  • What would it happen with any browser not supporting `` attribute? – diegoaguilar Feb 18 '15 at 17:06
  • It would still trigger this directive to run on the element. Its just checking if the attribute is set to 'tel', not if the browser supports it or not. – TheSharpieOne Feb 18 '15 at 17:11
  • Thanks man, did you have any related solution/code at hand? That was a long code :D – diegoaguilar Feb 18 '15 at 17:13
  • And sorry for asking in this question but ... Why `ng-pattern='/[0-9]{2,3}/'` would allow numbers with more than 3 digits!? – diegoaguilar Feb 18 '15 at 17:14
  • i have match validation: https://github.com/TheSharpieOne/angular-input-match as for your pattren, it is just checking that exsits somewhere within the input. you would need start and end `/^\d{2,3}$/` – TheSharpieOne Feb 18 '15 at 17:17
  • I just thought, how can I customize or break this directive to pass in arguments for validation and even possible replace, all from markup? – diegoaguilar Feb 18 '15 at 17:26
  • You can make `NANP_REGEXP` be passed in, as well as a strings for the view value (`'$1-$2'`) and internal value (`'$1$2'`) so they are not hardcoded. Demo: http://jsfiddle.net/TheSharpieOne/qhphg0ax/2/ Look at the seond input. It has the format overridden. – TheSharpieOne Feb 18 '15 at 17:53