AngularJS Form level validation

Allows to add form level validation to any form.

HTML

<div ng-app="myModule">
<h1>Form Validation Sample:Password and Repeat should be equals</h1>

    <form
        name="validatedForm"
        mw-validate="{ notMatch: 'password1 == password2' }"
        >
        <!-- Show errors -->
        <div ng-show="validatedForm.$invalid && ! validatedForm.$pristine" class="errors">
            <div ng-repeat="(errorType, fields) in validatedForm.$error">
                <p ng-repeat="field in fields">{{field.$name}} {{errorType}}</p>
            </div>
        </div>
    
        <!-- Form Fields -->       

        <p><label>Username: </label><input type="text" name="name" ng-model="name"
                required ng-minlength="5" ng-maxlength="20" size="20"/></p>
        <p><label>Password: </label><input type="text" name="password" ng-model="password1"/></p>
        <p><label>Repeat  : </label><input type="text" name="password2" ng-model="password2"/></p>
        <p><button ng-disabled="! validatedForm.$valid">Save</button></p>
        <pre>{{ validatedForm | json }}</pre>
    </form>
</div>

CSS

label {
    display: inline-block;
    width: 100px;
}


div.errors {
    background-color: #FFBBBB;
    margin: 10px;
    padding: 4px;
}

form {
    margin: 10px;
    padding: 5px;
    background-color: #EEEEFF;
}

form p {
    margin-top: 5px;
}

JavaScript

angular.module('myModule', []);

/**
 * Use: <form mw-validate="{ passwordMatch: 'password1 == password2' }" mw-name="form" >...</form>
 * mg-validate: Object with form validations. object key y used as error key. Value is an expression evaluated
 * for true or false. mw-name (or mw-name-exp) is used as control name.
 */
angular.module('myModule').directive('mwValidate', function($log) {
    var noop = function() {};

    var nullFormCtrl = {
        isNull: true,
        $addControl: noop,
        $removeControl: noop,
        $setValidity: noop,
        $setDirty: noop
    };
   
    $log.info('Initializing mw-validate');
    
    return {
        restrict: 'A',
        require: '^form', // Looks on parent also

        // The linking function will add behavior to the template
        // The fourth parameter is a NgModelController (http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController)
        link: function(scope, element, attrs, parentFormCtrl) {
            var modelCtrl = { $name: attrs.name || attrs.mwName },
                nameExp = attrs.mwNameExp,
                validateExpr = attrs.mwValidate;
            
            var $error = this.$error = {}; // keep invalid keys here

            parentFormCtrl = parentFormCtrl || nullFormCtrl ;
            
            $log.info('Creating controller for: ' + modelCtrl.$name );
            
            validateExpr = scope.$eval(validateExpr);

            if ( ! validateExpr) {
              return;
            }

            if (angular.isFunction(validateExpr)) {
              validateExpr = { validator: validateExpr };
            }

            // TODO Is necessary?
            parentFormCtrl.$addControl(modelCtrl);

            element.bind('$destroy', function() {
                parentFormCtrl.$removeControl(modelCtrl);
            });

            if ( nameExp ) {
                scope.$watch( nameExp, function( newValue ) {
                    modelCtrl.$name = newValue;
              ...