AngularJS를 사용한 양식의 동적 유효성 검사 및 이름


98

이 양식이 있습니다 : http://jsfiddle.net/dfJeN/

보시다시피 입력의 이름 값은 정적으로 설정됩니다.

name="username"

, 양식 유효성 검사가 제대로 작동합니다 (입력에서 무언가를 추가하고 모든 텍스트를 제거하면 텍스트가 표시되어야 함).

그런 다음 이름 값을 동적으로 설정하려고합니다. http://jsfiddle.net/jNWB8/

name="{input.name}"

그런 다음 이것을 내 유효성 검사에 적용합니다.

login.{{input.name}}.$error.required

(이 패턴은 ng-repeat에서 사용됩니다) 내 양식 유효성 검사가 손상되었습니다. 그것은 내 브라우저에서 올바르게 해석됩니다 (내가 login.username. $ error.required를 본 요소를 검사하면).

어떤 아이디어?

편집 : 콘솔에 범위를 기록한 후

{{input.name}}

식은 보간되지 않습니다. 내 양식은 {{input.name}} 속성이지만 사용자 이름은 없습니다.

업데이트 : 1.3.0-rc.3 name = "{{input.name}}"이 예상대로 작동합니다. # 1404를 참조하십시오


몇 가지 연구 끝에 "{{expression}} 바인딩보다 ngBind를 사용하는 것이 선호되는 시나리오는 Angular가 컴파일하기 전에 원시 상태로 브라우저에 일시적으로 표시되는 템플릿에 바인딩을 넣는 것이 바람직한 경우입니다." . 이 페이지 docs.angularjs.org/api/ng.directive:ngBind 에서 내가하려는 일에 대한 좋은 시작 인 것 같습니다. 해결책을 찾으면이 게시물이 업데이트됩니다.
— IxDay

열린 github 문제가 있습니다. github.com/angular/angular.js/issues/1404
— Yaroslav

답으로 문제를 해결하십시오. 그렇다면 점수 아래에있는 ckeckmark를 클릭하여 답으로 표시하십시오.
— Ricardo Souza 2014 년

다음은이 문제를 접한
— PFranchise

답변:


176

그런 식으로하려는 것을 할 수 없습니다.

ng-repeat와 같은 것을 사용하여 양식에 요소를 동적으로 추가해야한다고 가정하면 중첩 된 ng-form 을 사용하여 개별 항목의 유효성을 검사해야합니다.

<form name="outerForm">
<div ng-repeat="item in items">
   <ng-form name="innerForm">
      <input type="text" name="foo" ng-model="item.foo" />
      <span ng-show="innerForm.foo.$error.required">required</span>
   </ng-form>
</div>
<input type="submit" ng-disabled="outerForm.$invalid" />
</form>

안타깝게도 Angular의 잘 문서화 된 기능이 아닙니다.


11
결국 어떻게이 문제를 해결하게 되었습니까? 동적으로 생성 된 양식 필드와 이름을 표시하지 않기 때문에이 특정 답변이 문제와 어떻게 관련되어 있는지 여전히 알 수 없습니다.
— Oddman

7
이것은 완전한 솔루션 (또는 해결 방법)이며 angular 팀 ( docs.angularjs.org/api/ng.directive:form에서 ) 에서 제안한 접근 방식입니다 . "보간을 사용하여 입력 요소의 이름 속성을 동적으로 생성 할 수 없기 때문에 ngForm 지시문에서 각 반복 입력 세트를 래핑하고 외부 양식 요소에 중첩해야합니다. " 중첩 된 각 양식에는이 작업을 허용하는 자체 범위가 있습니다.
— Noremac 2013-12-05

2
이 예제와 제안은 여전히 ​​동적 "이름"을 다루지 않습니다. 동적으로 '복제 된'필드 집합을 중첩 할 수 있도록 허용하려는 것처럼 보이지만 각 필드의 기본 이름은 정적이어야합니다.
— thinice 2014 년

2
@thinice 네, 도움이됩니다. 이 솔루션을 사용하면 이름이 동적 일 필요가 없습니다. 원하는 것은 무엇이든 가능합니다 (예 : "foo"). 요점은 자식 폼에 자체 범위가 있으므로 유효성 검사 식은 innerForm.foo. $ error 등을 참조 할 수 있습니다. 그러면 ng-model은 부모 범위에서 원하는대로 (동적으로) 가리킬 수 있습니다.
— Jed Richards

@thinice-Wintamute가 맞습니다. 양식을 직접 제출하지 않기 때문에 동적 이름이 필요하지 않습니다. 의도는 일부 모델을 변경 한 다음 Ajax를 통해 게시하는 것입니다. 동적 이름은 그 시점에서 아무것도 할 수 없습니다. 실제로 HTML 양식 제출을 사용하는 경우 이상하거나 잘못된 작업을 수행하고 있으며 다른 접근 방식이 필요합니다.
— Ben Lesh 2014

44

중첩 된 ngForm을 사용하면 HTML 템플릿 내에서 특정 InputController에 액세스 할 수 있습니다. 그러나 다른 컨트롤러에서 액세스하려는 경우 도움이되지 않습니다.

예 :

<script>
  function OuterController($scope) {
    $scope.inputName = 'dynamicName';

    $scope.doStuff = function() {
      console.log($scope.formName.dynamicName); // undefined
      console.log($scope.formName.staticName); // InputController
    }
  }
</script>

<div controller='OuterController'>
  <form name='myForm'>
    <input name='{{ inputName }}' />
    <input name='staticName' />
  </form>
  <a ng-click='doStuff()'>Click</a>
</div>

이 지시문을 사용하여 문제를 해결합니다.

angular.module('test').directive('dynamicName', function($compile, $parse) {
  return {
    restrict: 'A',
    terminal: true,
    priority: 100000,
    link: function(scope, elem) {
      var name = $parse(elem.attr('dynamic-name'))(scope);
      // $interpolate() will support things like 'skill'+skill.id where parse will not
      elem.removeAttr('dynamic-name');
      elem.attr('name', name);
      $compile(elem)(scope);
    }
  };
});

이제 필요한 모든 곳에 'name'속성 대신 'dynamic-name'속성 만 동적 이름을 사용합니다.

예 :

<script>
  function OuterController($scope) {
    $scope.inputName = 'dynamicName';

    $scope.doStuff = function() {
      console.log($scope.formName.dynamicName); // InputController
      console.log($scope.formName.staticName); // InputController
    }
  }
</script>

<div controller='OuterController'>
  <form name='myForm'>
    <input dynamic-name='inputName' />
    <input name='staticName' />
  </form>
  <a ng-click='doStuff()'>Click</a>
</div>

1
I는 사용을 제외하고이 용액을 사용하는 $interpolate대신에 $parse더 유용 펠트
— TheRocketSurgeon

나는 당신이 termial : true를하는 것을 본다. 그게 무슨 뜻입니까? 이 지시문을 양식에도 사용할 수 있습니까 <form ng-repeat="item in items" dynamic-name="'item'+item.id"> ... <span ng-show="item{{item.id}}.$invalid">This form is invalid</span></form>?
— felixfbecker

16

이 문제는 Github에 대한 토론에 따라 AngularJS 1.3에서 수정되어야합니다 .

한편, @caitp 및 @Thinkscape가 만든 임시 솔루션은 다음 과 같습니다 .

// Workaround for bug #1404
// https://github.com/angular/angular.js/issues/1404
// Source: http://plnkr.co/edit/hSMzWC?p=preview
app.config(['$provide', function($provide) {
    $provide.decorator('ngModelDirective', function($delegate) {
        var ngModel = $delegate[0], controller = ngModel.controller;
        ngModel.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
            var $interpolate = $injector.get('$interpolate');
            attrs.$set('name', $interpolate(attrs.name || '')(scope));
            $injector.invoke(controller, this, {
                '$scope': scope,
                '$element': element,
                '$attrs': attrs
            });
        }];
        return $delegate;
    });
    $provide.decorator('formDirective', function($delegate) {
        var form = $delegate[0], controller = form.controller;
        form.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
            var $interpolate = $injector.get('$interpolate');
            attrs.$set('name', $interpolate(attrs.name || attrs.ngForm || '')(scope));
            $injector.invoke(controller, this, {
                '$scope': scope,
                '$element': element,
                '$attrs': attrs
            });
        }];
        return $delegate;
    });
}]);

JSFiddle 데모 .


1
ng 1.2에 갇힌 사람들에게 이것은 쉽게 '해키'가 가장 적은 수정입니다.
— 수류탄

14

@EnISeeK에 의해 좋은 것입니다.

.directive("dynamicName",[function(){
    return {
        restrict:"A",
        require: ['ngModel', '^form'],
        link:function(scope,element,attrs,ctrls){
            ctrls[0].$name = scope.$eval(attrs.dynamicName) || attrs.dynamicName;
            ctrls[1].$addControl(ctrls[0]);
        }
    };
}])

1
나는 다음을 추가합니다. ctrls [0]. $ name = scope. $ eval (attrs.dynamicName) || attrs.dynamicName;
— GnrlBzik 2014 년

7

EnlSeek 솔루션에 비해 약간의 개선

angular.module('test').directive('dynamicName', ["$parse", function($parse) {
 return {
    restrict: 'A',
    priority: 10000, 
    controller : ["$scope", "$element", "$attrs", 
           function($scope, $element, $attrs){
         var name = $parse($attrs.dynamicName)($scope);
         delete($attrs['dynamicName']);
         $element.removeAttr('data-dynamic-name');
         $element.removeAttr('dynamic-name');
          $attrs.$set("name", name);
    }]

  };
}]);

여기 플 런커 재판이 있습니다. 여기에 자세한 explantion입니다


+1, EnlSeek의 지시문이 제 지시문에서 무한 루프를 일으켰습니다. 그래도 작동하려면이 답변의 'fx'부분을 제거해야했습니다.
— John

우선 순위는 동일한 이름을 가정하지만 ng-if를 갖는 필드 세트를 방해 할 수 있습니다. 예 : <input type = 'text'dynamic-name = 'foo'ng-if = 'field.type == "text"/> <textarea dynamic-name ='foo 'ng-if ='field.type == "textarea"> </ textarea> 'priority : 10000'을 제거하면 문제가 해결되었지만 여전히 제대로 작동하는 것 같습니다.
— thinice

ngIf의 우선 순위는 600입니다.이 지시문에 대해 600 미만의 우선 순위를 지정하면 ngIf와 함께 작동합니다.
— jason zhang

우선 순위가 설정되지 않은 경우 (기본값은 0),이 지시문이 ngModel보다 먼저 평가되면 ngModel (우선 순위 0)과 함께 작동 할 수 있습니다. ngModel이 컴파일 / 링크되기 전에 항상 우선 순위를 부여하고 싶습니다.
— jason zhang

5

@caitp 및 @Thinkscape 솔루션을 약간 확장하여 다음과 같이 동적으로 생성 된 중첩 ng-forms 를 허용 합니다.

<div ng-controller="ctrl">
    <ng-form name="form">
        <input type="text" ng-model="static" name="static"/>

        <div ng-repeat="df in dynamicForms">
            <ng-form name="form{{df.id}}">
                <input type="text" ng-model="df.sub" name="sub"/>
                <div>Dirty: <span ng-bind="form{{df.id}}.$dirty"></span></div>
            </ng-form>
        </div>

        <div><button ng-click="consoleLog()">Console Log</button></div>
        <div>Dirty: <span ng-bind="form.$dirty"></span></div>
    </ng-form>      
</div>

다음은 JSFiddle 에 대한 내 데모입니다 .


4

저는 Ben Lesh의 솔루션을 사용했으며 저에게 잘 작동합니다. 그러나 내가 직면 한 한 가지 문제는를 사용하여 내부 양식을 추가했을 때 지시문 을 사용하는 경우 ng-form모든 양식 상태 form.$valid, form.$error등이 정의되지 않았다는 것 ng-submit입니다.

예를 들어 이것을 가지고 있다면 :

<form novalidate ng-submit="saveRecord()" name="outerForm">
    <!--parts of the outer form-->
    <ng-form name="inner-form">
      <input name="someInput">
    </ng-form>
    <button type="submit">Submit</button>
</form>

그리고 내 컨트롤러에서 :

$scope.saveRecord = function() {
    outerForm.$valid // this is undefined
}

따라서 양식을 제출하기 위해 일반 클릭 이벤트를 사용하여 돌아 가야했습니다.이 경우 양식 객체를 전달해야합니다.

<form novalidate name="outerForm">  <!--remove the ng-submit directive-->
    <!--parts of the outer form-->
    <ng-form name="inner-form">
      <input name="someInput">
    </ng-form>
    <button type="submit" ng-click="saveRecord(outerForm)">Submit</button>
</form>

그리고 수정 된 컨트롤러 방법 :

$scope.saveRecord = function(outerForm) {
    outerForm.$valid // this works
}

나는 이것이 왜 그런지 잘 모르겠지만 누군가에게 도움이되기를 바랍니다.


3

이 문제는 Angular 1.3+에서 수정되었습니다. 다음은 수행하려는 작업에 대한 올바른 구문입니다.

login[input.name].$invalid

0

아래와 같이 입력에 대한 동적 이름을 설정하면

<input name="{{dynamicInputName}}" />

그런 다음 아래 코드와 같이 동적 이름에 대해 세트 유효성 검사를 사용했습니다.

<div ng-messages="login.dynamicInputName.$error">
   <div ng-message="required">
   </div>
</div>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.