171 votes

Comment valider des entrées créées dynamiquement à l'aide de ng-repeat, ng-show (angular)

J'ai un tableau qui est créé en utilisant ng-repeat. Je veux ajouter la validation à chaque élément du tableau. Le problème est que chaque cellule de saisie porte le même nom que la cellule située au-dessus et au-dessous d'elle. J'ai essayé d'utiliser la fonction {{$index}} pour nommer les entrées, mais bien que les littéraux de chaîne en HTML semblent corrects, cela fonctionne maintenant.

Voici mon code à ce jour :

<tr ng-repeat="r in model.BSM ">
   <td>
      <input ng-model="r.QTY" class="span1" name="QTY{{$index}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/>
      <span class="alert-error" ng-show="form.QTY{{$index}}.$error.pattern"><strong>Requires a number.</strong></span>
      <span class="alert-error" ng-show="form.QTY{{$index}}.$error.required"><strong>*Required</strong></span>
   </td>
</tr>

J'ai essayé de retirer le {{}} à partir de l'index, mais cela ne fonctionne pas non plus. Pour l'instant, la propriété de validation de l'entrée fonctionne correctement, mais le message d'erreur ne s'affiche pas.

Quelqu'un a-t-il des suggestions ?

Edit : En plus des excellentes réponses ci-dessous, voici un article de blog qui traite de cette question de manière plus détaillée : http://www.thebhwgroup.com/blog/2014/08/angularjs-html-form-design-part-2/

5 votes

Pour ceux qui lisent ceci en 2015... la réponse la plus votée n'est plus la bonne. Regardez plus bas. :)

0 votes

Ce site semble être la réponse "pour 2015" dont parle @WillStrohl.

0 votes

Quelle est l'étiquette appropriée pour les SO ici ? Devrais-je laisser la réponse acceptée puisqu'elle était correcte à l'époque ou accepter la réponse correcte d'aujourd'hui ? Je veux juste que ce fil apparemment populaire soit utile aux nouveaux visiteurs.

1voto

tomgreen98 Points 1

En examinant ces solutions, celle fournie par Al Johri ci-dessus est la plus proche de mes besoins, mais sa directive était un peu moins programmable que je le souhaitais. Voici ma version de ses solutions :

angular.module("app", [])
    .directive("dynamicFormName", function() {
        return {
            restrict: "A",
            priority: 0,
            require: ["form"],
            compile: function() {
                return {
                    pre: function preLink(scope, iElement, iAttrs, ctrls) {
                        var name = "field" + scope.$index;

                        if (iAttrs.dnfnNameExpression) {
                            name = scope.$eval(iAttrs.dnfnNameExpression);
                        }

                        var parentForm = iElement.parent().controller("form");
                        if (parentForm) {
                            var formCtrl = ctrls[0];
                            delete parentForm[formCtrl.$name];
                            formCtrl.$name = name;
                            parentForm[formCtrl.$name] = formCtrl;
                        }
                    }
                 }
            }
        };
   });

Cette solution vous permet de passer une expression de générateur de nom à la directive et d'éviter le verrouillage de la substitution de motif qu'il utilisait.

J'ai également eu des difficultés au départ avec cette solution car elle ne montrait pas d'exemple d'utilisation dans le balisage, voici donc comment je l'ai utilisée.

<form name="theForm">
    <div ng-repeat="field in fields">
        <input type="number" ng-form name="theInput{{field.id}}" ng-model="field.value" dynamic-form-name dnfn-name-expression="'theInput' + field.id">        
    </div>
</form>

J'ai un exemple fonctionnel plus complet sur github .

0voto

SoEzPz Points 71

C'est possible et voici comment je fais la même chose avec un tableau d'entrées.

envelopper le tableau dans un formulaire comme suit

Alors, utilisez simplement ceci

J'ai un formulaire avec des directives multi-encastrées qui contiennent toutes des entrées, des sélections, etc... Ces éléments sont tous enfermés dans des ng-repeats, et des valeurs de chaîne dynamiques.

Voici comment utiliser la directive :

<form name="myFormName">
  <nested directives of many levels>
    <your table here>
    <perhaps a td here>
    ex: <input ng-repeat=(index, variable) in variables" type="text"
               my-name="{{ variable.name + '/' + 'myFormName' }}"
               ng-model="variable.name" required />
    ex: <select ng-model="variable.name" ng-options="label in label in {{ variable.options }}"
                my-name="{{ variable.name + index + '/' + 'myFormName' }}"
        </select>
</form>

Note : vous pouvez ajouter un index à la concaténation de la chaîne si vous avez besoin de sérialiser peut-être un tableau d'entrées ; ce que j'ai fait.

app.directive('myName', function(){

  var myNameError = "myName directive error: "

  return {
    restrict:'A', // Declares an Attributes Directive.
    require: 'ngModel', // ngModelController.

    link: function( scope, elem, attrs, ngModel ){
      if( !ngModel ){ return } // no ngModel exists for this element

      // check myName input for proper formatting ex. something/something
      checkInputFormat(attrs);

      var inputName = attrs.myName.match('^\\w+').pop(); // match upto '/'
      assignInputNameToInputModel(inputName, ngModel);

      var formName = attrs.myName.match('\\w+$').pop(); // match after '/'
      findForm(formName, ngModel, scope);
    } // end link
  } // end return

  function checkInputFormat(attrs){
    if( !/\w\/\w/.test(attrs.rsName )){
      throw myNameError + "Formatting should be \"inputName/formName\" but is " + attrs.rsName
    }
  }

  function assignInputNameToInputModel(inputName, ngModel){
    ngModel.$name = inputName
  }

  function addInputNameToForm(formName, ngModel, scope){
    scope[formName][ngModel.$name] = ngModel; return
  }

  function findForm(formName, ngModel, scope){
    if( !scope ){ // ran out of scope before finding scope[formName]
      throw myNameError + "<Form> element named " + formName + " could not be found."
    }

    if( formName in scope){ // found scope[formName]
      addInputNameToForm(formName, ngModel, scope)
      return
    }
    findForm(formName, ngModel, scope.$parent) // recursively search through $parent scopes
  }
});

Cela devrait permettre de gérer de nombreuses situations où vous ne savez pas où se trouvera le formulaire. Ou peut-être avez-vous des formulaires imbriqués, mais pour une raison quelconque, vous voulez attacher ce nom d'entrée à deux formulaires supérieurs ? Il suffit de passer le nom du formulaire auquel vous voulez attacher le nom de l'entrée.

Ce que je voulais, c'était un moyen d'attribuer des valeurs dynamiques à des entrées que je ne connaîtrai jamais, puis d'appeler simplement $scope.myFormName.$valid.

Vous pouvez ajouter tout ce que vous souhaitez : plus de tables, plus d'entrées de formulaire, des formulaires imbriqués, tout ce que vous voulez. Passez simplement le nom du formulaire sur lequel vous voulez valider les entrées. Puis, lors de l'envoi du formulaire, demandez si la valeur $scope.yourFormName.$valid

0voto

Andrew Clavin Points 467

Cela permettra au nom dans le ng-repeat d'apparaître séparément dans la validation du formulaire.

<td>
    <input ng-model="r.QTY" class="span1" name="{{'QTY' + $index}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/>
</td>

Mais j'ai eu du mal à le faire apparaître dans son message de validation et j'ai dû utiliser un ng-init pour qu'il résolve une variable comme clé de l'objet.

<td>
    <input ng-model="r.QTY" class="span1" ng-init="name = 'QTY' + $index" name="{{name}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/>
    <span class="alert-error" ng-show="form[name].$error.pattern"><strong>Requires a number.</strong></span>
    <span class="alert-error" ng-show="form[name].$error.required"><strong>*Required</strong></span>

0voto

Bartho Bernsmann Points 1139

Mes exigences étaient un peu différentes de celles demandées dans la question originale, mais j'espère pouvoir aider quelqu'un qui rencontre le même problème que moi.

Je devais définir si un champ était obligatoire ou non en fonction d'une variable d'étendue Donc, je devais essentiellement définir ng-required="myScopeVariable" (qui est une variable booléenne).

<div class="align-left" ng-repeat="schema in schemas">
    <input type="text" ng-required="schema.Required" />
</div>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X