简体   繁体   English

协调Angular.js和Bootstrap表单验证样式

[英]Reconcile Angular.js and Bootstrap form validation styling

I am using Angular with Bootstrap. 我正在使用Angular和Bootstrap。 Here is the code for reference: 以下是供参考的代码:

<form name="newUserForm" ng-submit="add()" class="" novalidate>
    <input type="text" class="input" ng-model="newUser.uname" placeholder="Twitter" ng-pattern="/^@[A-Za-z0-9_]{1,15}$/" required></td>
    <button type="submit" ng-disabled="newUserForm.$invalid" class="btn btn-add btn-primary">Add</button>
</form>

Bootstrap has styles for invalid fields in the form of input:invalid {.... } ; Bootstrap以input:invalid {.... }形式包含无效字段的样式input:invalid {.... } ; these kick in when the field is empty. 当田地空无一人时,这些就开始了。 Now I also have some pattern matching via Angular. 现在我也通过Angular进行了一些模式匹配。 This creates odd cases when ":invalid" is off, but ".ng-invalid" is on, which would require me to re-implement bootstrap CSS classes for the ".ng-invalid" class. 当“:invalid”关闭但是“.ng-invalid”打开时,这会产生奇怪的情况,这需要我为“.ng-invalid”类重新实现bootstrap CSS类。

I see two options, but having trouble with both 我看到两种选择,但两者都有问题

  • Make Angular use some custom classname instead of "ng-valid" (I don't know how to do this). 让Angular使用一些自定义类名而不是“ng-valid”(我不知道如何做到这一点)。
  • Disable html5 validation (I thought that that's what "novalidate" attribute in the form tag should do, but couldn't get it work for some reason). 禁用html5验证(我认为这是表单标签中的“novalidate”属性应该做的,但由于某些原因无法使其工作)。

The Angular-Bootstrap directives out there don't cover styling. Angular-Bootstrap指令不包括样式。

Use Bootstrap's "error" class for styling. 使用Bootstrap的“错误”类进行样式设置。 You can write less code. 您可以编写更少的代码。

<form name="myForm">
  <div class="control-group" ng-class="{error: myForm.name.$invalid}">
    <label>Name</label>
    <input type="text" name="name" ng-model="project.name" required>
    <span ng-show="myForm.name.$error.required" class="help-inline">
        Required</span>
  </div>
</form>

EDIT: As other answers and comments point out - in Bootstrap 3 the class is now "has-error", not "error". 编辑:正如其他答案和评论所指出的 - 在Bootstrap 3中,类现在是“has-error”,而不是“error”。

The classes have changed in Bootstrap 3: Bootstrap 3中的类已更改:

<form class="form-horizontal" name="form" novalidate ng-submit="submit()" action="/login" method="post">
  <div class="row" ng-class="{'has-error': form.email.$invalid, 'has-success': !form.email.$invalid}">
    <label for="email" class="control-label">email:</label>
    <div class="col">
    <input type="email" id="email" placeholder="email" name="email" ng-model="email" required>
    <p class="help-block error" ng-show="form.email.$dirty && form.email.$error.required">please enter your email</p>
    <p class="help-block error" ng-show="form.email.$error.email">please enter a valid email</p>
  ...

Note the quotes around 'has-error' and 'has-success' : took a while to find that... 请注意'has-error''has-success'周围的引号:花了一段时间才发现...

Another solution: Create directive which toggles has-error class according to a child input. 另一个解决方案:Create指令根据子输入切换has-error类。

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>

Minor improvement to @farincz's answer . @ farincz的回答略有改进。 I agree that a directive is the best approach here but I didn't want to have to repeat it on every .form-group element so I updated the code to allow adding it to either the .form-group or to the parent <form> element (which will add it to all contained .form-group elements): 我同意指令是最好的方法,但我不想在每个.form-group元素上重复它,所以我更新了代码以允许将它添加到.form-group或父<form> element(将其添加到所有包含的.form-group元素):

angular.module('directives', [])
  .directive('showValidation', [function() {
    return {
        restrict: "A",
        link: function(scope, element, attrs, ctrl) {

            if (element.get(0).nodeName.toLowerCase() === 'form') {
                element.find('.form-group').each(function(i, formGroup) {
                    showValidation(angular.element(formGroup));
                });
            } else {
                showValidation(element);
            }

            function showValidation(formGroupEl) {
                var input = formGroupEl.find('input[ng-model],textarea[ng-model]');
                if (input.length > 0) {
                    scope.$watch(function() {
                        return input.hasClass('ng-invalid');
                    }, function(isInvalid) {
                        formGroupEl.toggleClass('has-error', isInvalid);
                    });
                }
            }
        }
    };
}]);

Minor improvement to @Andrew Smith's answer. 对@Andrew Smith的回答略有改进。 I change input elements and using require keyword. 我更改输入元素并使用require关键字。

.directive('showValidation', [function() {
    return {
        restrict: "A",
        require:'form',
        link: function(scope, element, attrs, formCtrl) {
            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');
                        }, function(isInvalid) {
                            $formGroup.toggleClass('has-error', isInvalid);
                        });
                    });
                }
            });
        }
    };
}]);

Thank you to @farincz for a great answer. 感谢@farincz给出了一个很好的答案。 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) bs-has (当你想要另外两个一起使用的时候方便)

Modifications I have made: 我做的修改:

  • Added a check to only show the has states when the form field is dirty, ie they won't be shown until somebody interacts with them. 添加了一个检查,仅在表单字段为脏时显示has状态,即在有人与它们交互之前不会显示它们。
  • 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. 对于那些不使用jQuery的人,改变了传递给element.find()的字符串,因为Angular的jQLite中的element.find()仅支持按标记名查找元素。
  • Added support for select boxes and textareas. 添加了对选择框和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 (eg if a child of the element is marked with ng-if ). $timeout中包含element.find()以支持元素可能尚未将其子项呈现给DOM的情况(例如,如果元素的子元素标记为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). 更改if表达式以检查返回数组的长度if(input)来自@ farincz的answer的 if(input)总是返回true,因为element.find()的返回是一个jQuery数组)。

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. 您还可以安装Guilherme的凉亭包 ,将所有这些捆绑在一起。

If styling is the issue, but you don't want to disable the native validation, why not override the styling with your own, more specific style? 如果样式是问题,但您不想禁用本机验证,为什么不用您自己的, 更具体的样式覆盖样式?

input.ng-invalid, input.ng-invalid:invalid {
   background: red;
   /*override any styling giving you fits here*/
}

Cascade your problems away with CSS selector specificity! 使用CSS选择器特异性来解决您的问题!

My improvement to Jason Im's answer the following adds two new directives show-validation-errors and show-validation-error. 我对Jason Im的回答改进了以下内容,增加了两个新的指令show-validation-errors和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. show-validation-errors可以添加到错误容器中,以便它根据表单字段的有效性显示/隐藏容器。

and the show-validation-error shows or hides an element based upon that form fields validity on a given type. 并且show-validation-error显示或隐藏基于该表单的元素字段对给定类型的有效性。

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>

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 CSS你可以添加其他类型的控件,如选择,日期,密码等

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 HTML :除了ng-required之外,不需要在控件中添加任何内容

<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. 试试它并在你的控件中输入一些文字,我发现它非常方便和令人敬畏。

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. 没有小提琴就很难确定,但是看看angular.js代码它不会取代类 - 它只是添加并删除它自己的。 So any bootstrap classes (added dynamically by bootstrap UI scripts) should be untouched by angular. 因此任何引导类(由引导程序UI脚本动态添加)都应该不受角度的影响。

That said, it does not make sense to use Bootstrap's JS functionality for validation at the same time as Angular - only use Angular. 也就是说,使用Bootstrap的JS功能与Angular同时进行验证是没有意义的 - 只使用Angular。 I would suggest you employ the bootstrap styles and the angular JS ie add the bootstrap css classes to your elements using a custom validation directive. 我建议你使用bootstrap样式和角度JS,即使用自定义验证指令将bootstrap css类添加到元素中。

<div class="form-group has-feedback" ng-class="{ 'has-error': form.uemail.$invalid && form.uemail.$dirty }">
  <label class="control-label col-sm-2" for="email">Email</label>
  <div class="col-sm-10">
    <input type="email" class="form-control" ng-model="user.email" name="uemail" placeholder="Enter email" required>
    <div ng-show="form.$submitted || form.uphone.$touched" ng-class="{ 'has-success': form.uemail.$valid && form.uemail.$dirty }">
    <span ng-show="form.uemail.$valid" class="glyphicon glyphicon-ok-sign form-control-feedback" aria-hidden="true"></span>
    <span ng-show="form.uemail.$invalid && form.uemail.$dirty" class="glyphicon glyphicon-remove-circle form-control-feedback" aria-hidden="true"></span>
    </div>
  </div>
</div>

I know this is a very old question answer thread when I haven't heard the name of AngularJS itself :-) 当我没有听到AngularJS本身的名字时,我知道这是一个非常古老的问题答案线程:-)

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. 但对于那些登陆此页面以便以干净和自动的方式寻找Angular + Bootstrap表单验证的人来说,我已经编写了一个非常小的模块来实现相同的功能,而无需改变任何形式的HTML或Javascript。

Checkout Bootstrap Angular Validation . Checkout Bootstrap Angular验证

Following are the three simple steps: 以下是三个简单的步骤:

  1. Install via Bower bower install bootstrap-angular-validation --save 通过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> 添加脚本文件<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!! 将依赖bootstrap.angular.validation添加到您的应用程序, 就是这样!

This works with Bootstrap 3 and jQuery is not required . 这适用于Bootstrap 3, 不需要 jQuery

This is based on the concept of jQuery validation. 这是基于jQuery验证的概念。 This module provides some additional validation and common generic messages for validation error. 此模块为验证错误提供了一些额外的验证和常见通用消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM