Angularjs dynamic ng-pattern validation
Asked Answered
S

7

83

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;
        }
    };
Shadowy answered 19/9, 2013 at 16:33 Comment(0)
K
176

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.

Koby answered 24/9, 2013 at 14:48 Comment(5)
I would be very interested to know what the round brackets surrounding and after the function do. It doesn't work without themTony
They call the function directly, effectively placing its return value to the variable. It is very common in Javascript in order not to pollute an outer scope with implementation details of a specific component, e.g. look at the module pattern.Koby
The dirty solution, to play a trick on AngularJS based on JS's "ducktyping" is the the most awesome code I saw today. Really clever solution!Dishabille
Thanks for the awesome code, but still not very clear, 1) according to docs.angularjs.org/api/ng/directive/input, "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", but I suppose phoneNumberPattern is an object when return? Is this supposed to be a Regxp Obj? 2) And in the function(value) {} store in test, where does the parameter "value" come from? Appreciate for any replies!Weigle
1) The "If the expression evaluates to RegExp" clause is implemented by Angular by checking whether the thing it gets has a test method. That is why we give it an object with our own custom test(), essentially emulating a native JS RegExp. 2) The value is of course passed by Angular when executing the actual validity check.Koby
J
25

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.

Jinks answered 23/7, 2014 at 8:30 Comment(2)
The upside, it's simple. The downside, any inputs that get removed when the ng-if expression is false will have any text in them removed if ng-if is then evaluated to true later. Still, +1 for simplicity.Carraway
I wanted to add, if you go this route, make sure you understand that everything within the ng-if (if you put it on a div for example) will now have it's own scope.Carraway
V
5

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">
Vyky answered 29/10, 2014 at 17:37 Comment(0)
G
2

 <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">
Goldie answered 8/5, 2015 at 6:7 Comment(0)
S
2

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.

Superfine answered 3/2, 2017 at 18:29 Comment(0)
G
0

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

Geber answered 26/12, 2015 at 19:20 Comment(0)
A
-1

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}"
Athenian answered 30/9, 2015 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.