web-dev-qa-db-fra.com

Passer le formulaire au composant AngularJS pour validation

Je déplace ma base de code héritée vers la nouvelle architecture de composants promue avec AngularJS 1.5. J'ai rencontré un problème lors de cette opération pour les formulaires plus volumineux. Traditionnellement, je joignais la validation du formulaire comme suit:

<form name="myForm">
  <input type="text" name="input1" ng-model="vm.input1" required />
  <div ng-messages="myForm.input1.$error">
    <div ng-message="required">Please fill out this field.</div>
  </div>
  <!-- many more inputs -->
</form>

Lors de la transition vers une architecture de composant, je dois passer explicitement le formulaire au composant:

<form name="vm.myForm">
  <my-awesome-input-component model="vm.input1" form="vm.myForm"><my-awesome-input-component>
  <!-- many more inputs -->
</form>

Je voudrais éviter de polluer le vm avec mon formulaire. Existe-t-il un meilleur moyen de réaliser l'architecture de composant souhaitée pour les formulaires?

19

Mise à jour - changé nom-formulaire en référence-formulaire, car il n'était pas explicite que nous transmettions le formulaire réel référence et pas seulement le nom du formulaire. Cela peut être appelé comme vous le souhaitez, soyez clair sur ce que c'est réellement.

Comme le dit Iain Reid, vous n'avez pas besoin d'utiliser vm pour cela. Vous nommez simplement le formulaire comme vous le souhaitez, puis passez ce nom à votre composant, il ressemblerait à ceci:

<form name="myForm" ng-submit="ctrl.someFunction()" novalidate>
   <my-input form-reference="myForm"></my-input>
   <button type="submit">Some button</button>
</form>

Assurez-vous que vous écrivez "novalidate" dans votre formulaire pour désactiver les validations par défaut du navigateur, si vous souhaitez gérer les validations par vous-même (ce que je pense que vous utilisez en utilisant des messages ng).

Puis à partir de là, sur mon composant j'écrirais quelque chose comme:

angular.module("myApp")
  .component("myInput",{
     templateUrl:'path/to/template.html'
     bindings:{
       formReference:'<',
       myInputModel:'<',
       onUpdate:'&'
     },
     controller: MyInputController
  }

Et puis dans le modèle d'entrée:

<input type="text" name="myInput" ng-model="$ctrl.myInputModel" ng-change="$ctrl.update($ctrl.myInputModel)" required />
<div ng-messages="$ctrl.formReference.myInput.$error">
  <div ng-message="required">Please fill out this field.</div>
</div>

Quelques notes supplémentaires sur les liaisons et comment passer et mettre à jour les modèles:

  • '<': signifie une liaison unidirectionnelle, qui Angular dit d'utiliser à partir de maintenant pour tous les composants. Afin de mettre à jour la valeur et avoir une liaison bidirectionnelle , nous devons inclure une fonction "onUpdate".
  • onUpdate: '&' ce que je dis ici, c'est que je vais passer une fonction pour mettre à jour le modèle (un rappel pour les événements des composants).

Donc, dans le contrôleur d'entrée, j'écrirais quelque chose comme:

function MyInputController(){
    var ctrl = this;
    ctrl.update = function(value){
        ctrl.onUpdate({value: value});
    };
}

Et enfin, quand j'utilise mon composant dans un formulaire:

<form name="myForm" ng-submit="ctrl.someFunction()" novalidate>
   <my-input form-reference="myForm" my-input-model="ctrl.anyModelIWant" on-update="ctrl.updateMyInput(value)"></my-input>
   <button type="submit">Some button</button>
</form>

Et le contrôleur du formulaire aurait une fonction:

...
ctrl.updateMyInput = function(value){
   ctrl.anyModelIWant = value;
}
...

Documents officiels: https://docs.angularjs.org/guide/component

J'espère que tout cela aide quelqu'un là-bas :-)

22
RGonzalez

Voici une autre approche que certains peuvent trouver utile. Utilisez require pour inclure le parent form dans votre $ctrl:

angular.module("myApp")
    .component("myInput",{
        templateUrl:'path/to/template.html'
        bindings:{
            myInputModel:'<',
            onUpdate:'&'
        },
        controller: MyInputController,
        require: {
            form: '^form'
        }
}

Dans le modèle d'entrée:

<input type="text" name="myInput" ng-model="$ctrl.myInputModel" ng-change="$ctrl.update($ctrl.myInputModel)" required />
    <div ng-messages="$ctrl.form.myInput.$error">
    <div ng-message="required">Please fill out this field.</div>
</div>

Pas besoin de passer le formulaire explicitement dans votre composant car form est automatiquement ajouté à votre $ctrl:

<form name="myForm" ng-submit="ctrl.someFunction()" novalidate>
    <my-input my-input-model="ctrl.anyModelIWant" on-update="ctrl.updateMyInput(value)"></my-input>
    <button type="submit">Some button</button>
</form>

Je suppose que techniquement, vous polluez toujours votre VM, mais au moins vous n'avez pas à le transmettre explicitement dans toute votre hiérarchie.

Ajout de texte pour satisfaire aux exigences d'édition de 6 caractères, période modifiée en virgule pour corriger l'exemple. La période est déroutante pour les nouveaux arrivants.

3
John Barton