Formularze oparte na szablonach

Najprostszym podejściem do tworzenia formularzy w Angular jest użycie typowych struktur wywodzących się z HTML:

<form method="POST" action="/register" id="signup-form">
  <label for="email">Email</label>
  <input type="text" name="email" id="email">

  <label for="password">Password</label>
  <input type="password" name="password" id="password">

  <button type="submit">Sign Up</button>
</form>

Do podstawowej implementacji wystarczy dodać kilka atrybutów i upewnić się, że nasz komponent wie, co zrobić z danymi.

index.html

<signup-form>Loading...</signup-form>

signup-form.component.html

<form #signupForm="ngForm" (ngSubmit)="registerUser(signupForm)">
  <label for="email">Email</label>
  <input type="text" name="email" id="email" ngModel>

  <label for="password">Password</label>
  <input type="password" name="password" id="password" ngModel>

  <button type="submit">Sign Up</button>
</form>

signup-form.component.ts

import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'signup-form',
  templateUrl: 'signup-form.component.html',
})
export class SignupFormComponent {
  registerUser(form: NgForm) {
    console.log(form.value);
    // {email: '...', password: '...'}
    // ...
  }
}

Zagnieżdżanie formularzy

Angular ułatwia dopasowanie zagnieżdżonego drzewa danych do płaskiego formularza.

Załóżmy, że masz punkt końcowy płatności, który wymaga danych, podobny do następującego:

{
  "contact": {
    "firstname": "Bob",
    "lastname": "McKenzie",
    "email": "BobAndDoug@GreatWhiteNorth.com",
    "phone": "555-TAKE-OFF"
  },
  "address": {
    "street": "123 Some St",
    "city": "Toronto",
    "region": "ON",
    "country": "CA",
    "code": "H0H 0H0"
  },
  "paymentCard": {
    "provider": "Credit Lending Company Inc",
    "cardholder": "Doug McKenzie",
    "number": "123 456 789 012",
    "verification": "321",
    "expiry": "2020-02"
  }
}

Podczas gdy formularze są płaskie i jednowymiarowe, dane z których je budujemy nie są. Chcąc uniknąć kolizji nazw, tworzymy długie i niezręczne nazwy.

<form>
  <fieldset>
    <legend>Contact</legend>

    <label for="contact_first-name">First Name</label>
    <input type="text" name="contact_first-name" id="contact_first-name">

    <label for="contact_last-name">Last Name</label>
    <input type="text" name="contact_last-name" id="contact_last-name">

    <label for="contact_email">Email</label>
    <input type="email" name="contact_email" id="contact_email">

    <label for="contact_phone">Phone</label>
    <input type="text" name="contact_phone" id="contact_phone">
  </fieldset>

  <!-- ... -->

</form>

Moduł obsługi formularzy musiałby przekonwertować te dane na formularz oczekiwany przez interfejs API. Na szczęście jest to coś, na co Angular ma rozwiązanie.

ngModelGroup

Podczas budowania formularza sterowanego szablonami w Angular, możemy oprzeć się na dyrektywie ngModelGroup, aby osiągnąć czystszą implementację, podczas gdy Angular wykonuje intensywne przekształcanie pól formularzy w zagnieżdżone dane.

<form #paymentForm="ngForm" (ngSubmit)="purchase(paymentForm)">
  <fieldset ngModelGroup="contact">
    <legend>Contact</legend>

    <label>
      First Name <input type="text" name="firstname" ngModel>
    </label>
    <label>
      Last Name <input type="text" name="lastname" ngModel>
    </label>
    <label>
      Email <input type="email" name="email" ngModel>
    </label>
    <label>
      Phone <input type="text" name="phone" ngModel>
    </label>
  </fieldset>

  <fieldset ngModelGroup="address">
    <!-- ... -->
  </fieldset>

  <fieldset ngModelGroup="paymentCard">
    <!-- ... -->
  </fieldset>
</form>
  • Korzystanie z alternatywnego formatu etykietowania HTML5; Identyfikatory nie mają wpływu na paradygmat ngForm /ngModel
  • ngModelGroup nie musi być używane w <fieldset> - może to równie dobrze być <div> .

Po wypełnieniu tak zdefiniowanego formularza, otrzymamy dane, w strukturze odpowiedniej dla naszego API.

Powiązania modelu i szablonu

Wiązanie jednokierunkowe

Jeśli potrzebujesz formularza z wartościami domyślnymi, możesz zacząć używać składni wiążącej wartość dla ngModel.

app/signup-form.component.html

<form #signupForm="ngForm" (ngSubmit)="register(signupForm)">
  <label for="username">Username</label>
  <input type="text" name="username" id="username" [ngModel]="generatedUser">

  <label for="email">Email</label>
  <input type="email" name="email" id="email" ngModel>

  <button type="submit">Sign Up</button>
</form>

app/signup-form.component.ts

import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
// ...

@Component({
  // ...
})
export class SignupFormComponent {
  generatedUser: string = generateUniqueUserID();

  register(form: NgForm) {
    console.log(form.value);
    // ...
  }
}

Dwukierunkowe wiązanie

Chociaż Angular domyślnie przyjmuje wiązanie jednokierunkowe, wiązanie dwukierunkowe jest nadal dostępne.

Aby mieć dostęp do dwukierunkowego wiązania w formularzach opartych na szablonach, użyj składni:[(ngModel)] = propertyName .

Pamiętaj, aby zadeklarować wszystkie właściwości, które będą potrzebne w komponencie.

<form #signupForm="ngForm" (ngSubmit)="register(signupForm)">
  <label for="username">Username</label>
  <input type="text" name="username" id="username" [(ngModel)]="username">

  <label for="email">Email</label>
  <input type="email" name="email" id="email" [(ngModel)]="email">

  <button type="submit">Sign Up</button>
</form>
import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  // ...
})
export class SignUpFormComponent {
  username: string = generateUniqueUserID();
  email = '';

  register(form: NgForm) {
    console.log(form.value.username);
    console.log(this.username);
    // ...
  }
}

Sprawdzanie poprawności formularzy opartych na szablonach

Sprawdzanie poprawności możemy definiować następująco:

<!-- a required field -->
<input type="text" required>

<!-- an optional field of a specific length -->
<input type="text" pattern=".{3,8}">

<!-- a non-optional field of specific length -->
<input type="text" pattern=".{3,8}" required>

<!-- alphanumeric field of specific length -->
<input type="text" pattern="[A-Za-z0-9]{0,5}">

Zauważ, że atrybut pattern jest mniej wydajną wersją składni RegEx języka JavaScript.

Istnieją inne atrybuty HTML5, które można zastosować do różnych typów danych wejściowych; jednak w większości przypadków działają one jako górne i dolne limity, co zapobiega dodawaniu lub usuwaniu dodatkowych informacji.

<!-- a field which will accept no more than 5 characters -->
<input type="text" maxlength="5">

Podczas pisania formularza opartego na szablonie można użyć jednej lub obu tych metod.