47 votes

Formulaires réactifs Angular avec des Tableaux de formulaires imbriqués

Je suis nouveau dans Angular 2 et j'ai décidé que la meilleure façon d'apprendre serait de parcourir les guides officiels d'Angular.

J'ai parcouru le Guide des Formulaires Réactifs https://angular.io/guide/reactive-forms

lien démo : https://stackblitz.com/angular/jammvmbrpxle

Alors que le contenu était globalement assez bon, je suis bloqué sur comment je pourrais implémenter un formulaire plus complexe. Dans l'exemple donné, chaque Héros a la possibilité d'avoir de nombreuses adresses. Une adresse elle-même est un objet plat.

Et si les Adresses avaient des informations supplémentaires telles que la couleur et le type de pièces situées à l'adresse.

export class Address {
    street = '';
    city   = '';
    state  = '';
    zip    = '';
    rooms = Room[];
}

export class Room {
     type = '';
}

de sorte que le modèle de formulaire ressemblerait à ceci...

createForm() {
this.heroForm = this.fb.group({
  name: '',
  secretLairs: this.fb.array([
      this.fb.group({
          street: '',
          city: '',
          state: '',
          zip: '',
          rooms: this.fb.array([
              this.fb.group({
                 type: ''
          })]),
      })]),
  power: '',
  sidekick: ''
});

}

EDIT - Code finalisé qui fonctionne avec ngOnChanges

hero-detail.component.ts

createForm() {
    this.heroForm = this.fb.group({
      name: '',
      secretLairs: this.fb.array([
        this.fb.group({
          street: '',
          city: '',
          state: '',
          zip: '',
          rooms: this.fb.array([
            this.fb.group({
              type: ''
            })
          ])
        })
      ]),
      power: '',
      sidekick: ''
    });
  }

  ngOnChanges() {
    this.heroForm.reset({
      name: this.hero.name,
    });
    this.setAddresses(this.hero.addresses);
  }

  setAddresses(addresses: Address[]) {
    let control = this.fb.array([]);
    addresses.forEach(x => {
      control.push(this.fb.group({
        street: x.street,
        city: x.city,
        state: x.state,
        zip: x.zip,
        rooms: this.setRooms(x) }))
    })
    this.heroForm.setControl('secretLairs', control);
  }

  setRooms(x) {
    let arr = new FormArray([])
    x.rooms.forEach(y => {
      arr.push(this.fb.group({ 
        type: y.type 
      }))
    })
    return arr;
  }

hero-detail.component.html (la partie du tableau de formulaires imbriqués)

    Adresse #{{i + 1}}

        Rue :

        Ville :

        État :

            {{state}}

        Code Postal :

          Pièce #{{j + 1}}

            Type :

  Ajouter une cachette secrète

96voto

AJT_82 Points 30605

MODIFICATION : 2021 Comme la vérification des types est devenue plus stricte (bien !) nous devons apporter quelques modifications. Il n'est pas possible de taper le formarray imbriqué à l'aide d'un getter. Vous pouvez utiliser une fonction à la place, mais je n'aime pas cette idée, car elle est appelée à chaque détection de changement. En revanche, je contourne la vérification des types en utilisant ['controls'] à la place. Si vous voulez un typage plus fort pour le tableau imbriqué (projets), utilisez une fonction, mais rappelez-vous qu'elle est appelée à chaque détection de changement... Voici le code mis à jour :

Il n'est pas très différent d'avoir un formarray imbriqué. Fondamentalement, vous dupliquez simplement le code que vous avez... avec un tableau imbriqué :) Voici un exemple :

myForm: FormGroup;

constructor(private fb: FormBuilder) {
  this.myForm = this.fb.group({
    // vous pouvez également initialiser un formgroup à l'intérieur si vous le souhaitez
    companies: this.fb.array([])
  })
}

// getter pour un accès plus facile
get companiesFormArr(): FormArray {
  return this.myForm.get('companies') as FormArray;
}

addNewCompany() {
  this.companiesFormArr.push(
    this.fb.group({
      company: [''],
      projects: this.fb.array([])
    })
  );
}

deleteCompany(index: number) {
  this.companiesFormArr.removeAt(index);
}

C'est donc l'ajout et la suppression du form array le plus externe, donc ajouter et supprimer des formgroups du form array imbriqué revient simplement à dupliquer le code. Dans le template, nous passons le formgroup actuel auquel vous voulez ajouter un tableau (dans ce cas) un nouveau projet/supprimer un projet.

addNewProject(control) {
  control.push(
    this.fb.group({
      projectName: ['']
  }))
}

deleteProject(control, index) {
  control.removeAt(index)
}

Et de la même manière dans le template, vous itérez votre formarray externe, puis à l'intérieur, vous itérez votre form array interne :

DEMO

MODIFICATION :

Pour définir les valeurs de votre formulaire une fois que vous avez des données, vous pouvez appeler les méthodes suivantes qui vont itérer vos données et définir les valeurs de votre formulaire. Dans ce cas, data ressemble à ceci :

data = {
  companies: [
    {
      company: "entreprise exemple",
      projects: [
        {
          projectName: "projet exemple",
        }
      ]
    }
  ]
}

Nous appelons setCompanies pour définir les valeurs de notre formulaire :

setCompanies() {
  this.data.companies.forEach(x => {
    this.companiesFormArr.push(this.fb.group({ 
      company: x.company, 
      projects: this.setProjects(x) }))
  })
}

setProjects(x) {
  let arr = new FormArray([])
  x.projects.forEach(y => {
    arr.push(this.fb.group({ 
      projectName: y.projectName 
    }))
  })
  return arr;
}

4 votes

Réponse géniale comme d'habitude

0 votes

Merci Alex, cela ne résout pas complètement mon problème cependant. J'ai déterminé quelle était la cause du problème, mais pas comment le résoudre... En supposant que le formulaire devrait être pré-rempli avec des données... Je ne suis pas vraiment sûr comment configurer correctement le contrôle du formulaire.

0 votes

@yurzui Merci, vous êtes vraiment gentil Monsieur, et la même chose pour vous! ;)

-2voto

Eyayu Tefera Points 11

Voici ce que j'ai fait J'ai utilisé angularflexlayout et angular material, vous pouvez utiliser n'importe quelle bibliothèque, je voulais juste vous montrer la fonctionnalité

        Nom

            Téléphone

        ajouter

            Email

        ajouter

      Annuler

        Soumettre

puis contrôleur angulaire

  editForm: FormGroup;
  phones: FormArray;
  emails: FormArray;

  createForm() {
    this.editForm = this.fb.group({
      name: [''],
        phones: this.fb.array([this.createPhone()]),
      emails: this.fb.array([this.createEmail()]),
     });

  }

  get Phones() {
    return this.editForm.get('phones') as FormArray;
  }

  get Emails() {
    return this.editForm.get('emails') as FormArray;
  }

 createPhone() {
    return this.fb.group(({
      phone: '',
    }));
  }

  createEmail() {
    return this.fb.group(({
      email: ''
    }));
  }

  addPhone(): void {
    this.phones = this.editListingForm.get('phones') as FormArray;
    this.phones.push(this.createPhone());
  }

  addEmail(): void {
    this.emails = this.editListingForm.get('emails') as FormArray;
    this.emails.push(this.createEmail());
  }

-2voto

user12635584 Points 1
                    <tr formArrayName="entries"
                        *ngFor="let field of entriesGroup.get('entries').controls; let ind1 = index;">
                        <td [formGroupName]="ind1">
                            <input type="text" disabled formControlName="date1" name="date1"
                                class="form-control">
                        </td>

                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <input type="text" formControlName="startingLocation" name="startingLocation"
                                    class="form-control">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <input type="text" formControlName="endingLocation" name="endingLocation"
                                    class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <input type="text" formControlName="odometerReadStartingPoint"
                                    name="odometerReadStartingPoint" class="form-control"
                                    [ngModel]="ind1!=0?entriesGroup.get('entries').controls[ind1-1].get('odometerReadingDetails').get('odometerReadEndingPoint').value:field.get('odometerReadingDetails').get('odometerReadStartingPoint').value">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <input type="text" formControlName="odometerReadEndingPoint"
                                    name="odometerReadEndingPoint" class="form-control"
                                    [ngModel]="ind1<entriesGroup.get('entries').controls.length-1?entriesGroup.get('entries').controls[ind1+1].get('odometerReadingDetails').get('odometerReadStartingPoint').value:field.get('odometerReadingDetails').get('odometerReadEndingPoint').value">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <input type="text" disabled formControlName="odometerReadForOfficial"
                                    name="odometerReadForOfficial" class="form-control"
                                    [ngModel]="field.get('odometerReadingDetails').get('totalKilometersCovered').value-field.get('odometerReadingDetails').get('odometerReadForPersonal').value">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <input type="text" formControlName="odometerReadForPersonal"
                                    name="odometerReadForPersonal" class="form-control">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <input type="text" disabled formControlName="totalKilometersCovered"
                                    name="totalKilometersCovered" class="form-control"
                                    [ngModel]="field.get('odometerReadingDetails').get('odometerReadEndingPoint').value!=0?field.get('odometerReadingDetails').get('odometerReadEndingPoint').value-field.get('odometerReadingDetails').get('odometerReadStartingPoint').value:0">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="hideHeaders">
                            <div formGroupName="odometerReadingDetails">
                                <textarea rows="2" cols="20" type="text" formControlName="particularTravel"
                                    name="particularTravel" class="form-control">

                         </textarea>
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="fuelHide">
                            <div formGroupName="fuelDetails">
                                <input type="text" formControlName="fuelFilled" name="fuelFilled"
                                    class="form-control">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="fuelHide">
                            <div formGroupName="fuelDetails">
                                <input type="text" formControlName="costPerLiter" name="costPerLiter"
                                    class="form-control">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="fuelHide">
                            <div formGroupName="fuelDetails">
                                <input type="text" disabled formControlName="costOfTheFuel" name="costOfTheFuel"
                                    class="form-control"
                                    [ngModel]="field.get('fuelDetails').get('costPerLiter').value * field.get('fuelDetails').get('fuelFilled').value">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="fuelHide">
                            <div formGroupName="fuelDetails">
                                <input type="text" disabled formControlName="fuelConsumption"
                                    name="fuelConsumption" class="form-control"
                                    [ngModel]="field.get('fuelDetails').get('costOfTheFuel').value!=0?(ind1!=0?(entriesGroup.get('entries').controls[ind1-1].get('fuelDetails').get('fuelConsumption').value + field.get('fuelDetails').get('costOfTheFuel').value):field.get('fuelDetails').get('costOfTheFuel').value):0">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="NoNeed">
                            <div formGroupName="fuelDetails">
                                <input type="text" formControlName="requestId" name="requestId"
                                    class="form-control">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="fuelHide">
                            <div formGroupName="fuelDetails">
                                <input type="text" formControlName="couponCode" name="couponCode"
                                    class="form-control">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="hideDate">
                            <div formGroupName="fuelDetails">
                                <input type="text" formControlName="couponDate" name="couponDate"
                                    placeholder="date1,date2,..." class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="fuelHide">
                            <div formGroupName="fuelDetails">
                                <input type="text" formControlName="couponNumber" name="couponNumber"
                                    placeholder="coupon1,coupon2,..." class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="hideDate">
                            <div formGroupName="fuelDetails">
                                <input type="text" formControlName="couponsAmount" name="couponsAmount"
                                    class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="repaireHide">
                            <div formGroupName="vehicleRepairDetails">
                                <input type="text" formControlName="totalKmsCoveredBeforeRepair"
                                    name="totalKmsCoveredBeforeRepair" class="form-control" readonly
                                    [ngModel]="field.get('odometerReadingDetails').get('totalKilometersCovered').value">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="repaireHide">
                            <div formGroupName="vehicleRepairDetails">
                                <input type="text" formControlName="sparesCost" name="sparesCost"
                                    class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="repaireHide">
                            <div formGroupName="vehicleRepairDetails">
                                <input type="text" formControlName="labourCost" name="labourCost"
                                    class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="repaireHide">
                            <div formGroupName="vehicleRepairDetails">
                                <input type="text" formControlName="vehicleRepaireCost"
                                    name="vehicleRepaireCost" class="form-control"
                                    [ngModel]="field.get('vehicleRepairDetails').get('sparesCost').value*1 + field.get('vehicleRepairDetails').get('labourCost').value*1">
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="repaireHide">
                            <div formGroupName="vehicleRepairDetails">

                                <textarea rows="2" cols="20" type="text" formControlName="particularsOfRepairs"
                                    name="particularsOfRepairs" class="form-control">

                           </textarea>
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="workShop">
                            <div formGroupName="vehicleRepairDetails">
                                <select formControlName="workshopId" class="form-control"
                                    (click)="workshopDetails1($event,ind1)">

                                    <option *ngFor="let i of workshopdata"
                                        [selected]="i.workShopId==field.get('vehicleRepairDetails').get('workshopId').value">
                                        {{i.workShopId}}</option>
                                    <option value="others">OTHERS</option>
                                </select>
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="workShop">
                            <div formGroupName="vehicleRepairDetails">
                                <textarea rows="2" cols="50" type="text" formControlName="workshopAddress"
                                    name="workshopAddress" class="form-control">
           </textarea>
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="workShop">
                            <div formGroupName="vehicleRepairDetails">
                                <input type="text" formControlName="workshopContactNumber"
                                    name="workshopContactNumber" class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="workShop">
                            <div formGroupName="vehicleRepairDetails">

                                <textarea rows="2" cols="50" type="text"
                                    formControlName="workshopAccountDetails" name="workshopAccountDetails"
                                    class="form-control">
           </textarea>
                            </div>
                        </td>
                        <td [formGroupName]="ind1" *ngIf="repaireHide">
                            <div formGroupName="vehicleRepairDetails">
                                <input type="text" formControlName="requestId" name="requestId"
                                    class="form-control">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="driverHide">
                            <div formGroupName="driverChargesDetails">
                                <select formControlName="categoryType" class="form-control"
                                    (click)="onCatType1($event,ind1)">
                                    <option>Choose</option>
                                    <option *ngFor="let i of driverChargesLimit"
                                        [selected]="i.categoryType==field.get('driverChargesDetails').get('categoryType').value">
                                        {{i.categoryType}}</option>

                                </select>
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="driverHide">
                            <div formGroupName="driverChargesDetails">
                                <input type="text" formControlName="amount" name="amount" class="form-control"
                                    disabled>
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="driverHide">
                            <div formGroupName="driverChargesDetails">
                                <input type="time" formControlName="startingTime" name="startingTime"
                                    class="form-control" (change)="onStartTime1($event,ind1)">
                            </div>
                        </td>

                        <td [formGroupName]="ind1" *ngIf="driverHide">
                            <div formGroupName="driverChargesDetails">
                                <input type="time" formControlName="endingTime"
                                    (change)="onEndTime1($event,ind1)" name="endingTime" class="form-control">
                            </div>
                        </td>

                    </tr>

                </tbody>
            </div>
        </table>

2 votes

Si cela contient 8 objets de données, il se répète bien. Mais s'il en contient 10 ou plus, il ne se répète pas. Comment résoudre...?

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