JSFiddle - React, Tailwind, and code Playground

HTML

<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" validate-on-blur >
            <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>

JavaScript

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);
            });
        }
    };
});