65 votes

Comment ajouter et supprimer dynamiquement des champs de formulaire en Angular 2

J'essaie d'ajouter des champs de saisie de manière dynamique lorsque l'utilisateur clique sur le bouton d'ajout et pour chaque champ de formulaire, il doit y avoir un bouton de suppression, lorsque l'utilisateur clique sur ce bouton, les champs de formulaire doivent être supprimés, je dois réaliser cela en utilisant Angular 2, comme je suis nouveau dans Angular 2, s'il vous plaît aidez-moi à le terminer.

Ce que j'ai essayé

J'ai créé un ensemble de champs (3 boîtes de sélection et 1 boîte de texte), créé un bouton appelé ajouter des champs, mais je l'ai essayé dans angular 1.x, il fonctionne bien, mais dans angular 2, je ne sais pas comment le compléter, c'est lien de mon travail complet

app/app.component.ts
 import {
    Component
  }
from '@angular/core';
  @Component({
    selector: 'my-app',
    template: `
    <h1>{{title}}</h1>
    <div class="container">
    <button class="btn btn-success bt-sm">add</button>
    <form role="form" calss="form-inline">
    <div class="form-group col-xs-3">
    <label>Select State:</label>
    <select class="form-control" [(ngModel)]="rules.State" id="sel1">
            <option>State1</option>
            <option>State2</option>
            <option>State3</option>
            <option>State4</option>
</select>
     </div>
    <div class="form-group col-xs-3">
<label>Rule:</label>
     <input type="text" data-toggle="modal" data-target="#myModal" class="form-                   control">
    </div>
<div class="form-group col-xs-3">
<label>Pass State :</label>
    <select class="form-control" [(ngModel)]="rules.pass">
    <option>State1</option>
    <option>State2</option>
    <option>State3</option>
    <option>State4</option>
</select>
 </div>
 <div class="form-group col-xs-3">
    <label>Fail State:</label>
        <select class="form-control" [(ngModel)]="rules.fail">
        <option>State1</option>
        <option>State2</option>
        <option>State3</option>
     <option>State4</option>
     </select>
         </div>
    </form>
     </div>
 <div class="modal fade" id="myModal" role="dialog">
      <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                     <button type="button" class="close" data-dismiss="modal">&times    </button>
                    <h4 class="modal-title">Rules Configuration</h4>
                </div>
                <div class="modal-body">
                 <p>Rules</p>
                </div>
                 <div class="modal-footer">
                 <button type="button" class="btn btn-default" data-  dismiss="modal">Close</button>
                </div>
             </div>

                </div>
                 </div>
`
    })
    export class AppComponent {
            title = 'Rule Engine Demo';
          rules: Rules = {
                  State: '',
                  pass: '',
                 fail: ''
                };

1 votes

Vous pouvez utiliser ControlGroup pour y parvenir stackoverflow.com/questions/36627573/

0 votes

@A_Singh avez-vous une idée de la raison pour laquelle angular 2 ne charge pas les codes internes script du template html.

0 votes

Vous voulez dire injecter des scripts en utilisant [innerHTML] ne fonctionne pas ? C'est parce qu'Angular ne permet pas d'injecter des scripts de cette façon.

156voto

Ryan Peters Points 1271

C'est avec quelques mois de retard, mais j'ai pensé que je pourrais fournir une solution basée sur les éléments suivants ce tutoriel ici . L'essentiel est qu'il est beaucoup plus facile à gérer lorsque l'on change sa façon d'aborder les formulaires.

D'abord, utilisez ReactiveFormsModule au lieu ou en plus de la méthode normale FormsModule . Avec les formulaires réactifs, vous créez vos formulaires dans vos composants/services, puis vous les insérez dans votre page au lieu que celle-ci génère le formulaire lui-même. C'est un peu plus de code mais c'est beaucoup plus testable, beaucoup plus flexible, et pour autant que je sache, c'est la meilleure façon de créer beaucoup de formulaires non triviaux.

Le résultat final ressemblera un peu à ceci, conceptuellement :

  • Vous avez une base FormGroup avec ce que FormControl instances dont vous avez besoin pour l'ensemble du formulaire. Par exemple, comme dans le tutoriel dont j'ai donné le lien, disons que vous voulez un formulaire où un utilisateur peut saisir son nom une fois, puis un nombre quelconque d'adresses. Toutes les entrées de champ uniques se trouveraient dans ce groupe de formulaires de base.

  • A l'intérieur de ce FormGroup instance, il y aura un ou plusieurs FormArray instances. A FormArray est essentiellement un moyen de regrouper plusieurs contrôles et d'itérer sur eux. Vous pouvez également mettre plusieurs FormGroup dans votre tableau et utilisez-les comme des "mini-formulaires" imbriqués dans votre grand formulaire.

  • En emboîtant plusieurs FormGroup et/ou FormControl au sein d'un groupe dynamique FormArray Avec l'outil de gestion des formulaires, vous pouvez contrôler la validité et gérer le formulaire comme une seule grande pièce réactive composée de plusieurs parties dynamiques. Par exemple, si vous souhaitez vérifier la validité de chaque entrée avant de permettre à l'utilisateur d'envoyer le formulaire, la validité d'un sous-formulaire se répercutera sur le formulaire de niveau supérieur et l'ensemble du formulaire deviendra invalide, ce qui facilite la gestion des entrées dynamiques.

  • En tant que FormArray est essentiellement une enveloppe autour d'une interface de tableau, mais pour les éléments de formulaire, vous pouvez pousser, faire sauter, insérer et retirer des contrôles à tout moment sans recréer le formulaire ou effectuer des interactions complexes.

Au cas où le tutoriel dont je vous ai donné le lien ne fonctionnerait pas, voici quelques exemples de code que vous pouvez mettre en œuvre vous-même (mes exemples utilisent TypeScript) et qui illustrent les idées de base :

Code du composant de base :

import { Component, Input, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'my-form-component',
  templateUrl: './my-form.component.html'
})
export class MyFormComponent implements OnInit {
    @Input() inputArray: ArrayType[];
    myForm: FormGroup;

    constructor(private fb: FormBuilder) {}
    ngOnInit(): void {
        let newForm = this.fb.group({
            appearsOnce: ['InitialValue', [Validators.required, Validators.maxLength(25)]],
            formArray: this.fb.array([])
        });

        const arrayControl = <FormArray>newForm.controls['formArray'];
        this.inputArray.forEach(item => {
            let newGroup = this.fb.group({
                itemPropertyOne: ['InitialValue', [Validators.required]],
                itemPropertyTwo: ['InitialValue', [Validators.minLength(5), Validators.maxLength(20)]]
            });
            arrayControl.push(newGroup);
        });

        this.myForm = newForm;
    }
    addInput(): void {
        const arrayControl = <FormArray>this.myForm.controls['formArray'];
        let newGroup = this.fb.group({

            /* Fill this in identically to the one in ngOnInit */

        });
        arrayControl.push(newGroup);
    }
    delInput(index: number): void {
        const arrayControl = <FormArray>this.myForm.controls['formArray'];
        arrayControl.removeAt(index);
    }
    onSubmit(): void {
        console.log(this.myForm.value);
        // Your form value is outputted as a JavaScript object.
        // Parse it as JSON or take the values necessary to use as you like
    }
}

Code du sous-composant : (un pour chaque nouveau champ de saisie, pour garder les choses propres)

import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
    selector: 'my-form-sub-component',
    templateUrl: './my-form-sub-component.html'
})
export class MyFormSubComponent {
    @Input() myForm: FormGroup; // This component is passed a FormGroup from the base component template
}

Composant de base HTML

<form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate>
    <label>Appears Once:</label>
    <input type="text" formControlName="appearsOnce" />

    <div formArrayName="formArray">
        <div *ngFor="let control of myForm.controls['formArray'].controls; let i = index">
            <button type="button" (click)="delInput(i)">Delete</button>
            <my-form-sub-component [myForm]="myForm.controls.formArray.controls[i]"></my-form-sub-component>
        </div>
    </div>
    <button type="button" (click)="addInput()">Add</button>
    <button type="submit" [disabled]="!myForm.valid">Save</button>
</form>

Sous-composant HTML

<div [formGroup]="form">
    <label>Property One: </label>
    <input type="text" formControlName="propertyOne"/>

    <label >Property Two: </label>
    <input type="number" formControlName="propertyTwo"/>
</div>

Dans le code ci-dessus, j'ai essentiellement un composant qui représente la base du formulaire, puis chaque sous-composant gère son propre FormGroup au sein de la FormArray situé à l'intérieur de la base FormGroup . Le modèle de base transmet le sous-groupe au sous-composant, puis vous pouvez gérer la validation de l'ensemble du formulaire de manière dynamique.

De plus, cela permet de réorganiser facilement les composants en les insérant et en les retirant stratégiquement du formulaire. Cela fonctionne avec (apparemment) n'importe quel nombre d'entrées, car elles n'entrent pas en conflit avec les noms (un gros inconvénient des formulaires basés sur des modèles, pour autant que je sache) et vous conservez une validation pratiquement automatique. Le seul "inconvénient" de cette approche est que, outre l'écriture d'un peu plus de code, vous devez réapprendre le fonctionnement des formulaires. Cependant, cela ouvrira des possibilités pour des formulaires beaucoup plus grands et plus dynamiques au fur et à mesure.

Si vous avez des questions ou si vous voulez signaler des erreurs, n'hésitez pas. J'ai juste tapé le code ci-dessus basé sur quelque chose que j'ai fait moi-même la semaine dernière avec les noms changés et d'autres propriétés diverses laissées de côté, mais cela devrait être simple. La seule différence majeure entre le code ci-dessus et le mien est que j'ai déplacé toute la construction du formulaire vers un service séparé qui est appelé à partir du composant, de sorte que c'est un peu moins désordonné.

1 votes

Comment implémenter le ngModel dans ce formulaire ?

0 votes

@elporfirio Vous n'utilisez pas ngModel avec ces formulaires. ngModel est surtout utilisé lorsqu'il s'agit de liaison de données bidirectionnelle avec un objet JavaScript. Ces formulaires réactifs sont essentiellement leurs propres objets avec divers champs et propriétés utiles tels que la validation. Au lieu de cela, vous obtenez la propriété ".value" du formulaire et c'est l'ensemble de vos résultats. Vous pouvez manipuler cette propriété comme bon vous semble et l'associer à toutes les classes ou interfaces dont vous disposez.

0 votes

Oui, l'approche ci-dessus est très efficace pour moi, mais je ne comprends pas comment appliquer la directive personnalisée à la chose ci-dessus.

0voto

NarendraKhatri Points 11

Ajouter et supprimer un élément d'entrée de texte de manière dynamique tout le monde peut utiliser ce qui fonctionnera. Type de contact Fonds d'équilibre Fonds d'actions Allocation Le % d'allocation est requis ! Retirer Ajouter un contact

userForm: FormGroup;
  public contactList: FormArray;
  // returns all form groups under contacts
  get contactFormGroup() {
    return this.userForm.get('funds') as FormArray;
  }
  ngOnInit() {
    this.submitUser();
  }
  constructor(public fb: FormBuilder,private router: Router,private ngZone: NgZone,private userApi: ApiService) { }
  // contact formgroup
  createContact(): FormGroup {
    return this.fb.group({
      fundName: ['', Validators.compose([Validators.required])], // i.e Email, Phone
      allocation: [null, Validators.compose([Validators.required])]
    });
  }

  // triggered to change validation of value field type
  changedFieldType(index) {
    let validators = null;

    validators = Validators.compose([
      Validators.required,
      Validators.pattern(new RegExp('^\\+[0-9]?()[0-9](\\d[0-9]{9})$')) // pattern for validating international phone number
    ]);

    this.getContactsFormGroup(index).controls['allocation'].setValidators(
      validators
    );

    this.getContactsFormGroup(index).controls['allocation'].updateValueAndValidity();
  }

  // get the formgroup under contacts form array
  getContactsFormGroup(index): FormGroup {
    // this.contactList = this.form.get('contacts') as FormArray;
    const formGroup = this.contactList.controls[index] as FormGroup;
    return formGroup;
  }

  submitUser() {
    this.userForm = this.fb.group({
      first_name: ['', [Validators.required]],
      last_name: [''],
      email: ['', [Validators.required]],
      company_name: ['', [Validators.required]],
      license_start_date: ['', [Validators.required]],
      license_end_date: ['', [Validators.required]],
      gender: ['Male'],
      funds: this.fb.array([this.createContact()])
    })
    this.contactList = this.userForm.get('funds') as FormArray;
  }
  addContact() {
    this.contactList.push(this.createContact());
  }
  removeContact(index) {
    this.contactList.removeAt(index);
  }

2 votes

Je ne sais pas pourquoi vous avez posté deux réponses plutôt qu'une seule réponse complète. Afin de rendre votre réponse aussi utile que possible, pourriez-vous s'il vous plaît vous assurer que vous avez une seule réponse avec une bonne explication sur la façon dont votre réponse ajoute de la valeur par rapport à la réponse donnée acceptée en 2016 ?

0 votes

Veuillez utiliser le bouton "éditer" sous le message pour le mettre à jour. Ne postez pas votre réponse dans deux messages distincts.

-1voto

NarendraKhatri Points 11

C'est le code HTML. Tout le monde peut l'utiliser :

<div class="card-header">Contact Information</div>
          <div class="card-body" formArrayName="funds">
            <div class="row">
              <div class="col-6" *ngFor="let contact of contactFormGroup.controls; let i = index;">
                <div [formGroupName]="i" class="row">
                  <div class="form-group col-6">
                    <label>Type of Contact</label>
                    <select class="form-control" formControlName="fundName" type="text">
                      <option value="01">Balance Fund</option>
                      <option value="02">Equity Fund</option>
                    </select> 
                  </div>
                  <div class="form-group col-12">
                    <label>Allocation</label>
                    <input class="form-control" formControlName="allocation" type="number">
                    <span class="text-danger" *ngIf="getContactsFormGroup(i).controls['allocation'].touched && 
                    getContactsFormGroup(i).controls['allocation'].hasError('required')">
                        Allocation % is required! </span>
                  </div>
                  <div class="form-group col-12 text-right">
                    <button class="btn btn-danger" type="button" (click)="removeContact(i)"> Remove </button>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <button class="btn btn-primary m-1" type="button" (click)="addContact()"> Add Contact </button>

-2voto

AddAccordian(type, data) { console.log(type, données) ;

let form = this.form;

if (!form.controls[type]) {
  let ownerAccordian = new FormArray([]);
  const group = new FormGroup({});
  ownerAccordian.push(
    this.applicationService.createControlWithGroup(data, group)
  );
  form.controls[type] = ownerAccordian;
} else {
  const group = new FormGroup({});
  (<FormArray>form.get(type)).push(
    this.applicationService.createControlWithGroup(data, group)
  );
}
console.log(this.form);

}

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