cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - Activehtmloctocatspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <div class="container" ng-app="app">
    <h1>AngularJS validation and error handling</h1>
    <!--Example 1 - ugly manual validation-->
    <div class="panel panel-primary"
         ng-controller="badCtrl as $ctrl">
        <section class="panel-heading">
            <h2 class="panel-title">Ugly manual validation</h2>
        </section>
        <section class="panel-body">
            <div class="row">
                <div class="col-xs-6">
                    <div class="form-group"
                         ng-class="{'has-error': $ctrl.nameError}">
                        <label for="bad_name">Name:</label>
                        <input id="bad_name" type="text" class="form-control" placeholder="required, latin letters only"
                               ng-model="$ctrl.name"
                               ng-focus="$ctrl.nameError = false"
                               ng-blur="$ctrl.validateName()">
                    </div>
                    <ul class="list-group">
                        <li class="list-group-item">nameError = {{$ctrl.nameError}}</li>
                    </ul>
                </div>
                <div class="col-xs-6">
                    <div class="form-group"
                         ng-class="{'has-error': $ctrl.passError}">
                        <label for="bad_pass">Password:</label>
                        <input id="bad_pass" type="password" class="form-control" placeholder="required, longer than 4 chars"
                               ng-model="$ctrl.pass"
                               ng-focus="$ctrl.passError = false"
                               ng-blur="$ctrl.validatePass()">                               
                    </div>
                    <ul class="list-group">
                        <li class="list-group-item">passError = {{$ctrl.passError}}</li>
                    </ul>
                </div>
            </div>
            <button class="btn btn-primary pull-right"
                    ng-click="$ctrl.submit()">
                Submit
            </button>
        </section>
    </div>
    <!--Example 2 - built-in AngularJS validation tools-->
    <form name="form2" class="panel panel-primary" novalidate
          ng-controller="builtinValidationCtrl as $ctrl"
          ng-submit="$ctrl.submit()">
        <section class="panel-heading">
            <h2 class="panel-title">Validation with built-in AngularJS tools</h2>
        </section>
        <section class="panel-body">
            <ul class="list-group">
                <li class="list-group-item">form $dirty = {{form2.$dirty}}</li>
                <li class="list-group-item">form $valid = {{form2.$valid}}</li>
                <li class="list-group-item">form $submitted = {{form2.$submitted}}</li>
            </ul>
            <div class="row">
                <div class="col-xs-6">
                    <div class="form-group"
                         ng-class="{'has-error': form2.name.$touched && form2.name.$invalid}">
                        <label for="name2">Name:</label>
                        <input id="name2" name="name" type="text" class="form-control" placeholder="required, latin letters only"
                               ng-model="$ctrl.name"
                               required
                               ng-pattern="/^[a-zA-Z]+$/">
                    </div>
                    <ul class="list-group">
                        <li class="list-group-item">name $touched = {{form2.name.$touched}}</li>
                        <li class="list-group-item">name $dirty = {{form2.name.$dirty}}</li>
                        <li class="list-group-item">name $valid = {{form2.name.$valid}}</li>
                    </ul>
                </div>
                <div class="col-xs-6">
                    <div class="form-group"
                         ng-class="{'has-error': form2.pass.$touched && form2.pass.$invalid}">
                        <label for="pass2">Password:</label>
                        <input id="pass2" name="pass" type="password" class="form-control" placeholder="required, longer than 4 chars"
                               ng-model="$ctrl.pass"
                               required
                               ng-minlength="4">
                    </div>
                    <ul class="list-group">
                        <li class="list-group-item">password $touched = {{form2.pass.$touched}}</li>
                        <li class="list-group-item">password $dirty = {{form2.pass.$dirty}}</li>
                        <li class="list-group-item">password $valid = {{form2.pass.$valid}}</li>
                    </ul>
                </div>
            </div>
            <button type="submit" class="btn btn-primary pull-right"
                    ng-disabled="form2.$invalid">
                Submit
            </button>
        </section>
    </form>
    <!--Example 3 - custom validator directive-->
    <form name="form3" class="panel panel-primary" novalidate
          ng-controller="customValidatorCtrl as $ctrl"
          ng-submit="$ctrl.submit()">
        <section class="panel-heading">
            <h2 class="panel-title">Validation with custom validator directive</h2>
        </section>
        <section class="panel-body">
            <div class="row">
                <div class="col-xs-12">
                    <div class="form-group"
                         ng-class="{'has-error': form3.name.$touched && form3.name.$invalid}">
                        <label for="name3">Name:</label>
                        <input id="name3" name="name" type="text" class="form-control" placeholder="required, all names except Barack and Donald"
                               ng-model="$ctrl.name"
                               required
                               ng-pattern="/^[a-zA-Z]+$/"
                               name-black-list>
                    </div>
                </div>
            </div>
            <button type="submit" class="btn btn-primary pull-right"
                    ng-disabled="form3.$invalid">
                Submit
            </button>
        </section>
    </form>
    <!--Example 4 - one-occasion custom validation directive that takes verification function-->
    <form name="form4" class="panel panel-primary" novalidate
          ng-controller="oneOccasionFunctionDirectiveCtrl as $ctrl"
          ng-submit="$ctrl.submit()">
        <section class="panel-heading">
            <h2 class="panel-title">One-occasion custom validation with directive that takes verification function</h2>
        </section>
        <section class="panel-body">
            <div class="row">
                <div class="col-xs-12">
                    <div class="form-group"
                         ng-class="{'has-error': form4.name.$touched && form4.name.$invalid}">
                        <label for="name4">Name:</label>
                        <input id="name4" name="name" type="text" class="form-control" placeholder="required, all names except Arnold and Sylvester"
                               ng-model="$ctrl.name"
                               required
                               ng-pattern="/^[a-zA-Z]+$/"
                               custom-validation-function="$ctrl.validationFunc($value)">
                    </div>
                </div>
            </div>
            <button type="submit" class="btn btn-primary pull-right"
                    ng-disabled="form4.$invalid">
                Submit
            </button>
        </section>
    </form>
    <!--Example 5 - one-occasion custom validation with `ui-validate`-->
    <form name="form5" class="panel panel-primary" novalidate
          ng-controller="oneOccasionFunctionDirectiveCtrl as $ctrl"
          ng-submit="$ctrl.submit()">
        <section class="panel-heading">
            <h2 class="panel-title">One-occasion validation with `ui-validate`</h2>
        </section>
        <section class="panel-body">
            <div class="row">
                <div class="col-xs-12">
                    <div class="form-group"
                         ng-class="{'has-error': form5.name.$touched && form5.name.$invalid}">
                        <label for="name5">Name:</label>
                        <input id="name5" name="name" type="text" class="form-control" placeholder="required, all names except Arnold and Sylvester"
                               ng-model="$ctrl.name"
                               required
                               ng-pattern="/^[a-zA-Z]+$/"
                               ui-validate="'$ctrl.validationFunc($value)'">
                    </div>
                </div>
            </div>
            <button type="submit" class="btn btn-primary pull-right"
                    ng-disabled="form5.$invalid">
                Submit
            </button>
        </section>
    </form>
    <!--Example 6 - handling back-end validation errors-->
    <form name="$ctrl.form6" class="panel panel-primary" novalidate
          server-validation
          ng-controller="backendValidationCtrl as $ctrl"
          ng-submit="$ctrl.submit()">
        <section class="panel-heading">
            <h2 class="panel-title">Handling back-end validation errors + `ng-messages`</h2>
        </section>
        <section class="panel-body">
            <div class="row">
                <div class="col-xs-12">
                    <div class="form-group"
                         ng-class="{'has-error': $ctrl.form6.name_field.$invalid && ($ctrl.form6.name_field.$touched || $ctrl.form6.$submitted) }">
                        <label for="name6">Name:</label>
                        <ng-messages role="alert"
                                     ng-if="$ctrl.form6.name_field.$touched || $ctrl.form6.$submitted"
                                     for="$ctrl.form6.name_field.$error">
                            <span ng-message="serverError" class="label label-danger">{{$ctrl.serverErrorMessages.name_field[0]}}</span>
                            <span ng-message="pattern" class="label label-danger">Please use letters only</span>
                            <span ng-message="required" class="label label-danger">This field is equired</span>
                        </ng-messages>
                        <input id="name6" name="name_field" type="text" class="form-control" placeholder="required, back-end app accepts `Guido` name only"
                               ng-model="$ctrl.name_field"
                               required
                               ng-pattern="/^[a-zA-Z]+$/">
                    </div>
                </div>
            </div>
            <button type="submit" class="btn btn-primary pull-right">
                Submit
            </button>
        </section>
    </form>
</div>


            
          
!
            
              (() => {
    'use strict';

    const app = angular.module('app', [
        'ngMessages',
        'ui.validate'
    ]);

    /**
     * Example 1 - ugly manual validation
     */
    class BadController {
        constructor() {
            this.nameError = false;
            this.passError = false;
        }
        validateName() {
            // Not empty and letters only
            this.nameError = !this.name || !/^[a-zA-Z]+$/.test(this.name);
        }
        validatePass() {
            // At least 4 characters
            this.passError = !this.pass || this.pass.length < 4;
        }
        submit() {
            this.validateName();
            this.validatePass();
            if (this.nameError || this.passError) {
                return;
            }
            alert(`Submit ${this.name}/${this.pass}`);
        }
    }

    /**
     * Example 2 - built-in AngularJS validation tools
     */
    class BuiltinValidationController {
        submit() {
            alert(`Submit ${this.name}/${this.pass}`);
        }
    }

    /**
     * Example 3 - reusable custom validation directive
     */
    app.directive('nameBlackList', () => {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: (scope, element, attrs, controller) => {
                controller.$validators.forbiddenName = value => {
                    if (!value) {
                        return true;
                    }
                    const lowVal = String(value).toLowerCase();
                    return lowVal.indexOf('barack') === -1 && lowVal.indexOf('donald') === -1;
                };
            }
        };
    });

    class CustomValidatorController {
        submit() {
            alert(`Submit ${this.name}`);
        }
    }

    /**
     * Examples 4, 5 - one-occasion custom validation directive that takes verification function and the same results with `ui-validate`
     */
    app.directive('customValidationFunction', () => {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                valFunc: '&customValidationFunction'
            },
            link: (scope, element, attrs, controller) => {
                const normalizedFunc = (modelValue, viewValue) => {
                    const $value = modelValue || viewValue;
                    return scope.valFunc({
                        $value
                    });
                };
                controller.$validators.customValidationFunction = normalizedFunc;
            }
        };
    });

    class OneOccasionFunctionDirectiveController {
        submit() {
            alert(`Submit ${this.name}`);
        }

        validationFunc($value) {
            if (!$value) {
                return true;
            }
            const lowVal = String($value).toLowerCase();
            return lowVal.indexOf('arnold') === -1 && lowVal.indexOf('sylvester') === -1;
        }
    }

    /**
     * Example 6 - handling back-end validation errors
     */
    class backendValidationCtrl {
        static get $inject() {
            return [
                'backendServiceMocker',
                '$q'
            ];    
        }
        
        constructor(backendServiceMocker, $q) {
            this.backendServiceMocker = backendServiceMocker;
            this.$q = $q;
        }
        
        submit() {
            if (this.form6.$invalid) {
                return this.$q.reject();
            }
            return this.backendServiceMocker.postTheObjectWithName({
                name_field: this.name_field
            })
                .then(() => alert(`Successfully submited ${this.name_field}`))
                .catch(errorResponse => {
                    this.serverErrorMessages = errorResponse.data;
                    this.backendServiceMocker.serverErrorsHelper(errorResponse, this.form6);           
                });
        }
    }
    
    class backendServiceMocker {
        static get $inject() {
            return [
                '$http',
                '$q'
            ];
        }
        
        constructor($http, $q) {
            this.$http = $http;
            this.$q = $q;
        }
        
        postTheObjectWithName(obj) {
            // $http request would go here in real life
            if (!obj || !obj.name_field || String(obj.name_field).toLowerCase() !== 'guido') {
                return this.$q.reject({
                    status: 400,
                    data: {
                        name_field: ['Guido is the real name only']
                    }
                });
            }
            return this.$q.resolve({
                status: 201
            });
        }
        
        /**
         * Usually this goes to dedicated helpers service
         */        
        serverErrorsHelper(errorResponse, formController) {                        
            switch (errorResponse.status) {
                case 400:
                    if (errorResponse.data.non_field_errors) {
                        formController.$setValidity('serverError', false);
                        formController.$setSubmitted();
                    }
                    Object.getOwnPropertyNames(errorResponse.data).forEach(field => {
                        // Check if form contains defined in error response control
                        // And if control has ng-model                        
                        if (field !== 'non_field_errors' &&
                            angular.isObject(formController[field]) &&
                            formController[field].hasOwnProperty('$modelValue')) {
                            formController[field].$setValidity('serverError', false);
                            formController[field].$setTouched();
                        } else {
                            console.error(`Unknown server error ${field}.`);
                        }
                    });
                    break;
                default:
                    console.error('Unknown server error response.');
                    return;
            }
        }
    }

    app.directive('serverValidation', () => {
        return {
            restrict: 'A',
            require: 'form',
            link: (scope, element, attrs, formController) => {
                const resetServerValidity = fieldController => {
                    const storedFieldController = fieldController;
                    return () => {
                        storedFieldController.$setValidity('serverError', true);
                        formController.$setValidity('serverError', true);
                        return true;
                    };
                };
                scope.$watchCollection(() => formController, updatedFormController => {                    
                    Object.getOwnPropertyNames(updatedFormController).forEach(field => {
                        // Search for form controls with ng-model controllers
                        // Which do not have attached server error resetter validator
                        if (angular.isObject(updatedFormController[field]) &&
                            updatedFormController[field].hasOwnProperty('$modelValue') &&
                            angular.isObject(updatedFormController[field].$validators) &&
                            !updatedFormController[field].$validators.hasOwnProperty('serverValidityResetter')) {
                            updatedFormController[field].$validators.serverValidityResetter = resetServerValidity(updatedFormController[field]);
                        }
                    });
                });
            }
        };
    });    
    
    app.controller('badCtrl', BadController);
    app.controller('builtinValidationCtrl', BuiltinValidationController);
    app.controller('customValidatorCtrl', CustomValidatorController);
    app.controller('oneOccasionFunctionDirectiveCtrl', OneOccasionFunctionDirectiveController);
    app.controller('backendValidationCtrl', backendValidationCtrl);

    app.service('backendServiceMocker', backendServiceMocker);
})();
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console