Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS form validation directive for showing input errors

I need to create a validation directive for showing all input errors for each input automatically. This validation directive should show all errors at current moment and list of errors should be updated automatically while user is typing.

I need to show all errors for input if input is dirty, not empty and invalid. I need to add all errors into html element near this input element.

For example if input have type="email" and ng-minlength="5" and user typed 'abc' I need to show such errors near this input: 'Invalid email; Please enter at least 5 characters;'

For example if input has type="number" attr and min="200" and min-model="minnumber" and minnumber model set to '300' and user typed '100' I need to show such errors near this input: 'Please enter the minimum number of 500; Should be greater than Min Number;'

Also I need to update all errors messages for input in prev example if related model (min-model param) is updated.

var app = angular.module('app', []);

app.controller('appCtrl', function ($scope) {

});

app.directive('validate', function () {
    return {
        restrict: 'A',
        require: 'ngModel', // require:  '^form',

        link: function (scope, element, attrs, ctrl) {
            console.log('======================');
            console.log(scope);
            console.log(element);
            console.log(attrs);
            console.log(ctrl);
            console.log(scope.form.$error);
            angular.forEach(scope.form.$error, function (value, key) {
                console.log('scope.form.$error = ' + key + ': ' + value);
                console.log(value);
            });

        }
    };
});


app.directive('positiveInteger', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var INTEGER_REGEXP = /^\d+$/;
                if (INTEGER_REGEXP.test(viewValue)) { // it is valid
                    ctrl.$setValidity('positiveInteger', true);
                    return viewValue;
                } else { // it is invalid, return undefined (no model update)
                    ctrl.$setValidity('positiveInteger', false);
                    return undefined;
                }
            });
        }
    };
});


app.directive('positiveFloat', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var FLOAT_REGEXP = /^(?:[1-9]\d*|0)?(?:\.\d+)?$/;
                if (FLOAT_REGEXP.test(viewValue)) { // it is valid
                    ctrl.$setValidity('positiveInteger', true);
                    return viewValue;
                } else { // it is invalid, return undefined (no model update)
                    ctrl.$setValidity('positiveInteger', false);
                    return undefined;
                }
            });
        }
    };
});


app.directive('minModel', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                if (viewValue > scope[attrs.minModel]) { // it is valid
                    ctrl.$setValidity('minModel', true);
                    return viewValue;
                } else { // it is invalid, return undefined (no model update)
                    ctrl.$setValidity('minModel', false);
                    return undefined;
                }
            });

        }
    };
});

Can you help to make this validation directive?

Or maybe can you point me into the right direction?

Link to JSFiddle with some code for testing.

P.S. Something similar is made with UI-Utils but their directive does not give ability to set similar error messages in one place.

like image 667
webvitaly Avatar asked Aug 13 '14 04:08

webvitaly


People also ask

How do you show errors in AngularJS?

errors = []; $scope. hasError = false; $scope.

What is used for validating AngularJS forms?

Form Validation AngularJS also holds information about whether they have been touched, or modified, or not. You can use standard HTML5 attributes to validate input, or you can make your own validation functions. Client-side validation cannot alone secure user input. Server side validation is also necessary.

What is $setValidity in AngularJS?

$setValidity(validationErrorKey, isValid); Change the validity state, and notify the form. This method can be called within $parsers/$formatters or a custom validation implementation. However, in most cases it should be sufficient to use the ngModel. $validators and ngModel.


1 Answers

Here is the pattern that I used (with Angular 1.3):

app.directive('number', function() {
  var NUMBER_REGEXP = /^(\d+)$/;
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$validators.number = function(modelValue, viewValue) {
        return NUMBER_REGEXP.test(viewValue);
      };
    }
  };
});

Then I was able to check for errors in the HTML (with Bootstrap 3.3) using this pattern:

<form name="form">
  <div class="form-group"
       ng-class="{'has-error': form.value.$dirty && form.value.$error.number}">
    <label for="id_value" class="control-label">Value:</label>
    <div>
      <input type="text" class="form-control" id="id_value" name="value"
             ng-model="model.number" number>
      <p class="help-block" ng-if="form.value.$error.number">Please enter a number</p>
    </div>
  </div>
</form>

Explanation:

The number attribute in the <input name="value"> tag triggers the directive, which causes the ngModel validator 'number' to be called and to set/unset value.$error.number.

If value.$error.number is set, then the has-error class is applied to the form-group so it displays a red input field and the help message is displayed.

like image 78
Brent Washburne Avatar answered Sep 19 '22 11:09

Brent Washburne