web-dev-qa-db-fra.com

Comment réinitialiser un formulaire, notamment en supprimant toutes les erreurs de validation?

J'ai une forme angulaire. Les champs sont validés à l'aide de l'attribut ng-pattern. J'ai aussi un bouton de réinitialisation. J'utilise le Ui.Utils Event Binder pour gérer l'événement reset comme suit:

<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
  <div>
    <label>
      Area Code
      <input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
    </label>

    <div ng-messages="searchForm.areaCode.$error">
      <div class="error" ng-message="pattern">The area code must be three digits</div>
    </div>
  </div>

  <div>
    <label>
      Phone Number
      <input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
    </label>

    <div ng-messages="searchForm.phoneNumber.$error">
      <div class="error" ng-message="pattern">The phone number must be seven digits</div>
    </div>
  </div>

  <br>
  <div>
    <button type="reset">Reset</button>
    <button type="submit" ng-disabled="searchForm.$invalid">Search</button>
  </div>
</form>

Comme vous pouvez le constater, lorsque le formulaire est réinitialisé, il appelle la méthode reset sur le $scope. Voici à quoi ressemble le contrôleur entier:

angular.module('app').controller('mainController', function($scope) {
    $scope.resetCount = 0;

    $scope.reset = function(form) {
        form.$setPristine();
        form.$setUntouched();
        $scope.resetCount++;
    };

    $scope.search = function() {
        alert('Searching');
    };
});

J'appelle form.$setPristine() et form.$setUntouched, en suivant les conseils de une autre question ici sur Stack Overflow. La seule raison pour laquelle j'ai ajouté le compteur était pour prouver que le code était appelé (quel qu'il soit).

Le problème est que même après la réinitialisation du formulaire, les messages de validation ne disparaissent pas. Vous pouvez voir le code complet sur Plunker . Voici une capture d'écran montrant que les erreurs ne disparaissent pas:

Validation Errors

22
battmanz

J'ai commencé avec le commentaire de @Brett et l'ai construit. En fait, j'ai plusieurs formulaires et chaque formulaire comporte plusieurs champs (plus que les deux indiqués). Je voulais donc une solution générale.

J'ai remarqué que l'objet Angular form possède une propriété pour chaque contrôle (entrée, sélection, textarea, etc.) ainsi que d'autres propriétés angulaires. Cependant, chacune des propriétés angulaires commence par un signe dollar ($). Alors j'ai fini par faire ceci (y compris le commentaire pour le bénéfice des autres programmeurs):

$scope.reset = function(form) {
    // Each control (input, select, textarea, etc) gets added as a property of the form.
    // The form has other built-in properties as well. However it's easy to filter those out,
    // because the Angular team has chosen to prefix each one with a dollar sign.
    // So, we just avoid those properties that begin with a dollar sign.
    let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);

    // Set each control back to undefined. This is the only way to clear validation messages.
    // Calling `form.$setPristine()` won't do it (even though you wish it would).
    for (let name of controlNames) {
        let control = form[name];
        control.$setViewValue(undefined);
    }

    form.$setPristine();
    form.$setUntouched();
};
21
battmanz
      $scope.search = {areaCode: xxxx, phoneNumber: yyyy}

Structurez tous les modèles de votre formulaire en un seul endroit, comme ci-dessus, afin que vous puissiez le supprimer comme suit:

      $scope.search = angular.copy({});

Après cela, vous pouvez simplement appeler ceci pour réinitialiser la validation:

      $scope.search_form.$setPristine();
      $scope.search_form.$setUntouched();
      $scope.search_form.$rollbackViewValue();
7
Antoni Xu

Il ne semble pas y avoir de moyen facile de réinitialiser les erreurs $ en angulaire. Le meilleur moyen serait probablement de recharger la page actuelle pour commencer avec un nouveau formulaire. Sinon, vous devez supprimer tous les $ error manuellement avec ce script:

form.$setPristine(true);
form.$setUntouched(true);

// iterate over all from properties
angular.forEach(form, function(ctrl, name) {
  // ignore angular fields and functions
  if (name.indexOf('$') != 0) {
    // iterate over all $errors for each field        
    angular.forEach(ctrl.$error, function(value, name) {
      // reset validity
      ctrl.$setValidity(name, null);
    });
  }
});
$scope.resetCount++; 
5
Michael

Vous pouvez ajouter un indicateur de validation et afficher ou masquer les erreurs en fonction de sa valeur avec ng-if ou ng-show dans votre code HTML. Le formulaire comporte un indicateur $ valide que vous pouvez envoyer à votre contrôleur.

ng-if supprimera ou recréera l'élément dans le DOM, alors que ng-show l'ajoutera mais ne l'affichera pas (selon la valeur de l'indicateur). 

EDIT: Comme l'a souligné Michael, si le formulaire est désactivé, la façon dont j'ai pointé ne fonctionnera pas car le formulaire n'est jamais soumis. Mis à jour le code en conséquence.

HTML

<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
  <div>
    <label>
      Area Code
      <input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
    </label>

    <div ng-messages="searchForm.areaCode.$error">
      <div class="error" ng-message="pattern" ng-if="searchForm.areaCode.$dirty">The area code must be three digits</div>
    </div>
  </div>

  <div>
    <label>
      Phone Number
      <input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
    </label>

    <div ng-messages="searchForm.phoneNumber.$error">
      <div class="error" ng-message="pattern" ng-if="searchForm.phoneNumber.$dirty">The phone number must be seven digits</div>
    </div>
  </div>

  <br>
  <div>
    <button type="reset">Reset</button>
    <button type="submit" ng-disabled="searchForm.$invalid">Search</button>
  </div>
</form>

JS

$scope.search = function() {
    alert('Searching');
};

$scope.reset = function(form) {
     form.$setPristine();
     form.$setUntouched();
     $scope.resetCount++;
 };

Codepen avec solution de travail: http://codepen.io/anon/pen/zGPZoB

5
Ariel

Suivre a travaillé pour moi

let form = this.$scope.myForm;   
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
for (let name of controlNames) {
    let control = form [name];
    control.$error = {};
}

En bref: pour vous débarrasser des erreurs ng-messages, vous devez effacer l'objet$ errorpour chaque élément de formulaire.

2
Sumeet

On dirait que je dois avoir le bon comportement sous reset. Malheureusement, l’utilisation de la norme reset a échoué. Je n'inclus pas non plus la bibliothèque ui-event. Mon code est donc un peu différent du vôtre, mais il fait ce dont vous avez besoin.

<form name="searchForm" id="searchForm" ng-submit="search()">
  pristine = {{searchForm.$pristine}} valid ={{searchForm.$valid}}
  <div>
    <label>
      Area Code
      <input type="tel" required name="areaCode" ng-model="obj.areaCode" ng-pattern="/^([0-9]{3})?$/" ng-model-options="{ allowInvalid: true }">
    </label>

    <div ng-messages="searchForm.areaCode.$error">
      <div class="error" ng-message="pattern">The area code must be three digits</div>
      <div class="error" ng-message="required">The area code is required</div>
    </div>
  </div>

  <div>
    <label>
      Phone Number
      <input type="tel" required name="phoneNumber" ng-model="obj.phoneNumber" ng-pattern="/^([0-9]{7})?$/" ng-model-options="{ allowInvalid: true }">
    </label>

    <div ng-messages="searchForm.phoneNumber.$error">
      <div class="error" ng-message="pattern">The phone number must be seven digits</div>
      <div class="error" ng-message="required">The phone number is required</div>
    </div>
  </div>

  <br>
  <div>
    <button ng-click="reset(searchForm)" type="reset">Reset</button>
    <button type="submit" ng-disabled="searchForm.$invalid">Search</button>
  </div>
</form>

Et JS:

$scope.resetCount = 0;
$scope.obj = {};
$scope.reset = function(form_) {
  $scope.resetCount++;
  $scope.obj = {};
  form_.$setPristine();
  form_.$setUntouched();
  console.log($scope.resetCount);
};

$scope.search = function() {
  alert('Searching');
};

Exemple en direct sur jsfiddle .

Notez la directive ng-model-options="{allowinvalid: true}". Utilisez-le nécessairement ou jusqu'à ce que le champ de saisie ne soit plus valide, la valeur model ne soit pas enregistrée. Par conséquent, la réinitialisation ne fonctionnera pas.

P.S. Mettre la valeur (areaCode, phoneNumber) sur l'objet simplifie la purification.

2
Stepan Kasyanenko

J'ai eu le même problème et j'ai essayé de faire la solution battmanz (réponse acceptée).

Je suis à peu près sûr que sa réponse est très bonne, mais cela n'a pas fonctionné pour moi.

J'utilise ng-model pour lier des données et une bibliothèque de matériaux angulaire pour les entrées et les directives ng-message pour le message d'erreur , donc ce que je dirai ne sera utile que pour les personnes utilisant la même configuration.

J'ai beaucoup regardé l'objet formController en javascript, en fait il y a beaucoup de fonction angulaire $ comme l'a noté battmanz, et il y a aussi vos noms de champs, qui sont des objets avec certaines fonctions dans ses champs.

Alors, qu'est-ce que vous effacez votre formulaire?

Habituellement, je vois un formulaire sous forme d'objet JSON et tous les champs sont liés à une clé de cet objet Json.

//lets call here this json vm.form
vm.form = {};

//you should have something as ng-model = "vm.form.name" in your view

Donc, au début, pour effacer le formulaire, je viens de rappeler le formulaire de soumission:

vm.form = {};

Et comme expliqué dans cette question, ng-messages ne disparaîtra pas avec ça, c'est vraiment dommage.

Quand j'ai utilisé la solution battmanz telle qu'il l'a écrite, les messages n'apparaissaient plus, mais les champs n'étaient plus vides après la soumission, même si j'écrivais

vm.form = {};

Et j’ai découvert que c’était normal, parce que sa solution supprime la liaison du modèle du formulaire, car tous les champs sont définis comme indéfinis . Le texte était donc toujours dans la vue, car il n’y avait plus de liaison. et il a décidé de rester dans le code HTML.

Alors qu'est-ce que j'ai fait?

En fait, je viens de vider le champ (en fixant la liaison sur {}) et d’utiliser

form.$setPristine();
form.$setUntouched();

En fait, cela semble logique, puisque la liaison est toujours là, les valeurs du formulaire sont maintenant vides et la directive angular ng-messages ne se déclenche que si le formulaire n'est pas intact, alors je pense que c'est normal après tout.

Le code final (très simple) est le suivant: 

function reset(form) {
        form.$setPristine();
        form.$setUntouched();
};

Un gros problème que j'ai rencontré avec ça:  

Seulement une fois , le rappel semble s'être foutu quelque part et les champs n'étaient pas vides (c'était comme si je n'avais pas cliqué sur le bouton d'envoi).

Lorsque j'ai de nouveau cliqué, la date d'envoi était vide. Cela est d'autant plus étrange que mon bouton d'envoi est censé être désactivé lorsqu'un champ obligatoire n'est pas rempli avec le bon modèle et que vide n'est certainement pas un bon.

Je ne sais pas si ma façon de faire est la meilleure ou même la bonne, si vous avez des critiques/suggestions, ou des problèmes à propos du problème que j'ai rencontré, merci de me le faire savoir, j'aime toujours progresser dans angularJS.

J'espère que cela aidera quelqu'un et désolé pour le mauvais anglais.

1
Alburkerk

suite à la réponse de @battmanz, mais sans utiliser la syntaxe ES6 pour prendre en charge les navigateurs plus anciens. 

 $scope.resetForm = function (form) {

            try {
                var controlNames = Object.keys(form).filter(function (key) { return key.indexOf('$') !== 0 });

                console.log(controlNames);
                for (var x = 0; x < controlNames.length; x++) {
                    form[controlNames[x]].$setViewValue(undefined);
                }

                form.$setPristine();
                form.$setUntouched();
            } catch (e) {                
                console.log('Error in Reset');
                console.log(e);
            }

        };
1
Abdul Rehman Sayed

Vous pouvez passer votre objet loginForm dans la fonction ng-click="userCtrl.login(loginForm) Et dans l'appel de fonction 

this.login = function (loginForm){
  loginForm.$setPristine();
  loginForm.$setUntouched();
}
0
code4j

Donc, aucune des réponses ne fonctionnait complètement pour moi. Esp, effacement de la valeur de vue, j'ai donc combiné toutes les réponses pour effacer la valeur de vue, effacer les erreurs et effacer la sélection avec une requête j (à condition que les champs soient saisis et portent le même nom que le nom du modèle) 

 var modelNames = Object.keys($scope.form).filter(key => key.indexOf('$') !== 0);
                    modelNames.forEach(function(name){
                        var model = $scope.form[name];
                        model.$setViewValue(undefined);
                        jq('input[name='+name+']').val('');
                        angular.forEach(model.$error, function(value, name) {
                          // reset validity
                          model.$setValidity(name, null);
                        });
                    });
                    $scope.form.$setPristine();
                    $scope.form.$setUntouched();
0
Diwas Subedi