角ng-repeat with ng-form,访问控制器验证

时间:2022-12-25 23:14:40

I am trying to generate an editable list using ng-repeat. I want to remind the user to update any edits before moving on, so I am using ng-form to create "nested" forms on the fly because the documentation says I can then use validation on these dynamically created inputs.

我正在尝试使用ng-repeat生成一个可编辑的列表。我想提醒用户在继续之前更新任何编辑,所以我使用ng-form动态地创建“嵌套”表单,因为文档说我可以对这些动态创建的输入使用验证。

While that seems to work within the HTML, I don't see how to access those dynamically created forms and related validation fields in the controller. Specifically, when the user changes the input I use the form $dirty property to bring up a button to tell the user to commit the changes. So far, so good. However, once the changes are committed I want to $setPristine() on the field to indicate that the changes have been set. There may be other ways of ensuring that changes are committed on each input before I allow the main form committed, but this was the best I could come up with.

虽然这在HTML中似乎可行,但我不知道如何访问控制器中动态创建的表单和相关的验证字段。具体地说,当用户更改输入时,我使用表单$dirty属性打开一个按钮,告诉用户提交更改。到目前为止还好。然而,一旦我要提交更改setPristine美元()在球场上表明变化集。可能还有其他的方式确保更改在每个输入在我允许的主要形式,但这是我能想出最好的。

Unfortunately, even though the documentation says that if I name the ng-form it will be propagated to the $scope object, I can't find a way to access it. $scope.dynamic_form is undefined.

不幸的是,即使文档说如果我给ng-form命名,它将被传播到$scope对象,我还是找不到访问它的方法。美元的范围。dynamic_form是未定义的。

Here is a plunker showing what I mean:

这里有一个柱塞显示了我的意思:

plnk

plnk

Thanks!

谢谢!

[EDIT] Just to add to the issue, what does work for this specific example is to add to the ng-click on the dynamically created input:

[编辑]为了增加问题的严重性,在这个特定的例子中,要做的是在动态创建的输入中添加ng-click:

ng-click="namesForm.name.$setPristine();clean()"

But I still don't have access to the dynamically created form in the controller. I would like, for example, to add a watcher to the namesForm.name.$pristine so that I can set the mainForm.$setValidity(false) whenever the sub-form is $dirty to prevent the user from submitting the main form until all sub-form changes have been committed.

但是我仍然不能访问控制器中动态创建的表单。例如,我想在namesForm.name中添加一个监视程序。$ pure,这样我就可以设置主窗体。

So in a nutshell, the issue is how to access in a parent controller the validation values of a dynamically created nested ngForm?

总之,问题是如何在父控制器中访问动态创建的嵌套ngForm的验证值?

1 个解决方案

#1


40  

Updated 2015-01-17:

更新2015-01-17:

As pointed out by Leblanc Meneses in the comments Angular 1.3 now supports interpolation with form, ngForm and input directives.

正如Leblanc Meneses在评论中指出的那样,角1.3现在支持使用表单、ngForm和输入指令进行插值。

This means that using expressions to name your elements:

这意味着使用表达式来命名元素:

<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
    <input type="text"
           name="input_{{$index}}_0"></input>
    <!-- ... -->
</div>  

will work as expected:

将正常工作:

$scope['namesForm_0']
$scope.namesForm_1

// Access nested form elements:
$scope.namesForm_1.input_1_0
...

Original answer for Angular <= 1.2:

角<= 1.2:

Working with forms and the ngFormController can get tricky pretty quickly.

使用表单和ngFormController会很快变得棘手。

You need to be aware that you can dynamically add form elements and inputs but they can't be dynamically named - interpolation does not work in the ngForm or name directives.

您需要知道,您可以动态地添加表单元素和输入,但是不能动态地命名它们——插补在ngForm或名称指令中不起作用。

For example, if you tried to name your nested forms dynamically like this:

例如,如果您想动态地命名嵌套窗体,如下所示:

<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
    <!-- ... -->
</div>  

Instead of making all the nested forms available on the scope like this: scope['namesForm_0'] you would only have access to the single (last) form with the literal name scope['namesForm_{{$index}}'].

而不是让所有嵌套的表单在作用域上可用,比如:作用域['namesForm_0'],您将只能使用字面名称作用域['namesForm_{$index}]访问单个(最后一个)窗体。

In your situation you need to create a custom directive that will be added along with ngFormto handle setting $pristine$ and $invalid for that form instance.

在您的情况下,您需要创建一个自定义指令,该指令将与ngFormto一起添加,用于处理为该表单实例设置的原始$和$invalid。

JavaScript:

JavaScript:

This directive will watch the $dirty state of its form to set the $validity to prevent submission when dirty and handle setting the $pristine state when the 'clean' button is pressed.

该指令将监视其窗体的$dirty状态,设置$validity以防止在脏时提交,并在按下“clean”按钮时处理设置$原始状态。

app.directive('formCleaner', function(){
    return {
        scope: true,
        require: '^form',
        link: function(scope, element, attr){
            scope.clean = function () {
                scope.namesForm.$setPristine();
            };

            scope.$watch('namesForm.$dirty', function(isDirty){
                scope.namesForm.$setValidity('name', !isDirty);
            });
        }
    };
});

HTML:

HTML:

Then the only change to your HTML is to add the formCleaner directive.

然后,对HTML的唯一更改就是添加formCleaner指令。

So change your original HTML from this:

因此,改变你原来的HTML从这里:

<body ng-controller="MainCtrl">
    <form name="mainForm" submit="submit()">
        <h3>My Editable List</h3>
        <div ng-form="namesForm"
             ng-repeat="name in names">
            <!-- ... -->
        </div>
        <button class="btn btn-default" type="submit">Submit</button>
    </form>
</body>

to this, by adding form-cleaner next to ng-form:

为此,在ng-form旁边添加form-cleaner:

<body ng-controller="MainCtrl">
    <form name="mainForm" submit="submit()">
        <h3>My Editable List</h3>

        <!-- Add the `form-cleaner` directive to the element with `ng-form` -->
        <div form-cleaner
             ng-form="namesForm"
             ng-repeat="name in names">
            <!-- ... -->
        </div>
        <button class="btn btn-default" type="submit">Submit</button>
    </form>
</body>

Here is an updated Plunker showing the new behaviour: http://plnkr.co/edit/Lxem5HJXe0UCvslqbJr3?p=preview

这里有一个更新的柱塞显示了新的行为:http://plnkr.co/edit/lxem5hjxe0ucvslqbjr3?

#1


40  

Updated 2015-01-17:

更新2015-01-17:

As pointed out by Leblanc Meneses in the comments Angular 1.3 now supports interpolation with form, ngForm and input directives.

正如Leblanc Meneses在评论中指出的那样,角1.3现在支持使用表单、ngForm和输入指令进行插值。

This means that using expressions to name your elements:

这意味着使用表达式来命名元素:

<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
    <input type="text"
           name="input_{{$index}}_0"></input>
    <!-- ... -->
</div>  

will work as expected:

将正常工作:

$scope['namesForm_0']
$scope.namesForm_1

// Access nested form elements:
$scope.namesForm_1.input_1_0
...

Original answer for Angular <= 1.2:

角<= 1.2:

Working with forms and the ngFormController can get tricky pretty quickly.

使用表单和ngFormController会很快变得棘手。

You need to be aware that you can dynamically add form elements and inputs but they can't be dynamically named - interpolation does not work in the ngForm or name directives.

您需要知道,您可以动态地添加表单元素和输入,但是不能动态地命名它们——插补在ngForm或名称指令中不起作用。

For example, if you tried to name your nested forms dynamically like this:

例如,如果您想动态地命名嵌套窗体,如下所示:

<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
    <!-- ... -->
</div>  

Instead of making all the nested forms available on the scope like this: scope['namesForm_0'] you would only have access to the single (last) form with the literal name scope['namesForm_{{$index}}'].

而不是让所有嵌套的表单在作用域上可用,比如:作用域['namesForm_0'],您将只能使用字面名称作用域['namesForm_{$index}]访问单个(最后一个)窗体。

In your situation you need to create a custom directive that will be added along with ngFormto handle setting $pristine$ and $invalid for that form instance.

在您的情况下,您需要创建一个自定义指令,该指令将与ngFormto一起添加,用于处理为该表单实例设置的原始$和$invalid。

JavaScript:

JavaScript:

This directive will watch the $dirty state of its form to set the $validity to prevent submission when dirty and handle setting the $pristine state when the 'clean' button is pressed.

该指令将监视其窗体的$dirty状态,设置$validity以防止在脏时提交,并在按下“clean”按钮时处理设置$原始状态。

app.directive('formCleaner', function(){
    return {
        scope: true,
        require: '^form',
        link: function(scope, element, attr){
            scope.clean = function () {
                scope.namesForm.$setPristine();
            };

            scope.$watch('namesForm.$dirty', function(isDirty){
                scope.namesForm.$setValidity('name', !isDirty);
            });
        }
    };
});

HTML:

HTML:

Then the only change to your HTML is to add the formCleaner directive.

然后,对HTML的唯一更改就是添加formCleaner指令。

So change your original HTML from this:

因此,改变你原来的HTML从这里:

<body ng-controller="MainCtrl">
    <form name="mainForm" submit="submit()">
        <h3>My Editable List</h3>
        <div ng-form="namesForm"
             ng-repeat="name in names">
            <!-- ... -->
        </div>
        <button class="btn btn-default" type="submit">Submit</button>
    </form>
</body>

to this, by adding form-cleaner next to ng-form:

为此,在ng-form旁边添加form-cleaner:

<body ng-controller="MainCtrl">
    <form name="mainForm" submit="submit()">
        <h3>My Editable List</h3>

        <!-- Add the `form-cleaner` directive to the element with `ng-form` -->
        <div form-cleaner
             ng-form="namesForm"
             ng-repeat="name in names">
            <!-- ... -->
        </div>
        <button class="btn btn-default" type="submit">Submit</button>
    </form>
</body>

Here is an updated Plunker showing the new behaviour: http://plnkr.co/edit/Lxem5HJXe0UCvslqbJr3?p=preview

这里有一个更新的柱塞显示了新的行为:http://plnkr.co/edit/lxem5hjxe0ucvslqbjr3?