how-to-display-angular-form-validation-error-inside-angular-bootstrap-popover

how-to-display-angular-form-validation-error-inside-angular-bootstrap-popover

HTML

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="container-fluid">
  <div class="row">
    <div class="col-sm-6">
      <form name="myForm" ng-controller="MyFormController as vm" ng-submit="myForm.$valid && vm.onSubmit()" submit-notify="" novalidate>
        <div class="panel panel-primary">
          <div class="panel-heading">Form Validation with Popovers</div>
          <div class="panel-body">
            <div class="form-group">
              <label>First name</label>
              <input type="text" name="firstName" class="form-control" required ng-model="person.firstName" popover-validation="" />
            </div>
            <div class="form-group">
              <label>Surname</label>
              <input type="text" name="surname" class="form-control" required ng-model="person.surname" popover-validation="" />
            </div>
            <div class="form-group">
              <label>Email</label>
              <input type="email" name="email" class="form-control" ng-model="person.email" popover-validation="" />
            </div>
          </div>
          <div class="panel-footer">
            <button type="submit" class="btn btn-success">Submit</button>
          </div>
        </div>
      </form>
    </div>
  </div>
</div>

CSS

.popover-content-errors {
  padding: 0px;
}

.popover-content-errors .list-group {
  margin-bottom: 0px
}

.popover-content-errors .list-group-item {
  border-left: none;
  white-space: nowrap;
}

.popover-content-errors .list-group-item:first-child {
  border-top: none;
}

.popover-content-errors .list-group-item:last-child {
  border-bottom: none;
}

JavaScript

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

app.directive('submitNotify', function() {
  return {
    restrict: 'A',
    require: 'form',
    controller: function SubmitNotify() {},
    link: function(scope, element, attrs, form) {
      var $setSubmitted = form.$setSubmitted;
      form.$setSubmitted = function() {
        $setSubmitted.bind(form)();
        scope.$broadcast('onSubmitNotify');
      };
    }
  };
})
app.directive('popoverValidation', [function() {
  return {
    restrict: 'A',
    require: ['ngModel', '^submitNotify'],
    link: function(scope, element, attrs, require) {
      scope.$on('onSubmitNotify', function() {
        var ngModel = require[0];
        if (!ngModel.$valid) {
          showPopover(ngModel.$error);
        }
      });

      function showPopover($error) {
        var options = {
          content: getValidationErrorsHtml($error),
          html: true,
          template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content popover-content-errors"></div></div>',
          title: '<span class="text-info"><strong>Error</strong></span><button type="button" data-dismiss="popover" class="close">&times;</button>',
          trigger: 'manual'
        }
        $(element).popover(options);
        $(element).on('shown.bs.popover', hidePopover);
        $(element).popover('show');
      }

      function hidePopover() {
        $(this).next('.popover').find('button[data-dismiss="popover"]').click(function(e) {
          $(element).popover('hide');
        });
      }

      function getValidationErrorsHtml($error) {
        var errors = [];

        if ($error.required) {
          errors.push(requiredErrorMessage());
        }

        if ($error.email) {
          errors.push(invalidEmailAddress());
        }

        var errorHtml = '<ul class="list-group">';

        for (var i = 0; i < errors.length; i++) {
          errorHtml += '<li class="list-group-item">'...