簡體   English   中英

協調Angular.js和Bootstrap表單驗證樣式

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

我正在使用Angular和Bootstrap。 以下是供參考的代碼:

<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以input:invalid {.... }形式包含無效字段的樣式input:invalid {.... } ; 當田地空無一人時,這些就開始了。 現在我也通過Angular進行了一些模式匹配。 當“:invalid”關閉但是“.ng-invalid”打開時,這會產生奇怪的情況,這需要我為“.ng-invalid”類重新實現bootstrap CSS類。

我看到兩種選擇,但兩者都有問題

  • 讓Angular使用一些自定義類名而不是“ng-valid”(我不知道如何做到這一點)。
  • 禁用html5驗證(我認為這是表單標簽中的“novalidate”屬性應該做的,但由於某些原因無法使其工作)。

Angular-Bootstrap指令不包括樣式。

使用Bootstrap的“錯誤”類進行樣式設置。 您可以編寫更少的代碼。

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

編輯:正如其他答案和評論所指出的 - 在Bootstrap 3中,類現在是“has-error”,而不是“error”。

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

請注意'has-error''has-success'周圍的引號:花了一段時間才發現...

另一個解決方案: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);
              });
          }
      }
  };
}]);

然后在模板中簡單地使用它

<div class="form-group" bs-has-error>
    <input class="form-control" ng-model="foo" ng-pattern="/.../"/>
</div>

@ farincz的回答略有改進。 我同意指令是最好的方法,但我不想在每個.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);
                    });
                }
            }
        }
    };
}]);

對@Andrew Smith的回答略有改進。 我更改輸入元素並使用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);
                        });
                    });
                }
            });
        }
    };
}]);

感謝@farincz給出了一個很好的答案。 以下是我為了適應我的用例而進行的一些修改。

此版本提供三個指令:

  • bs-has-success
  • bs-has-error
  • bs-has (當你想要另外兩個一起使用的時候方便)

我做的修改:

  • 添加了一個檢查,僅在表單字段為臟時顯示has狀態,即在有人與它們交互之前不會顯示它們。
  • 對於那些不使用jQuery的人,改變了傳遞給element.find()的字符串,因為Angular的jQLite中的element.find()僅支持按標記名查找元素。
  • 添加了對選擇框和textareas的支持。
  • $timeout中包含element.find()以支持元素可能尚未將其子項呈現給DOM的情況(例如,如果元素的子元素標記為ng-if )。
  • 更改if表達式以檢查返回數組的長度if(input)來自@ farincz的answer的 if(input)總是返回true,因為element.find()的返回是一個jQuery數組)。

我希望有人覺得這很有用!

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

用法:

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

您還可以安裝Guilherme的涼亭包 ,將所有這些捆綁在一起。

如果樣式是問題,但您不想禁用本機驗證,為什么不用您自己的, 更具體的樣式覆蓋樣式?

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

使用CSS選擇器特異性來解決您的問題!

我對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);
                });

            }
        };
    });

})();

show-validation-errors可以添加到錯誤容器中,以便它根據表單字段的有效性顯示/隱藏容器。

並且show-validation-error顯示或隱藏基於該表單的元素字段對給定類型的有效性。

預期用途的一個例子:

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

我認為回復為時已晚,但希望你會喜歡它:

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

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

試試它並在你的控件中輸入一些文字,我發現它非常方便和令人敬畏。

沒有小提琴就很難確定,但是看看angular.js代碼它不會取代類 - 它只是添加並刪除它自己的。 因此任何引導類(由引導程序UI腳本動態添加)都應該不受角度的影響。

也就是說,使用Bootstrap的JS功能與Angular同時進行驗證是沒有意義的 - 只使用Angular。 我建議你使用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>

當我沒有聽到AngularJS本身的名字時,我知道這是一個非常古老的問題答案線程:-)

但對於那些登陸此頁面以便以干凈和自動的方式尋找Angular + Bootstrap表單驗證的人來說,我已經編寫了一個非常小的模塊來實現相同的功能,而無需改變任何形式的HTML或Javascript。

Checkout Bootstrap Angular驗證

以下是三個簡單的步驟:

  1. 通過Bower bower install bootstrap-angular-validation --save
  2. 添加腳本文件<script src="bower_components/bootstrap-angular-validation/dist/bootstrap-angular-validation.min.js"></script>
  3. 將依賴bootstrap.angular.validation添加到您的應用程序, 就是這樣!

這適用於Bootstrap 3, 不需要 jQuery

這是基於jQuery驗證的概念。 此模塊為驗證錯誤提供了一些額外的驗證和常見通用消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM