Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display angular form validation error inside angular bootstrap popover?

I am new to angular js. I want to show the form error in angular bootstrap popover at the right hand side of the element.I tried to create the directive and I got an element when it changes classes. But I don't have an idea how to move next.

(function(angular) {
  'use strict';
var app=angular.module('formExample', [])
  .controller('ExampleController', ['$scope', function($scope) {
    $scope.master = {};

    $scope.update = function(user) {
      $scope.master = angular.copy(user);
    };

    $scope.reset = function(form) {
      if (form) {
        form.$setPristine();
        form.$setUntouched();
      }
      $scope.user = angular.copy($scope.master);
    };

    $scope.reset();
  }]);
app.directive("alert", function(){
    return {
        restrict: 'C',
        priority: -1000,
        link: function(scope, ele, attrs, ctrl){
          scope.$watch(function() {console.log(ele.attr('class')); })
          if (ctrl) {
            console.log("applying custom behaviour to input: ", ele.attr('id'));
            // ... awesomeness here
          }
        }
    };
});
})(window.angular);

I just want to show the error message

  1. when user clicks save button(all form fields error message)
  2. blur of an element(only for the element which is lost the focus)

Here is my plnkr which I tried to get the message.

Update

Somehow I displayed the angular bootstrap popover and close button which closes the popover.

I have two issues in the current plunker.

  1. I want to display the error message inside my popover template respective to the element where it is getting opened. I need this template because I need a close button.
  2. Once I closed the popover if the field is empty and user clicks submit the popover is not opening next time. I want to display error message each and every time of submit.
like image 629
svk Avatar asked Feb 07 '17 07:02

svk


2 Answers

How about putting your template like this:

<script type="text/ng-template" id="myPopoverTemplate.html">
  <div class="gmePopover">
    <div class="popover-header">
      <button type="button" class="close" popover-toggle><span aria-hidden="true">&times;</span></button>
    </div>
    <div class="popover-content">
        somecontent
    </div>
  </div>
</script>

Working Plunker here.

UPDATE:

You can use angularjs foreach to loop through all errors in your form, then from there you can show the popover base on your element. Something like this: working plunker

<script type="text/javascript">
  var app=angular.module('testApp', ['ngAnimate', 'ngSanitize'], function($httpProvider) {});
  app.controller("PopoverDemoCtrl", function($scope, $http, $window) {
    $scope.validate = function() {
        var _popover;
        var error = $scope.testForm.$error;
        angular.forEach(error.required, function(field){
            var message = 'This field (' + field.$name + ') is required';
            _popover = $('#' + field.$name).popover({
              trigger: 'manual',
              title: '<span class="text-info"><strong>title</strong></span>'+
            '<button type="button" id="close" class="close" onclick="$(&quot;#' + field.$name + '&quot;).popover(&quot;hide&quot;);">&times;</button>',
              content: message,
              html: true
            });

            return $('#' + field.$name).popover("show")
        });
    };
  });
</script>
like image 124
jomsk1e Avatar answered Nov 08 '22 04:11

jomsk1e


You can create a directive that intercepts the $setSubmitted method of the FormController.

You can find more information regarding the method here

Please find working example here

When this directive intercepts the $setSubmitted method, we can notify another directive to show the validation errors in a bootstrap popover.

I am working under the following assumptions(feel free to correct me):

  • you will be using a form tag
  • on your form tag you will have ng-submit="nameOfForm.$valid && vm.onSubmit()"

The solution works with two directives:

submitNotify and popoverValidation

submitNotify notifies popoverValidation when the form is submitted, the popoverValidation directive then shows the form errors if there are any.

Directive 1 : submitNotify

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

Explanation:

  • Can only be used as a attribute directive
  • Requires a form tag, or ngForm

The link function:

The link function replaces the $setSubmitted function with a callback function. The callback function notifies the popoverValidation directive that the form has been submitted.

Directive 2: popoverValidation

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">' + errors[i] + '</li>';
                }

                errorHtml += '</ul>';

                return errorHtml;
            }

            function requiredErrorMessage() {
                return 'This field is required';
            }

            function invalidEmailAddress() {
                return 'Please enter a valid email address';
            }
        }
    };
}]);

Explanation:

  • Can only be used as a attribute directive
  • Requires a submitNotify tag on the parent form

The link function:

  • The popoverValidation directive gets notified that the form is submitted
  • It checks if the ng-model binded property is valid
  • If not valid a popover gets displayed

Complete HTML:

<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>

Some CSS:

<style type="text/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;
    }
</style>

MyFormController

controller('MyFormController', ['$scope', function ($scope) {
    var self = this;
    $scope.person = {
        email:'john.doe.com'
    }
    self.onSubmit = function () {   
        console.log('MyFormController.onSubmit');
    };
}]);
like image 21
Tjaart van der Walt Avatar answered Nov 08 '22 05:11

Tjaart van der Walt