使用AngularJS在表单中进行动态验证和命名

IxD*_*Day 98 angularjs

我有这样的表格:http://jsfiddle.net/dfJeN/

如您所见,输入的名称值是静态设置的:

name="username"
Run Code Online (Sandbox Code Playgroud)

,表单验证工作正常(添加一些东西并删除输入中的所有文本,必须出现一个文本).

然后我尝试动态设置名称值:http://jsfiddle.net/jNWB8/

name="{input.name}"
Run Code Online (Sandbox Code Playgroud)

然后我将此应用于我的验证

login.{{input.name}}.$error.required
Run Code Online (Sandbox Code Playgroud)

(此模式将用于ng-repeat)但我的表单验证已被破坏.它在我的浏览器中被正确解释(如果我检查了我看到login.username.$ error.required的元素).

任何的想法 ?

编辑:在控制台中记录范围后,似乎是

{{input.name}}
Run Code Online (Sandbox Code Playgroud)

表达式不是插值的.我的表单为{{input.name}}属性,但没有用户名.

更新:从1.3.0-rc.3 name ="{{input.name}}"按预期工作.请参阅#1404

Ben*_*esh 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>
Run Code Online (Sandbox Code Playgroud)

可悲的是,这只是Angular的一个记录良好的功能.

  • 你最近是怎么解决这个问题的?我仍然没有看到这个特定答案如何与您的问题相关 - 因为它没有显示动态生成的表单字段和名称? (11认同)
  • 这是一个完整的解决方案(或解决方法)和angular团队建议的方法(来自http://docs.angularjs.org/api/ng.directive:form):"因为你不能动态生成输入元素的name属性使用插值,你必须在ngForm指令中包装每组重复输入,并将它们嵌套在外部表单元素中." 每个嵌套表单都有自己的范围,允许它工作. (7认同)
  • 此示例和建议仍未解决动态"名称".看起来他们希望允许您动态嵌套"克隆"字段集,但每个字段的基础名称必须是静态的. (2认同)
  • @thinice是的,它确实有帮助.使用此解决方案,名称不需要是动态的.它可以是你喜欢的任何东西(比如"foo").关键是子表单具有自己的作用域,因此验证表达式只能引用innerForm.foo.$ error等.然后,ng-model可以指向父作用域中您想要的任何内容(可能是动态的). (2认同)

Nic*_*ier 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>
Run Code Online (Sandbox Code Playgroud)

我使用此指令来帮助解决问题:

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);
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

现在,只要"dynamic-name"属性而不是"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>
Run Code Online (Sandbox Code Playgroud)


Pao*_*tti 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;
    });
}]);
Run Code Online (Sandbox Code Playgroud)

演示JSFiddle.


srf*_*rnk 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]);
        }
    };
}])
Run Code Online (Sandbox Code Playgroud)


小智 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);
    }]

  };
}]);
Run Code Online (Sandbox Code Playgroud)

这是一个掠夺性试验.这是详细的解释


Gab*_*bel 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>
Run Code Online (Sandbox Code Playgroud)

这是我在JSFiddle上的演示.