JSFiddle - React, Tailwind, and code Playground

HTML

<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<div ng-app = 'app'>
    <div ng-controller='myCtrl'>
        <form name="form" ng-submit="doSomething()" novalidate>
            <input type="text" name="phone" ng-model="phoneNumber" phone-number ng-/>
            <p ng-show="form.phone.$invalid">[error] Wrong phone number</p>
            <p ng-show="form.phone.$invalid && form.phone.$hasVisited">[error && visited] Wrong phone number</p>
            <p ng-show="form.phone.$invalid && !form.phone.$hasFocus">[error && blur] Wrong phone number</p>
            <div><input type="submit" value="Submit"/></div>
        </form>
        <div> {{ result }} </div>
    </div>    
</div>

JavaScript

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

app.controller('myCtrl', function($scope) {
    $scope.result = 'No result';
    $scope.phoneNumber = '';
    $scope.doSomething = function () {
       if ($scope.form.$invalid) return;
       $scope.result = $scope.phoneNumber;
    }
})

app.directive('phoneNumber', function () {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, element, attrs, ctrl) { 
            if (!ctrl) return;
            ctrl.$parsers.unshift(function(viewValue) {
                var valid = /^\d*$/.test(viewValue);
                ctrl.$setValidity('phoneNumber', valid);
                return viewValue;
            });
            ctrl.$formatters.unshift(function(modelValue) {
                var valid = /^\d*$/.test(modelValue);
                ctrl.$setValidity('phoneNumber', valid);
                return modelValue;
            });
            
        }
    }
});

var focusDirective = function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    link: function (scope, element, attrs, ctrl) {
      var elm = $(element);
      if (!ctrl) return;
      elm.on('focus', function () {
        elm.addClass('has-focus');
        ctrl.$hasFocus = true;
        if(!scope.$$phase) scope.$digest();
      });
      elm.on('blur', function () {
        elm.removeClass('has-focus');
        elm.addClass('has-visited');
        ctrl.$hasFocus = false;
        ctrl.$hasVisited = true;
        if(!scope.$$phase) scope.$digest();
      });
      elm.closest('form').on('submit', function () {
        elm.addClass('has-visited');
        ctrl.$hasFocus = false;
        ctrl.$hasVisited = true;
        if(!scope.$$phase) scope.$digest();
      })
    }
  }   
};
app.directive('input', focusDirective);