Angularjs dynamic ng-pattern validation

AngularjsAngularjs DirectiveAngularjs Ng-Change

Angularjs Problem Overview


I have a form that if a checkbox is false enforces validation on a text input using the ng-required directive. If the checkbox is true, the field is hidden and the ng-required is set to false.

The problem is that I also have a regex for validation specified on the input as well utilizing the ng-pattern angular directive. The issue I am running into is that if a user fills in an invalid phone number, checks the box to deactivate that input (and consequently needs no further validation) the form will not allow submission as it is invalid based on the ng-pattern.

I attempted to resolve this issue by adding an ng-change function to set the input model to null, however the ng-pattern and thus the field is still set to invalid on the initial set of the checkbox to false. If I however uncheck the box, setting everything back to initial form load, then check the box again, the form is valid and able to submit. I am not sure what I am missing. Here is the ng-change code I have thus far:

    var phoneNumberRegex = /^\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})$/;
    $scope.phoneNumberPattern = phoneNumberRegex;
    $scope.removeValidation = function() {
        if ($scope.cell._newUser === false) {
            $scope.request._number = '';
            $scope.phoneNumberPattern = /[0-9a-zA-Z]?/;
        } else {
            $scope.phoneNumberPattern = phoneNumberRegex;
        }
    };

Angularjs Solutions


Solution 1 - Angularjs

This is an interesting problem, complex Angular validation. The following fiddle implements what you want:

http://jsfiddle.net/2G8gA/1/

Details

I created a new directive, rpattern, that is a mix of Angular's ng-required and the ng-pattern code from input[type=text]. What it does is watch the required attribute of the field and take that into account when validating with regexp, i.e. if not required mark field as valid-pattern.

Notes

  • Most of the code is from Angular, tailored to the needs of this.
  • When the checkbox is checked, the field is required.
  • The field is not hidden when the required checkbox is false.
  • The regular expression is simplified for the demo (valid is 3 digits).

A dirty (but smaller) solution, if you do not want a new directive, would be something like:

$scope.phoneNumberPattern = (function() {
    var regexp = /^\(?(\d{3})\)?[ .-]?(\d{3})[ .-]?(\d{4})$/;
    return {
        test: function(value) {
            if( $scope.requireTel === false ) {
                return true;
            }
            return regexp.test(value);
        }
    };
})();

And in HTML no changes would be required:

<input type="text" ng-model="..." ng-required="requireTel"
    ng-pattern="phoneNumberPattern" />

This actually tricks angular into calling our test() method, instead of RegExp.test(), that takes the required into account.

Solution 2 - Angularjs

Not taking anything away from Nikos' awesome answer, perhaps you can do this more simply:

<form name="telForm">
  <input name="cb" type='checkbox' data-ng-modal='requireTel'>
  <input name="tel" type="text" ng-model="..." ng-if='requireTel' ng-pattern="phoneNumberPattern" required/>
  <button type="submit" ng-disabled="telForm.$invalid || telForm.$pristine">Submit</button>
</form>

Pay attention to the second input: We can use an ng-if to control rendering and validation in forms. If the requireTel variable is unset, the second input would not only be hidden, but not rendered at all, thus the form will pass validation and the button will become enabled, and you'll get what you need.

Solution 3 - Angularjs

Used pattern :

 ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"

Used reference file:

 '<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>'

Example for Input:

 <input type="number" require ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"><input type="submit">

Solution 4 - Angularjs

 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"></script>

<input type="number" require ng-pattern="/^\d{0,9}(\.\d{1,9})?$/"><input type="submit">

Solution 5 - Angularjs

I just ran into this the other day.

What I did, which seems easier than the above, is to set the pattern on a variable on the scope and refer to it in ng-pattern in the view.

When "the checkbox is unchecked" I simply set the regex value to /.*/ on the onChanged callback (if going to unchecked). ng-pattern picks that change up and says "OK, your value is fine". Form is now valid. I would also remove the bad data from the field so you don't have an apparent bad phone # sitting there.

I had additional issues around ng-required, and did the same thing. Worked like a charm.

Solution 6 - Angularjs

> Sets pattern validation error key if the ngModel $viewValue does not match a RegExp found by evaluating the Angular expression given in the attribute value. If the expression evaluates to a RegExp object, then this is used directly. If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it in ^ and $ characters.

It seems that a most voted answer in this question should be updated, because when i try it, it does not apply test function and validation not working.

Example from Angular docs works good for me:

Modifying built-in validators

html

<form name="form" class="css-form" novalidate>
  <div>
   Overwritten Email:
   <input type="email" ng-model="myEmail" overwrite-email name="overwrittenEmail" />
   <span ng-show="form.overwrittenEmail.$error.email">This email format is invalid!</span><br>
   Model: {{myEmail}}
  </div>
</form>

js

var app = angular.module('form-example-modify-validators', []);

app.directive('overwriteEmail', function() {
    var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@example\.com$/i;

    return {
        require: 'ngModel',
        restrict: '',
        link: function(scope, elm, attrs, ctrl) {
            // only apply the validator if ngModel is present and Angular has added the email validator
            if (ctrl && ctrl.$validators.email) {

                // this will overwrite the default Angular email validator
                ctrl.$validators.email = function(modelValue) {
                    return ctrl.$isEmpty(modelValue) || EMAIL_REGEXP.test(modelValue);
                };
             }
         }
     };
 });

Plunker

Solution 7 - Angularjs

You can use site https://regex101.com/ for building your own specific pattern for some country:

For example, Poland:

-pattern = xxxxxxxxx OR xxx-xxx-xxx OR xxx xxx xxx 
-regexp ="^\d{9}|^\d{3}-\d{3}-\d{3}|^\d{3}\s\d{3}\s\d{3}"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBrianView Question on Stackoverflow
Solution 1 - AngularjsNikos ParaskevopoulosView Answer on Stackoverflow
Solution 2 - AngularjskumarharshView Answer on Stackoverflow
Solution 3 - AngularjsAnil SinghView Answer on Stackoverflow
Solution 4 - AngularjsJijo PauloseView Answer on Stackoverflow
Solution 5 - AngularjsjessewolfeView Answer on Stackoverflow
Solution 6 - AngularjsfedorshishiView Answer on Stackoverflow
Solution 7 - AngularjsLeptonView Answer on Stackoverflow