ng-repeat, ng-show (각도)를 사용하여 동적으로 생성 된 입력의 유효성을 검사하는 방법


167

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 /


4
2015 년에이 글을 읽는 사람들에게 ... 가장 많이 투표 된 답변은 더 이상 정답이 아닙니다. 아래를보세요. :)
Will Strohl

이것은 @WillStrohl이 말하는 "2015 년"의 대답 인 것 같습니다.
osiris

여기에 적절한 에티켓은 무엇입니까? 수락 된 답변은 당시에 정확 했으므로 오늘 그대로 두어야합니까? 새로운 방문자에게 도움이 될 것으로 보이는이 인기있는 스레드를 원하면됩니다.
PFranchise 2018

@PFranchise, 몰라요하지만 눈에 띄는 메모가 도움이 될 것이라고 생각합니다. 귀하의 질문에 대한 수정 사항으로, 더 많은 사람들이 볼 수있는 곳에 메모가 남아있을 수 있습니다.
osiris

답변:


197

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/


2
훌륭합니다. 그러나 동일한 이름을 가진 여러 개의 텍스트 상자를 갖는 것이 유효한 HTML입니까?
Ian Warburton

1
중첩 형태가 유효한 HTML로 간주 밤은 stackoverflow.com/questions/379610/can-you-nest-html-forms 각 계획이에 대한 수정인가?
Blowsie

11
당신이 @Blowsie 하지 여기에 실제 양식을 중첩 아니라 ng-form다른 SO 질문에 대한 링크는 여기에 해당되지 않도록 요소를 DOM.
pkozlowski.opensource

7
큰. 당신 ng-repeat이 묶여 있다면 attr table tr을 사용해야 한다는 것을 알아야합니다 ng-form="myname".
ivkremer

11
이 답변은 편집해야합니다. github.com/angular/angular.js/issues/1404 문제 는 AngularJS 1.3.0 (2014 년 9 월부터 커밋) 이후 해결되었습니다
tanguy_k

228

질문이 제기 된 이후 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를 도입했습니다. ngMessage와 동일한 기술을 사용할 수 있습니다.

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

2
이것은 지시문을 수행하는 것보다 완벽하고 훨씬 쉽습니다. 양식을 구성 요소에 전달 하고이 방법을 사용할 수 있습니다. 고마워 친구!
dinkydani 2016 년

이 형식을 사용하려면 양식 이름에 하이픈을 사용할 수 없습니다. 이것이 왜 그런지 아는 사람이 있습니까?
Patrick Szalapski 2016 년

@PatrickSzalapski : 양식 이름이 Angular에서 사용되고 하이픈이있는 변수 이름이 Javascript에서 유효한 구문이 아니기 때문입니다. 해결 방법 : <span ng-show = "vm [ 'my-form'] [ 'person_'+ $ index]. $ invalid"> 이름 입력 </ span>
HoffZ 2016 년

반복되는 항목을 동적으로 제거 $valid하면 입력 의 속성이 잘못됩니다.false
jonathanwiesel

양식의 맨 위와 같이 한 곳에서 모든 오류를 표시하고 싶습니까?
codingbbq

13

ng-form을 사용하지 않으려면 양식의 이름 속성을 변경하는 사용자 지정 지시문을 사용할 수 있습니다. 이 지시문을 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

ng- 옵션 원숭이 패치에 대한 Github 의견 : https://github.com/angular/angular.js/commit/9ee2cdff44e7d496774b340de816344126c457b3#commitcomment-6832095 https://twitter.com/aljohri/status/482963541520314369

최신 정보:

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

3
명확하게하기 위해이 답변이 선택되지 않았다는 것이 최선의 답변이 아님을 나타내는 것은 아닙니다. 질문이 처음 제기 된 지 거의 2 년 만에 게시되었습니다. 이 같은 문제가 발생하면 선택한 답변 외에도이 답변과 tomGreen을 모두 고려할 것입니다.
PFranchise 2014

11

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


받아 들인 대답이 효과가 없었습니다. 그러나 이것은했다. (나는 Angular 2.1.14를 사용한다)
Jesper Tejlgaard

+1이 답변이 나를 위해 일한 링크를 확인 : 방금 추가 할 필요가 ng-form="formName"반복 겨 ...이 : 마법처럼 일했다 태그에
압델 Alaoui

3

컨트롤러 측면에 "사용자 지정 유효성 검사"가 포함 된보다 복잡한 예제가 추가되었습니다. 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>

1

이 솔루션을 살펴보면 위의 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 에 대한 더 완전한 작업 예제가 있습니다.


1

다음 구문을 사용하면 유효성 검사가 ng repeat으로 작동 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>

1

pkozlowski.opensource의 바탕 대답 , 내가 가진 그 또한 작업 동적 입력 이름을 가지고 할 수있는 방법이 추가되었습니다 ngMessages을 . 요소와의 사용법 ng-init에 유의하십시오 . 의 속성에 대한 변수 값을 포함하는 변수 이름이됩니다 .ng-formfurryNamefurryNameinputname

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

1

너무 늦었지만 누구에게나 도움이 될 수 있습니다.

  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>

실제 데모 보기


1

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-show를 ng-pattern으로 보여줘야합니다

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

0

가능하며 여기에 입력 테이블로 동일한 작업을 수행하는 방법이 있습니다.

테이블을 이렇게 포장하십시오

그런 다음 사용하십시오

입력, 선택 등을 포함하는 다중 중첩 지시문이있는 양식이 있습니다 ...이 요소는 모두 ng-repeats 및 동적 문자열 값으로 묶습니다.

지시문을 사용하는 방법은 다음과 같습니다.

<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 여부를 묻습니다.


0

그러면 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> 


0

여기 내가 어떻게하는지에 대한 예는 그것이 최선의 해결책인지는 모르지만 완벽하게 작동합니다.

먼저 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);
       };

0

내 요구 사항은 원래 질문에 대한 요구 사항과 약간 달랐지만, 내가했던 것과 같은 문제를 겪고있는 사람을 도울 수 있기를 바랍니다.

범위 변수를 기반으로 필드가 필요한지 여부를 정의해야했습니다. 따라서 기본적으로 ng-required="myScopeVariable"(부울 변수) 를 설정해야했습니다 .

<div class="align-left" ng-repeat="schema in schemas">
    <input type="text" ng-required="schema.Required" />
</div>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.