Reconcile Angular.js and Bootstrap form validation styling

前端 未结 12 1950
刺人心
刺人心 2020-12-22 18:53

I am using Angular with Bootstrap. Here is the code for reference:

相关标签:
12条回答
  • 2020-12-22 19:32

    My improvement to Jason Im's answer the following adds two new directives show-validation-errors and show-validation-error.

    'use strict';
    (function() {
    
        function getParentFormName(element,$log) {
            var parentForm = element.parents('form:first');
            var parentFormName = parentForm.attr('name');
    
            if(!parentFormName){
                $log.error("Form name not specified!");
                return;
            }
    
            return parentFormName;
        }
    
        angular.module('directives').directive('showValidation', function () {
            return {
                restrict: 'A',
                require: 'form',
                link: function ($scope, element) {
                    element.find('.form-group').each(function () {
                        var formGroup = $(this);
                        var inputs = formGroup.find('input[ng-model],textarea[ng-model],select[ng-model]');
    
                        if (inputs.length > 0) {
                            inputs.each(function () {
                                var input = $(this);
                                $scope.$watch(function () {
                                    return input.hasClass('ng-invalid') && !input.hasClass('ng-pristine');
                                }, function (isInvalid) {
                                    formGroup.toggleClass('has-error', isInvalid);
                                });
                                $scope.$watch(function () {
                                    return input.hasClass('ng-valid') && !input.hasClass('ng-pristine');
                                }, function (isInvalid) {
                                    formGroup.toggleClass('has-success', isInvalid);
                                });
                            });
                        }
                    });
                }
            };
        });
    
        angular.module('directives').directive('showValidationErrors', function ($log) {
            return {
                restrict: 'A',
                link: function ($scope, element, attrs) {
                    var parentFormName = getParentFormName(element,$log);
                    var inputName = attrs['showValidationErrors'];
                    element.addClass('ng-hide');
    
                    if(!inputName){
                        $log.error("input name not specified!")
                        return;
                    }
    
                    $scope.$watch(function () {
                        return !($scope[parentFormName][inputName].$dirty && $scope[parentFormName][inputName].$invalid);
                    },function(noErrors){
                        element.toggleClass('ng-hide',noErrors);
                    });
    
                }
            };
        });
    
        angular.module('friport').directive('showValidationError', function ($log) {
            return {
                restrict: 'A',
                link: function ($scope, element, attrs) {
                    var parentFormName = getParentFormName(element,$log);
                    var parentContainer = element.parents('*[show-validation-errors]:first');
                    var inputName = parentContainer.attr('show-validation-errors');
                    var type = attrs['showValidationError'];
    
                    element.addClass('ng-hide');
    
                    if(!inputName){
                        $log.error("Could not find parent show-validation-errors!");
                        return;
                    }
    
                    if(!type){
                        $log.error("Could not find validation error type!");
                        return;
                    }
    
                    $scope.$watch(function () {
                        return !$scope[parentFormName][inputName].$error[type];
                    },function(noErrors){
                        element.toggleClass('ng-hide',noErrors);
                    });
    
                }
            };
        });
    
    })();
    

    The show-validation-errors can be added to a container of errors so that it will show/hide the container based upon a form fields validity.

    and the show-validation-error shows or hides an element based upon that form fields validity on a given type.

    An example of intended use:

            <form role="form" name="organizationForm" novalidate show-validation>
                <div class="form-group">
                    <label for="organizationNumber">Organization number</label>
                    <input type="text" class="form-control" id="organizationNumber" name="organizationNumber" required ng-pattern="/^[0-9]{3}[ ]?[0-9]{3}[ ]?[0-9]{3}$/" ng-model="organizationNumber">
                    <div class="help-block with-errors" show-validation-errors="organizationNumber">
                        <div show-validation-error="required">
                            Organization number is required.
                        </div>
                        <div show-validation-error="pattern">
                            Organization number needs to have the following format "000 000 000" or "000000000".
                        </div>
                    </div>
                </div>
           </form>
    
    0 讨论(0)
  • 2020-12-22 19:35

    Another solution: Create directive which toggles has-error class according to a child input.

    app.directive('bsHasError', [function() {
      return {
          restrict: "A",
          link: function(scope, element, attrs, ctrl) {
              var input = element.find('input[ng-model]'); 
              if (input.length) {
                  scope.$watch(function() {
                      return input.hasClass('ng-invalid');
                  }, function(isInvalid) {
                      element.toggleClass('has-error', isInvalid);
                  });
              }
          }
      };
    }]);
    

    and then simple use it in template

    <div class="form-group" bs-has-error>
        <input class="form-control" ng-model="foo" ng-pattern="/.../"/>
    </div>
    
    0 讨论(0)
  • 2020-12-22 19:35

    I know this is a very old question answer thread when I haven't heard the name of AngularJS itself :-)

    But for others who land to this page looking for Angular + Bootstrap form validation in a clean and automated way, I've written a pretty small module for achieving the same without altering the HTML or Javascript in any form.

    Checkout Bootstrap Angular Validation.

    Following are the three simple steps:

    1. Install via Bower bower install bootstrap-angular-validation --save
    2. Add the script file <script src="bower_components/bootstrap-angular-validation/dist/bootstrap-angular-validation.min.js"></script>
    3. Add the dependency bootstrap.angular.validation to your application and that's it!!

    This works with Bootstrap 3 and jQuery is not required.

    This is based on the concept of jQuery validation. This module provides some additional validation and common generic messages for validation error.

    0 讨论(0)
  • 2020-12-22 19:37

    It's hard to tell for sure without a fiddle but looking at the angular.js code it does not replace classes - it just adds and removes its own. So any bootstrap classes (added dynamically by bootstrap UI scripts) should be untouched by angular.

    That said, it does not make sense to use Bootstrap's JS functionality for validation at the same time as Angular - only use Angular. I would suggest you employ the bootstrap styles and the angular JS i.e. add the bootstrap css classes to your elements using a custom validation directive.

    0 讨论(0)
  • 2020-12-22 19:38

    Thank you to @farincz for a great answer. Here are some modifications I have made to fit with my use case.

    This version provides three directives:

    • bs-has-success
    • bs-has-error
    • bs-has (a convenience for when you want to use the other two together)

    Modifications I have made:

    • Added a check to only show the has states when the form field is dirty, i.e. they won't be shown until somebody interacts with them.
    • Altered the string passed into element.find() for those not using jQuery, as element.find() in Angular's jQLite only supports finding elements by tagname.
    • Added support for select boxes and textareas.
    • Wrapped the element.find() in a $timeout to support cases where the element may not yet have it's children rendered to the DOM (e.g. if a child of the element is marked with ng-if).
    • Changed if expression to check for the length of the returned array (if(input) from @farincz's answer always returns true, as the return from element.find() is a jQuery array).

    I hope somebody finds this useful!

    angular.module('bs-has', [])
      .factory('bsProcessValidator', function($timeout) {
        return function(scope, element, ngClass, bsClass) {
          $timeout(function() {
            var input = element.find('input');
            if(!input.length) { input = element.find('select'); }
            if(!input.length) { input = element.find('textarea'); }
            if (input.length) {
                scope.$watch(function() {
                    return input.hasClass(ngClass) && input.hasClass('ng-dirty');
                }, function(isValid) {
                    element.toggleClass(bsClass, isValid);
                });
            }
          });
        };
      })
      .directive('bsHasSuccess', function(bsProcessValidator) {
        return {
          restrict: 'A',
          link: function(scope, element) {
            bsProcessValidator(scope, element, 'ng-valid', 'has-success');
          }
        };
      })
      .directive('bsHasError', function(bsProcessValidator) {
        return {
          restrict: 'A',
          link: function(scope, element) {
            bsProcessValidator(scope, element, 'ng-invalid', 'has-error');
          }
        };
      })
      .directive('bsHas', function(bsProcessValidator) {
        return {
          restrict: 'A',
          link: function(scope, element) {
            bsProcessValidator(scope, element, 'ng-valid', 'has-success');
            bsProcessValidator(scope, element, 'ng-invalid', 'has-error');
          }
        };
      });
    

    Usage:

    <!-- Will show success and error states when form field is dirty -->
    <div class="form-control" bs-has>
      <label for="text"></label>
      <input 
       type="text" 
       id="text" 
       name="text" 
       ng-model="data.text" 
       required>
    </div>
    
    <!-- Will show success state when select box is anything but the first (placeholder) option -->
    <div class="form-control" bs-has-success>
      <label for="select"></label>
      <select 
       id="select" 
       name="select" 
       ng-model="data.select" 
       ng-options="option.name for option in data.selectOptions"
       required>
        <option value="">-- Make a Choice --</option>
      </select>
    </div>
    
    <!-- Will show error state when textarea is dirty and empty -->
    <div class="form-control" bs-has-error>
      <label for="textarea"></label>
      <textarea 
       id="textarea" 
       name="textarea" 
       ng-model="data.textarea" 
       required></textarea>
    </div>
    

    You can also install Guilherme's bower package that bundles all this together.

    0 讨论(0)
  • 2020-12-22 19:48

    I think it's too late to reply but hope you are going to love it:

    CSS you can add other type of controls like select, date, password etc

    input[type="text"].ng-invalid{
        border-left: 5px solid #ff0000;
        background-color: #FFEBD6;
    }
    input[type="text"].ng-valid{
        background-color: #FFFFFF;
        border-left: 5px solid #088b0b;
    }
    input[type="text"]:disabled.ng-valid{
        background-color: #efefef;
        border: 1px solid #bbb;
    }
    

    HTML: no need to add anything in controls except ng-required if it is

    <input type="text"
           class="form-control"
           ng-model="customer.ZipCode"
           ng-required="true">
    

    Just try it and type some text in your control, I find it really handy and awesome.

    0 讨论(0)
提交回复
热议问题