I struggle with AngularJS's form validation. It is quite powerful but also pretty confusing for me. My biggest problem is that by default most examples show all errors on the page as soon as the user starts typing. As a user, that is scary and ugly.

Not being so great at AngularJS myself, I dug around and found a decent way to prevent AngularJS from performing validation on a field until AFTER the user has left the field. This way, the form shows the error after the user believes they are done with it.

But that led to a problem. Once the error was displayed, the user would go back to fix it. Since the validation wouldn't occur again until the the blur event occurs, the user isn't immediately informed that they fixed the problem. Again, this is not great for the user.

So, I now have the field not validate until the onblur event occurs. Once the error is shown, the validation is moved back to the onchange/keydown/input/blur combination it was before. As soon as the input error is corrected, the validation shows the update.

// Original form with default validation. Not good for user. See this Fiddle : http://jsfiddle.net/justbn/VNQgY/

<body ng-app="app">
    <div ng-controller="FormController">
        <form name="testValidation">
            <label for="firstName">First Name:</label>
            <input type="text" id="firstName" name="firstName" ng-model="firstName" ng-minlength="3" ng-maxlength="25" >
            <p class="error" ng-show="testValidation.firstName.$error.minlength">Please make the first name at least 3 characters long</p>
            <p class="error" ng-show="testValidation.firstName.$error.maxlength">Please limit the first name to no more than 25 characters</p>

            <button type="submit" ng-disabled="!testValidation.valid">Submit</button>
        </form>
    </div>
</body>

As I said, I did some digging and found a directive by Alexander Kohout, that applies validation only on blur. I made a small (and probably badly written) modification to it that applies generic validation again after the field has been blurred once.

// See this fiddle: http://jsfiddle.net/justbn/VNQgY/2/

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

app.controller = app.controller('FormController', ['$scope', function($scope) {
}]);

app.directive('validateOnBlur', function() {

    /**
     * After blur has occurred on a field, reapply change monitoring
     * @param scope
     * @param elm
     * @param attr
     * @param ngModelCtrl
     */
    var removeBlurMonitoring = function(scope, elm, attr, ngModelCtrl) {
        elm.unbind('blur');

        // Reapply regular monitoring for the field
        elm.bind('keydown input change blur', function() {
            scope.$apply(function() {
                ngModelCtrl.$setViewValue(elm.val());
            });
        });
    };

    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, elm, attr, ngModelCtrl) {
            var allowedTypes = ['text', 'email', 'password'];
            if (allowedTypes.indexOf(attr.type)  === -1) {
                return;
            }

            // Unbind onchange event so that validation will be triggerd onblur
            elm.unbind('input').unbind('keydown').unbind('change');

            // Set OnBlur event listener
            elm.bind('blur', function() {

                scope.$apply(function() {
                    ngModelCtrl.$setViewValue(elm.val());
                });

                removeBlurMonitoring(scope, elm, attr, ngModelCtrl);
            });
        }
    };
});

Now, the field doesn't validate until AFTER the user leaves the field. Of course, this means that the submit button won't enable until after the user tabs away on the last first in the form. That isn't so great for the user either.

Does anyone have a better solution?