web-dev-qa-db-ja.com

Angular ng-repeat with ng-form、コントローラーの検証にアクセスする

_ng-repeat_を使用して編集可能なリストを生成しようとしています。先に進む前に編集内容を更新するようユーザーに思い出させたいので、ドキュメントでこれらの動的に作成された入力で検証を使用できると書かれているので、_ng-form_を使用してその場で「ネスト」フォームを作成しています。

これはHTML内で機能するように見えますが、コントローラーで動的に作成されたフォームと関連する検証フィールドにアクセスする方法がわかりません。具体的には、ユーザーが入力を変更する場合、フォームの$ dirtyプロパティを使用して、変更をコミットするようユーザーに指示するボタンを表示します。ここまでは順調ですね。ただし、変更がコミットされたら、フィールドで$setPristine()を使用して、変更が設定されたことを示します。メインフォームのコミットを許可する前に、各入力で変更をコミットすることを保証する他の方法があるかもしれませんが、これは私が思いつく最高のものでした。

残念ながら、ドキュメントではng-formに名前を付けると_$scope_オブジェクトに伝播されると書かれていますが、それにアクセスする方法が見つかりません。 _$scope.dynamic_form_は未定義です。

ここに私が意味することを示すプランカーがあります:

plnk

ありがとう!

[編集]問題に追加するために、この特定の例で機能するのは、動的に作成された入力の_ng-click_に追加することです。

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

しかし、コントローラーで動的に作成されたフォームにはまだアクセスできません。たとえば、サブフォームが_namesForm.name.$pristine_である場合は常にmainForm.$setValidity(false)を設定できるように_$dirty_にウォッチャーを追加して、ユーザーがメインを送信できないようにしますすべてのサブフォームの変更がコミットされるまでフォーム。

簡単に言えば、問題は、動的に作成されたネストされたngFormの検証値に親コントローラーでアクセスする方法ですか?

33
MyTimeFinder

2015-01-17に更新:

Leblanc Menesesがコメントで指摘したようにAngular 1.3formngFormおよびinputディレクティブ。

これは、式を使用して要素に名前を付けることを意味します。

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

期待どおりに動作します:

$scope['namesForm_0']
$scope.namesForm_1

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

Angular <= 1.2:の元の答え

フォームとngFormControllerを使用すると、かなりすぐにトリッキーになります。

フォーム要素と入力を動的に追加できるが、動的に名前を付けることはできないことに注意する必要がありますnamed-ngFormまたはnameディレクティブ。

たとえば、次のようにネストされたフォームに動的に名前を付けようとした場合:

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

次のようなスコープですべてのネストされたフォームを使用可能にする代わりに:scope['namesForm_0']リテラル名scope['namesForm_{{$index}}']を持つ単一の(最後の)フォームにのみアクセスできます。

あなたの状況では、そのフォームインスタンスの$pristine$$invalidの設定を処理するためにngFormとともに追加されるカスタムディレクティブを作成する必要があります。

JavaScript:

このディレクティブは、フォームの$dirty状態を監視して$validityを設定し、ダーティな場合の送信を防止し、 'clean'ボタンが押された場合の$pristine状態の設定を処理します。

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への唯一の変更は、formCleanerディレクティブを追加することです。

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

これに、form-cleanerの隣にng-formを追加して:

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

新しい動作を示す更新されたPlunkerは次のとおりです。 http://plnkr.co/edit/Lxem5HJXe0UCvslqbJr3?p=preview

44
Sly_cardinal