Angular: работа с формами

В ангуляре есть 2 подхода к созданию форм:

Template-Driven – все настройки формы (валидаторы, группировка и т.д.) прописываются внутри html-шаблона, а в классе происходит только обработка полученного значения. Для их работы в app.modules.ts подключаются FromsModule.

Reactive – форма создается програмно, т.е. все настройки ее прописываются в классе и потом синхронизируется с DOM, где находится практически “голая форма”. Для их работы в app.modules.ts подключаются ReactiveFormsModule.

Получение данных с формы при отправке (нажатии submit)

Вариант №1 (передача локальной ссылки внутрь функции с шаблона)

Html шаблон:

<form (ngSubmit)="onSubmit(testForm)" #testForm="ngForm">
  <input type="text" ngModel name="username">
  <input type="text" ngModel name="email">
  <button type="submit">Test Submit</button>
</form>

Класс компонента:

onSubmit(data: NgForm) {
    console.log(data);
}

Вариант №2 (применение @ViewChild вместо передачи ссылки через параметр)

Это более предпочтительный метод, хотя возвращает точно тот же объект, а синтаксис выглядит даже сложнее.

<form (ngSubmit)="onSubmit()" #testForm="ngForm">
  <input type="text" ngModel name="username">
  <input type="text" ngModel name="email">
  <button type="submit">Test Submit</button>
</form>

Класс компонента:

export class MoveDetalisComponent implements OnInit {
  @ViewChild('testForm') someNewNameFrom: NgForm | null = null;

  constructor(private testService: TestService) {}

  onSubmit() {
    console.log(this.someNewNameFrom);
  }

....

Валидация формы

Простая валидация с отключением кнопки и присвоением стилей невалидным инпутам

1. Добавляем условия валидации к инпутам, такие как required и email, делаем кнопку неактивной, когда эти условия false, а также выводим span с предупреждением, когда емейл невалидный:

<form (ngSubmit)="onSubmit()" #testForm="ngForm">
  <input type="text" ngModel name="username" required>
  <input type="text" ngModel name="email" email="ng-modal" required span>
<span *ngIf="email.invalid && email.touched">Enter valid email!</div>
  <button [disabled]="!testForm.valid" type="submit">Test Submit</button>
</form>

Условия, которые вешаются на кнопку либо блок, всплывающие при невалидности, можно комбинировать операторами && или ||, как видно в примере с блоком выше.

2. Присваиваем стили к компонентам, которые не прошли валидацю и уже были touched (это можно благодаря тому, что инпутам Агуляр добавляет определенные стили, зависимо от происходящих с ними действиий):

input.ng-invalid.ng-touched {
  border: 3px solid red;
}

Байндинг и значение по умолчанию для инпута

Есть 3 варианта в Ангуляре:

1) Без байндинга – как во всех примерах выше, когда байндинга и значения по умолчанию для инпута нет совсем.

2) Односторонний байндинг, который позволяет задать значение инпуту по умолчанию. По сути это “разовая статика”:

<input type="text" ngModel name="email"[ngModel]="emailDefault">

3) Двусторонний байндинг – добавляем просто круглые скобки внутри [ngModel] и получаем полный контроль над инпутом:

<input type="text" ngModel name="email"[(ngModel)]="emailDefault">

Группировка элементов формы

Внутри формы ее несколько элементов (либо все) для удобства валидации и получения данных можно групировть при помощи директивы ngModelGroup

<form (ngSubmit)="onSubmit()" #testForm="ngForm">
  <div class="form-group" ngModelGroup="userMainData">
    <input type="text" ngModel name="username">
    <input type="text" ngModel name="firstname">
    <input type="text" ngModel name="lastname">
  </div>
  <button [disabled]="!testForm.valid" type="submit">Test Submit</button>
  <div class="form-group" ngModelGroup="userContactData">
    <input type="number" ngModel name="phone">
    <input type="text" ngModel name="email">
  </div>
</form>

Теперь при отправке формы в свойстве value будет храниться объект не со значениями инпутов, как было ранее, а эти значения будут внутри двух других объектов – userContactData и userMainData (названия наших групп).

Валидация группы

Кроме того, в DOMe к нашим группам применяются классы для валидации, как для инпутов, благодаря чему можно валидировать целую группу (если какой-то из инпутов невалидны, то что-то выводить).

В примере ниже для группы добавляем локальную переменную #userMainData="ngModelGroup", и потом выводим блок при ее невалидности аналогично инпутам в примерах выше.

<form (ngSubmit)="onSubmit()" #testForm="ngForm">
  <div class="form-group" ngModelGroup="userMainData" #userMainData="ngModelGroup">
    <input type="text" ngModel name="username" required>
    <input type="text" ngModel name="firstname" required>
    <input type="text" ngModel name="lastname" required>
  </div>
  <div *ngIf="!userMainData.valid && userMainData.touched" class="error">User Data is invalid</div>
  <button [disabled]="!testForm.valid" type="submit">Test Submit</button>
  <div class="form-group" ngModelGroup="userContactData">
    <input type="number" ngModel name="phone">
    <input type="text" ngModel name="email">
  </div>
</form>

Другие типы инпутов

Select – option

Допустим, нам надо вывести выпадающий список с вопросами и textarea для ввода ответа:

<form (ngSubmit)="onSubmit()" #testForm="ngForm">
  <div class="form-group">
    <label for="secret">Secret Questions</label>
    <select id="secret" [ngModel]="defaultQuestion" name="secret">
      <option value="pet">Your first Pet?</option>
      <option value="teacher">Your first teacher?</option>
    </select>
    <textarea name="questionAnswer" class="form-control" [(ngModel)]="answer"></textarea>
  </div>
  <button [disabled]="!testForm.valid" type="submit">Test Submit</button>
</form>

В классе просто задаем 2 свойства – вопрос по умолчанию (должен совпадать из value одной из option) и переменную для байндинга с полем для ответа:

defaultQuestion: string = 'pet';
answer: string = '';

Radio buttons

На самом деле здесь все практически, как для обычного инпута.

Например, мы имеем массив genders в классе компонента:

genders = ['male', 'female', 'others'];

Выведем на его основе в шаблоне radi кнопки внутри формы:

<form (ngSubmit)="onSubmit()" #testForm="ngForm">
  <div class="form-group" ngModelGroup="userMainData" #userMainData="ngModelGroup">
    <input type="text" ngModel name="username" required>
    <input type="text" ngModel name="firstname" required>
    <input type="text" ngModel name="lastname" required>
  </div>
  <div *ngIf="!userMainData.valid && userMainData.touched" class="error">User Data is invalid</div>
  <button [disabled]="!testForm.valid" type="submit">Test Submit</button>

  <div class="radio" *ngFor="let gender of genders">
    <label>
      <input type="radio" name="gender" ngModel [value]="gender">
      {{gender}}
    </label>
  </div>
</form>

Все, теперь при отправке формы получить значение можно в NgForm.value.gender

Автозаполнение формы по клику

Например, есть у нас вот такой шаблон:

<form (ngSubmit)="onSubmit()" #testForm="ngForm">
  <div ngModelGroup="userMainData" #userMainData="ngModelGroup">
    <input type="text" ngModel name="username" required>
    <input type="text" ngModel name="firstname" required>
    <input type="text" ngModel name="lastname" required>
  </div>
  <button [disabled]="!testForm.valid" type="submit">Test Submit</button>
</form>

В него мы при помощи метода patchValue у нашей подключенной в классе формы передаем объект данных, аналогичный получаемому при отправке:

...
export class MoveDetalisComponent implements OnInit {
  @ViewChild('testForm') someNewNameFrom: NgForm | null = null;

  constructor(private testService: TestService) {}

  onSubmit() {
    this.someNewNameFrom?.form.patchValue({
      userMainData: {
        firstname: 'Ivan',
        lastname: 'Ivanov',
        username: 'iviv235',
      },
    });
  }
...

Есть и другой способ, но менее предпочительнй (речь о this.nameForm.setValue, куда таким же образом передается аналогичный объект).

Очистка формы

Для сбрасывания (перезагрузки) формы после отправки и не только используется метод reset(form) у объявленой формы:

@ViewChild('testForm') someNewNameFrom: NgForm | null = null;

this.someNewNameFrom.reset();

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *