簡體   English   中英

如何驗證使用 ng-repeat、ng-show(角度)動態創建的輸入

[英]How to validate inputs dynamically created using ng-repeat, ng-show (angular)

我有一個使用 ng-repeat 創建的表。 我想為表中的每個元素添加驗證。 問題是每個輸入單元格都與其上方和下方的單元格具有相同的名稱。 我嘗試使用{{$index}}值來命名輸入,但盡管 HTML 中的字符串文字看起來是正確的,但它現在正在工作。

這是我現在的代碼:

<tr ng-repeat="r in model.BSM ">
   <td>
      <input ng-model="r.QTY" class="span1" name="QTY{{$index}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/>
      <span class="alert-error" ng-show="form.QTY{{$index}}.$error.pattern"><strong>Requires a number.</strong></span>
      <span class="alert-error" ng-show="form.QTY{{$index}}.$error.required"><strong>*Required</strong></span>
   </td>
</tr>

我曾嘗試從索引中刪除{{}} ,但這也不起作用。 截至目前,輸入的驗證屬性工作正常,但未顯示錯誤消息。

有人有什么建議嗎?

編輯:除了下面的好答案,這里有一篇博客文章更詳細地介紹了這個問題: http : //www.thebhwgroup.com/blog/2014/08/angularjs-html-form-design-part-2 /

自從提出這個問題以來,Angular 團隊已經通過動態創建輸入名稱解決了這個問題。

使用Angular 1.3 及更高版本,您現在可以執行以下操作:

<form name="vm.myForm" novalidate>
  <div ng-repeat="p in vm.persons">
    <input type="text" name="person_{{$index}}" ng-model="p" required>
    <span ng-show="vm.myForm['person_' + $index].$invalid">Enter a name</span>
  </div>
</form>

演示

Angular 1.3 還引入了 ngMessages,一種更強大的表單驗證工具。 您可以對 ngMessages 使用相同的技術:

<form name="vm.myFormNgMsg" novalidate>
    <div ng-repeat="p in vm.persons">
      <input type="text" name="person_{{$index}}" ng-model="p" required>
      <span ng-messages="vm.myFormNgMsg['person_' + $index].$error">
        <span ng-message="required">Enter a name</span>
      </span>
    </div>
  </form>

AngularJS 依賴輸入名稱來暴露驗證錯誤。

不幸的是,截至今天,(不使用自定義指令)動態生成輸入的名稱是不可能的。 事實上,檢查輸入文檔我們可以看到 name 屬性只接受一個字符串。

要解決“動態名稱”問題,您需要創建一個內部表單(請參閱ng-form

<div ng-repeat="social in formData.socials">
      <ng-form name="urlForm">
            <input type="url" name="socialUrl" ng-model="social.url">
            <span class="alert error" ng-show="urlForm.socialUrl.$error.url">URL error</span>
      </ng-form>
  </div>

另一種選擇是為此編寫自定義指令。

這是顯示 ngForm 用法的 jsFiddle: http : //jsfiddle.net/pkozlowski_opensource/XK2ZT/2/

如果您不想使用 ng-form,您可以使用自定義指令來更改表單的 name 屬性。 將此指令作為屬性放在與 ng-model 相同的元素上。

如果您結合使用其他指令,請注意它們沒有設置“終端”屬性,否則此函數將無法運行(假設它的優先級為 -1)。

例如,當將此指令與 ng-options 一起使用時,您必須運行這一行monkeypatch: https : //github.com/AlJohri/bower-angular/commit/eb17a967b7973eb7fc1124b024aa8b3ca540a155

angular.module('app').directive('fieldNameHack', function() {
    return {
      restrict: 'A',
      priority: -1,
      require: ['ngModel'],
      // the ngModelDirective has a priority of 0.
      // priority is run in reverse order for postLink functions.
      link: function (scope, iElement, iAttrs, ctrls) {

        var name = iElement[0].name;
        name = name.replace(/\{\{\$index\}\}/g, scope.$index);

        var modelCtrl = ctrls[0];
        modelCtrl.$name = name;

      }
    };
});

我經常發現使用 ng-init 將 $index 設置為變量名很有用。 例如:

<fieldset class='inputs' ng-repeat="question questions" ng-init="qIndex = $index">

這會將您的正則表達式更改為:

name = name.replace(/\{\{qIndex\}\}/g, scope.qIndex);

如果您有多個嵌套的 ng-repeat,您現在可以使用這些變量名稱代替 $parent.$index。

指令的“終端”和“優先級”的定義: https : //docs.angularjs.org/api/ng/service/ $compile#directive-definition-object

Github 關於需要 ng-option monkeypatch 的評論: https : //github.com/angular/angular.js/commit/9ee2cdff44e7d496774b340de816344126c457b3#commitcomment-6832095 https://twitter.com/aljohri/status/41436

更新:

您也可以使用 ng-form 進行這項工作。

angular.module('app').directive('formNameHack', function() {
    return {
      restrict: 'A',
      priority: 0,
      require: ['form'],
      compile: function() {
        return {
          pre: function(scope, iElement, iAttrs, ctrls) {
            var parentForm = $(iElement).parent().controller('form');
            if (parentForm) {
                var formCtrl = ctrls[0];
                delete parentForm[formCtrl.$name];
                formCtrl.$name = formCtrl.$name.replace(/\{\{\$index\}\}/g, scope.$index);
                parentForm[formCtrl.$name] = formCtrl;
            }
          }
        }
      }
    };
});

在使用 ng-repeat 指令的標簽內使用 ng-form 指令。 然后,您可以使用 ng-form 指令創建的范圍來引用通用名稱。 例如:

    <div class="form-group col-sm-6" data-ng-form="subForm" data-ng-repeat="field in justificationInfo.justifications"">

        <label for="{{field.label}}"><h3>{{field.label}}</h3></label>
        <i class="icon-valid" data-ng-show="subForm.input.$dirty && subForm.input.$valid"></i>
        <i class="icon-invalid" data-ng-show="subForm.input.$dirty && subForm.input.$invalid"></i>
        <textarea placeholder="{{field.placeholder}}" class="form-control" id="{{field.label}}" name="input" type="text" rows="3" data-ng-model="field.value" required>{{field.value}}</textarea>

    </div>

歸功於: http : //www.benlesh.com/2013/03/angular-js-validating-form-elements-in.html

在控制器http://jsfiddle.net/82PX4/3/的一側添加了帶有“自定義驗證”的更復雜的示例

<div class='line' ng-repeat='line in ranges' ng-form='lineForm'>
    low: <input type='text' 
                name='low'
                ng-pattern='/^\d+$/' 
                ng-change="lowChanged(this, $index)" ng-model='line.low' />
    up: <input type='text' 
                name='up'
                ng-pattern='/^\d+$/'
                ng-change="upChanged(this, $index)" 
                ng-model='line.up' />
    <a href ng-if='!$first' ng-click='removeRange($index)'>Delete</a>
    <div class='error' ng-show='lineForm.$error.pattern'>
        Must be a number.
    </div>
    <div class='error' ng-show='lineForm.$error.range'>
        Low must be less the Up.
    </div>
</div>

查看這些解決方案,上面 Al Johri 提供的解決方案最接近我的需求,但他的指令比我想要的可編程性要差一些。 這是我的解決方案版本:

angular.module("app", [])
    .directive("dynamicFormName", function() {
        return {
            restrict: "A",
            priority: 0,
            require: ["form"],
            compile: function() {
                return {
                    pre: function preLink(scope, iElement, iAttrs, ctrls) {
                        var name = "field" + scope.$index;

                        if (iAttrs.dnfnNameExpression) {
                            name = scope.$eval(iAttrs.dnfnNameExpression);
                        }

                        var parentForm = iElement.parent().controller("form");
                        if (parentForm) {
                            var formCtrl = ctrls[0];
                            delete parentForm[formCtrl.$name];
                            formCtrl.$name = name;
                            parentForm[formCtrl.$name] = formCtrl;
                        }
                    }
                 }
            }
        };
   });

此解決方案讓您只需將名稱生成器表達式傳遞給指令,並避免鎖定他正在使用的模式替換。

我最初也遇到了這個解決方案的問題,因為它沒有顯示在標記中使用它的例子,所以這里是我如何使用它。

<form name="theForm">
    <div ng-repeat="field in fields">
        <input type="number" ng-form name="theInput{{field.id}}" ng-model="field.value" dynamic-form-name dnfn-name-expression="'theInput' + field.id">        
    </div>
</form>

我在github上有一個更完整的工作示例。

如果我使用以下語法scope.step3Form['item[107][quantity]'].$touched我不知道這是最佳實踐還是最佳解決方案,但它可以工作

<tr ng-repeat="item in items">
   <td>
        <div class="form-group">
            <input type="text" ng-model="item.quantity" name="item[<% item.id%>][quantity]" required="" class="form-control" placeholder = "# of Units" />
            <span ng-show="step3Form.$submitted || step3Form['item[<% item.id %>][quantity]'].$touched">
                <span class="help-block" ng-show="step3Form['item[<% item.id %>][quantity]'].$error.required"> # of Units is required.</span>
            </span>
        </div>
    </td>
</tr>

基於 pkozlowski.opensource's answer ,我添加了一種方法來獲得動態輸入名稱也可以與ngMessages一起 使用 注意ng-form元素上的ng-init部分和furryName的使用。 furryName成為包含inputname屬性的變量值的變量name

<ion-item ng-repeat="animal in creatures track by $index">
<ng-form name="animalsForm" ng-init="furryName = 'furry' + $index">
        <!-- animal is furry toggle buttons -->
        <input id="furryRadio{{$index}}"
               type="radio"
               name="{{furryName}}"
               ng-model="animal.isFurry"
               ng-value="radioBoolValues.boolTrue"
               required
                >
        <label for="furryRadio{{$index}}">Furry</label>

        <input id="hairlessRadio{{$index}}"
               name="{{furryName}}"
               type="radio"
               ng-model="animal.isFurry"
               ng-value="radioBoolValues.boolFalse"
               required
               >
        <label for="hairlessRadio{{$index}}">Hairless</label>

        <div ng-messages="animalsForm[furryName].$error"
             class="form-errors"
             ng-show="animalsForm[furryName].$invalid && sectionForm.$submitted">
            <div ng-messages-include="client/views/partials/form-errors.ng.html"></div>
        </div>
</ng-form>
</ion-item>

這是我如何做到這一點的示例,我不知道它是否是最佳解決方案,但效果很好。

首先,在 HTML 中編碼。 看看 ng-class,它正在調用 hasError 函數。 還要查看輸入的名稱聲明。 我使用 $index 創建不同的輸入名稱。

<div data-ng-repeat="tipo in currentObject.Tipo"
    ng-class="{'has-error': hasError(planForm, 'TipoM', 'required', $index) || hasError(planForm, 'TipoM', 'maxlength', $index)}">
    <input ng-model="tipo.Nombre" maxlength="100" required
        name="{{'TipoM' + $index}}"/>

現在,這里是 hasError 函數:

$scope.hasError = function (form, elementName, errorType, index) {
           if (form == undefined
               || elementName == undefined
               || errorType == undefined
               || index == undefined)
               return false;

           var element = form[elementName + index];
           return (element != null && element.$error[errorType] && element.$touched);
       };

為時已晚,但可能可以幫助任何人

  1. 為每個控件創建唯一名稱
  2. 使用fromname[uniquname].$error

示例代碼:

<input 
    ng-model="r.QTY" 
    class="span1" 
    name="QTY{{$index}}" 
    ng-pattern="/^[\d]*\.?[\d]*$/" required/>
<div ng-messages="formName['QTY' +$index].$error"
     ng-show="formName['QTY' +$index].$dirty || formName.$submitted">
   <div ng-message="required" class='error'>Required</div>
   <div ng-message="pattern" class='error'>Invalid Pattern</div>
</div>

此處查看工作演示

如果您使用 ng-repeat $index 是這樣工作的

  name="QTY{{$index}}"

   <td>
       <input ng-model="r.QTY" class="span1" name="QTY{{$index}}" ng-            
        pattern="/^[\d]*\.?[\d]*$/" required/>
        <span class="alert-error" ng-show="form['QTY' + $index].$error.pattern">
        <strong>Requires a number.</strong></span>
        <span class="alert-error" ng-show="form['QTY' + $index].$error.required">
       <strong>*Required</strong></span>
    </td>

我們必須以 ng-pattern 顯示 ng-show

   <span class="alert-error" ng-show="form['QTY' + $index].$error.pattern">
   <span class="alert-error" ng-show="form['QTY' + $index].$error.required">

這是可能的,這是我如何用輸入表做同樣的事情。

像這樣把桌子包起來

然后就用這個

我有一個帶有多嵌套指令的表單,所有指令都包含輸入、選擇等……這些元素都包含在 ng-repeat 和動態字符串值中。

這是如何使用指令:

<form name="myFormName">
  <nested directives of many levels>
    <your table here>
    <perhaps a td here>
    ex: <input ng-repeat=(index, variable) in variables" type="text"
               my-name="{{ variable.name + '/' + 'myFormName' }}"
               ng-model="variable.name" required />
    ex: <select ng-model="variable.name" ng-options="label in label in {{ variable.options }}"
                my-name="{{ variable.name + index + '/' + 'myFormName' }}"
        </select>
</form>

注意:如果您需要序列化輸入表,您可以添加和索引字符串連接; 這就是我所做的。

app.directive('myName', function(){

  var myNameError = "myName directive error: "

  return {
    restrict:'A', // Declares an Attributes Directive.
    require: 'ngModel', // ngModelController.

    link: function( scope, elem, attrs, ngModel ){
      if( !ngModel ){ return } // no ngModel exists for this element

      // check myName input for proper formatting ex. something/something
      checkInputFormat(attrs);

      var inputName = attrs.myName.match('^\\w+').pop(); // match upto '/'
      assignInputNameToInputModel(inputName, ngModel);

      var formName = attrs.myName.match('\\w+$').pop(); // match after '/'
      findForm(formName, ngModel, scope);
    } // end link
  } // end return

  function checkInputFormat(attrs){
    if( !/\w\/\w/.test(attrs.rsName )){
      throw myNameError + "Formatting should be \"inputName/formName\" but is " + attrs.rsName
    }
  }

  function assignInputNameToInputModel(inputName, ngModel){
    ngModel.$name = inputName
  }

  function addInputNameToForm(formName, ngModel, scope){
    scope[formName][ngModel.$name] = ngModel; return
  }

  function findForm(formName, ngModel, scope){
    if( !scope ){ // ran out of scope before finding scope[formName]
      throw myNameError + "<Form> element named " + formName + " could not be found."
    }

    if( formName in scope){ // found scope[formName]
      addInputNameToForm(formName, ngModel, scope)
      return
    }
    findForm(formName, ngModel, scope.$parent) // recursively search through $parent scopes
  }
});

這應該可以處理許多您不知道表單在哪里的情況。 或者,您可能有嵌套的表單,但出於某種原因,您想將此輸入名稱附加到兩個表單上? 好吧,只需傳入要附加輸入名稱的表單名稱。

我想要的是一種將動態值分配給我永遠不會知道的輸入的方法,然后只需調用 $scope.myFormName.$valid。

您可以添加任何您想要的內容:更多表格、更多表單輸入、嵌套表單,以及您想要的任何內容。 只需傳遞您想要驗證輸入的表單名稱。 然后在表單提交上詢問 $scope.yourFormName.$valid

這將使 ng-repeat 中的名稱在表單驗證中單獨出現。

<td>
    <input ng-model="r.QTY" class="span1" name="{{'QTY' + $index}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/>
</td>

但是我很難讓它在它的驗證消息中查找,所以我不得不使用 ng-init 讓它將變量解析為對象鍵。

<td>
    <input ng-model="r.QTY" class="span1" ng-init="name = 'QTY' + $index" name="{{name}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/>
    <span class="alert-error" ng-show="form[name].$error.pattern"><strong>Requires a number.</strong></span>
    <span class="alert-error" ng-show="form[name].$error.required"><strong>*Required</strong></span> 

我的要求與原始問題中提出的要求略有不同,但希望我可以幫助遇到與我相同問題的人。

我必須根據范圍變量定義一個字段是否需要。所以我基本上必須設置ng-required="myScopeVariable" (這是一個布爾變量)。

<div class="align-left" ng-repeat="schema in schemas">
    <input type="text" ng-required="schema.Required" />
</div>

暫無
暫無

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

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