web-dev-qa-db-fra.com

Angular ng-repeat avec ng-form, accès à la validation dans le contrôleur

J'essaie de générer une liste modifiable en utilisant ng-repeat. Je veux rappeler à l'utilisateur de mettre à jour toutes les modifications avant de continuer, donc j'utilise ng-form Pour créer des formulaires "imbriqués" à la volée car la documentation dit que je peux ensuite utiliser la validation sur ces entrées créées dynamiquement.

Bien que cela semble fonctionner dans le HTML, je ne vois pas comment accéder à ces formulaires créés dynamiquement et aux champs de validation associés dans le contrôleur. Plus précisément, lorsque l'utilisateur modifie l'entrée, j'utilise la propriété form $ dirty pour afficher un bouton pour indiquer à l'utilisateur de valider les modifications. Jusqu'ici tout va bien. Cependant, une fois les modifications validées, je souhaite $setPristine() sur le champ pour indiquer que les modifications ont été définies. Il peut y avoir d'autres moyens de s'assurer que les modifications sont validées sur chaque entrée avant d'autoriser le formulaire principal, mais c'était le mieux que je pouvais trouver.

Malheureusement, même si la documentation indique que si je nomme le ng-form, il sera propagé à l'objet $scope, Je ne peux pas trouver un moyen d'y accéder. $scope.dynamic_form N'est pas défini.

Voici un plongeur montrant ce que je veux dire:

plnk

Merci!

[EDIT] Juste pour ajouter au problème, ce qui fonctionne pour cet exemple spécifique est d'ajouter au ng-click Sur l'entrée créée dynamiquement:

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

Mais je n'ai toujours pas accès au formulaire créé dynamiquement dans le contrôleur. Je voudrais, par exemple, ajouter un observateur au namesForm.name.$pristine Afin que je puisse définir la mainForm.$setValidity(false) chaque fois que le sous-formulaire est $dirty Pour empêcher l'utilisateur de soumettre le formulaire principal jusqu'à ce que toutes les modifications de sous-formulaire aient été validées.

Donc, en un mot, la question est de savoir comment accéder dans un contrôleur parent aux valeurs de validation d'un ngForm imbriqué créé dynamiquement?

33
MyTimeFinder

Mise à jour 2015-01-17:

Comme l'a souligné Leblanc Meneses dans les commentaires Angular 1.3 prend désormais en charge l'interpolation avec form, ngForm et input directives.

Cela signifie que l'utilisation d'expressions pour nommer vos éléments:

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

fonctionnera comme prévu:

$scope['namesForm_0']
$scope.namesForm_1

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

Réponse originale pour Angular <= 1.2:

Travailler avec des formulaires et le ngFormController peut devenir délicat assez rapidement.

Vous devez savoir que vous pouvez ajouter dynamiquement des éléments de formulaire et des entrées, mais ils ne peuvent pas être nommés dynamiquement - l'interpolation ne fonctionne pas dans le ngForm ou name directives.

Par exemple, si vous avez essayé de nommer dynamiquement vos formulaires imbriqués comme ceci:

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

Au lieu de rendre tous les formulaires imbriqués disponibles sur la portée comme ceci: scope['namesForm_0'] vous n'auriez accès qu'au seul (dernier) formulaire avec le nom littéral scope['namesForm_{{$index}}'].

Dans votre situation, vous devez créer une directive personnalisée qui sera ajoutée avec ngFormpour gérer le paramètre $pristine$ et $invalid pour cette instance de formulaire.

JavaScript:

Cette directive surveillera le $dirty état de sa forme pour définir le $validity pour empêcher la soumission lorsqu'il est sale et gérer le réglage du $pristine indique lorsque le bouton "nettoyer" est enfoncé.

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:

Ensuite, la seule modification de votre code HTML consiste à ajouter la directive formCleaner.

Modifiez donc votre code HTML d'origine à partir de ceci:

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

à cela, en ajoutant form-cleaner à côté de 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>

Voici un Plunker mis à jour montrant le nouveau comportement: http://plnkr.co/edit/Lxem5HJXe0UCvslqbJr3?p=preview

44
Sly_cardinal