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.