Začíname s Angularom – Dynamické formuláre / vnorené formuláre (Reactive Forms)

Angular Reactive forms / dynamic forms

V tomto článku nadviažem na predchádzajúci článok, ktorý sa venuje reaktívnym formulárom a dynamickému vytváraniu formulárov. Tentokrát som sa pohral s vnorenými formulármi, kde vo vnútri dvoch vnorených formulároch vytváram dynamický formulár.

Pri tvorbe dynamických formulárov je dôležité nezabudnúť, že je potrebné v HTML definovať formGroup, formGroupName a formArrayName. Angular totiž z toho vytvára Path a ak je zle nastavené vnáranie / štruktúra, tak zobrazí v konzole error. Pri poliach je dôležité uvádzať formControlName, inak sa nebudú aktualizovať hodnoty.

Kým pri klasických formulároch sú prvky formulára pomenované cez vlastnosti objektu, pri dynamických formulároch ide o pole a preto názov formulárového prvku je index v poli.

V nasledujúcich riadkoch uvádzam programový kód, kde demonštrujem reaktívny formulár s vnorenými formulármi a dynamicky vytvorenými prvkami formulára. Príklad je vytvorený pre Bootstrap a obsahuje aj základnú validáciu. Na príklade tak uvidíte viacero implementovaných techník, ktoré sa používajú vo formulároch.

V príklade nie je ukázaný spôsob nahratia Bootstrapu a podpory pre reaktívne formuláre. Toto je uvedené v predchádzajúcom článku, respektíve vo videu.

Obsah súboru component.ts

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

@Component({
  selector: 'app-reactive-forms1',
  templateUrl: './reactive-forms1.component.html',
  styleUrls: ['./reactive-forms1.component.css']
})

export class ReactiveForms1Component implements OnInit {

  form: FormGroup = new FormGroup({});
  services: any;

  serviceList: Array<any> = [
    { name: 'ADSL', code: 'ADSL', selected: false },
    { name: 'Cable Broad Band', code: 'CBL', selected: false },
    { name: 'Foxtel TV', code: 'FOXTEL', selected: true },
    { name: 'Home Wireless', code: 'HWL', selected: true },
    { name: '4G Network', code: '4G', selected: false }
  ];

  createServiceList() {
    const arr = this.serviceList.map(service => {
      return this.fb.control(service.selected);
    });
    return { allServices: this.fb.array(arr) };
  }

  getSelectedServices() {
    let result: any = [];
    this.form.value.serviceInfo.servicesList.allServices.map((checked: boolean, index: number) => {
      if (checked) {
        result.push({ [(index + 1) + ""]: this.serviceList[index].name + ", " + this.serviceList[index].code });
      }
    })
    return result;
  }

  constructor(private fb: FormBuilder) {
    this.services = this.createServiceList();
  }

  ngOnInit(): void {

    this.form = this.fb.group({
      name: ['', Validators.required],
      serviceInfo: this.fb.group({
        deliveryDate: ['', Validators.required],
        servicesList: this.fb.group(this.services)
      })
    });
  }

  get allServices(): FormArray {
    let si: any = this.form.get("serviceInfo");
    let sg: any = si.get("servicesList");
    let as: FormArray = <FormArray>sg.get('allServices');
    return as;
  }

  onSubmit() {
    console.log(this.form.value);
    console.log(this.getSelectedServices());
    this.getSelectedServices().forEach((field: any) => {
      console.log(field);
    });
  }
}

Obsah súboru component.html

<div class="container mt-4">
  <h1>Select your services</h1>
  <form [formGroup]="form" (submit)="onSubmit()">
    <div class="form-group">
      <label for="name">Your name:</label>
      <input class="form-control" name="name" [formControlName]="'name'" />
    </div>
    <div class="mt-4" formGroupName="serviceInfo">
      <div class="form-group">
        <label>Delivery Date:</label>
        <input
          class="form-control"
          [formControlName]="'deliveryDate'"
          type="date"
        />
        <div class="mt-4" formGroupName="servicesList">
          <div class="form-group">
            <div formArrayName="allServices">
              <label>Services:</label>
              <div *ngFor="let service of allServices.controls; let i = index">
                <input
                  id="service_{{ i }}"
                  class="form-check-input"
                  [formControlName]="i"
                  [checked]="service.value"
                  type="checkbox"
                  value=""
                />
                <span ngNonBindable>&nbsp;&nbsp;</span>
                <label class="form-check-label" for="service_{{ i }}">
                  {{ serviceList[i].name }}
                </label>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="mt-4">
      <button type="submit" class="btn btn-primary" [disabled]="!form.valid">
        Save
      </button>
    </div>
  </form>
</div>

Záver

Dynamicky vytvorené controls sú typu Array a nie object. Preto ich názov je index namiesto názvu property v objekte. Keďže aktuálnych príkladov na túto problematiku je na internete málo, dúfam, že vám tento článok pomôže.

https://www.mldgroup.com

Vyštudovaný top manažér pôsobiaci najmä ako manažér, marketér, softvérový inžinier, konzultant, bloger, YouTuber a zatiaľ neúspešný hudobník a producent. V rámci praxe pôsobil v rôznych odvetviach na rôznych pozíciách v malých aj veľkých firmách, vrátane spoluprác a partnerstiev s významnými firmami či poradenskými spoločnosťami.

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *