Typy angular formulárov: Ktorý sa hodí kam?

Formuláre sú základom a zároveň najzraniteľnejšou časťou webu. V sérii blogov o bezpečnosti sme neraz písali, ako a prečo treba všetky vstupy ošetriť, a že ide o najčastejší spôsob, akým sa záškodníci nabúrajú do webu. Pri výbere toho, ako formulár nakódime, preto treba byť opatrní. Našťastie máme, napríklad pri Angulari, hneď niekoľko možností.

Templatové formuláre

Ide o najjednoduchší typ zadefinovaný v šablóne, v ktorom sa na viazanie dát do formulára používa ngModel. Kód takéhoto formulára s poliami meno, priezvisko, telefónne číslo a email vyzerá asi takto:

<form (ngSubmit)="onSubmit()" #myForm="ngForm">
  <div>
    <label for="name">Meno:</label>
    <input type="text" id="name" name="name" ngModel required>
  </div>
  <div>
    <label for="surname">Priezvisko:</label>
    <input type="text" id="surname" name="surname" ngModel required>
  </div>
  <div>
    <label for="phone">Telefónne číslo:</label>
    <input type="tel" id="phone" name="phone" ngModel required>
  </div>
  <div>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" ngModel required>
  </div>
  <button type="submit" [disabled]="!myForm.form.valid">Odoslať</button>
</form>

Na prvý pohľad vyzerá všetko v poriadku. Druhý a tretí pohľad však môžu odhaliť, že formulár nekontroluje formát vstupu, napríklad či sú v telefónnom čísle naozaj čísla a či má email zavináč. Bez problémov sa tak odošle s akoukoľvek chybou. Tieto formuláre sú teda vhodné len na naozaj maličké projekty osobného charakteru a do sveta by som ich nepúšťal. 

Ak ich predsa len použiť chcete, odporúčam preddefinované (https://angular.io/api/forms/Validators) alebo aj custom (https://angular.io/guide/form-validation) validátory, ktoré bezpečnosť formuláre ošetria. Programátorom sú dostupné vo forme templatov a dajú sa nasadiť na typové aj reaktívne formuláre. 

Reaktívne formuláre

Pri tomto type sa na viazanie dát do formulára používajú FormControl a FormGroup. Výsledok vyzerá približne takto:

HTML kód:

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div>
    <label for="name">Meno:</label>
    <input type="text" id="name" formControlName="name">
  </div>
  <div>
    <label for="surname">Priezvisko:</label>
    <input type="text" id="surname" formControlName="surname">
  </div>
  <div>
    <label for="phone">Telefónne číslo:</label>
    <input type="tel" id="phone" formControlName="phone">
  </div>
  <div>
    <label for="email">Email:</label>
    <input type="email" id="email" formControlName="email">
  </div>
  <button type="submit" [disabled]="!myForm.valid">Odoslať</button>
</form>

Komponentný kód:

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

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

export class FormComponent implements OnInit {
  myForm: FormGroup;
  constructor(private formBuilder: FormBuilder) { }

  ngOnInit(): void {
    this.myForm = this.formBuilder.group({
      name: ['', Validators.required],
      surname: ['', Validators.required],
      phone: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]]
    });
  }
  onSubmit() {
    console.log(this.myForm.value);
  }
}

Tu sú do kódu zapracované aj nejaké tie validátory, a teda používateľ nemôže poslať úplne hocičo. Pri veľkých projektoch je minimálne takáto základná typová kontrola takmer nutnosťou. Vďaka nej programátori v každom momente vedia, aký typ dát im cez pole vo formulári do systému dorazí. 

Rôznymi mechanikami by sa dalo dosiahnuť vytiahnutie typov dát aj z bežného templatového formulára. Ale načo robiť prácu navyše, keď môžeme rovno pri návrhu siahnuť po reaktívnych formulároch, a urobiť to poriadne aj efektívnejšie? :)

Trošku ten kód vyšperkovať a bol by súci medzi ľudí. V tomto bode by sme mohli aj skončiť, ale Angular nám od verzie 14 pripravil jednu zaujímavú novinku, kvôli ktorej sa oplatí pokračovať.

Reaktívne typové formuláre

Dosiaľ bol pri extrahovaní obsahu z formulára priradený obsahu typ any. To však programátorovi nepovie, či bude novým obsahom reťazec písmen string, boolean (áno/nie) alebo číslo. Ak by chcel na formulár napríklad napájať ďalšiu logiku alebo s údajmi ďalej pracovať, tieto informácie mu rozhodne chýbajú.

Vďaka reaktívnym typovým formulárom je však možné ich definovať. A navyše, aj samotné IDE, v ktorom programátor kódi, vývojára hneď varuje pri neoprávnenom priradení.

Komplementárny kód k HTML z predošlého reaktívneho formulára teda vyzerá asi takto:

export class FormComponent implements OnInit {
    myForm: FormGroup<{
        name: FormControl<string>;
        surname: FormControl<string>;
        phone: FormControl<string>;
        email: FormControl<string>;
    }>;

    constructor(private formBuilder: FormBuilder) {}

    ngOnInit(): void {
        this.myForm = this.formBuilder.group({
            name: ['', Validators.required],
            surname: ['', Validators.required],
            phone: ['', Validators.required],
            email: ['', [Validators.required, Validators.email]],
        });
    }

    onSubmit() {
        console.log(this.myForm.value);
    }
}

Prípadne môžeme využiť aj dedenie, pri ktorom návratová hodnota pre `.group` nastaví aj typ pre `myForm`, a trošku to vylepšiť:

export class FormComponent {

    myForm = this.formBuilder.group({
        name: ['', Validators.required],
        surname: ['', Validators.required],
        phone: ['', Validators.required],
        email: ['', [Validators.required, Validators.email]],
    });

    constructor(private formBuilder: FormBuilder) {}

    onSubmit() {
        console.log(this.myForm.value);
    }
}

Ako som spomínal, IDE vykonáva v takomto prípade nad kódom aj kontrolu, z ktorej sa môžu vrátiť užitočné chyby ako: 

Alebo, v prípade nižšie, sa string snaží vložiť do surnameCount, ktoré je však number, pretože programátor ho chcel využiť na informáciu o dĺžke priezviska.

Chybu opraví “length”, čo je už správny typ pre túto hodnotu.

Reaktívne typové formuláre sú užitočné najmä pre programátora, pretože vďaka nim môže pracovať striktne s typmi, ktoré vyžaduje. Nemôže sa teda stať, že do aplikácie vstúpia náhodné nedefinované hodnoty a projekt sa dostane do nepredvídateľného, až nebezpečného stavu. Nasadenie typov vo formulároch dokáže odhaliť obrovské chyby, keďže väčšina webov na sieti používa práve formuláre. 

Zároveň sa programátorom ľahšie kódi. Typové kontroly im nedovolia, aby niekde, kde má ísť number, hodili string, a v neskoršom štádiu spôsobili väčší bug. Už samotný transpiler a IDE ich bude varovať: „Holá, tadeto neprejdeš a nespustíš webku, lebo tam máš nesprávny formát alebo robíš nepovolenú operáciu!”. 

Vďaka reaktívnym typovým formulárom teda môžeme kódiť opäť “čistejšie” a bezpečnejšie. Ďakujeme, Angular 14+!

Máš toto všetko v malíčku? Potom sa pridaj do nášho tímu!