Содержание
При помощи форм наше веб-приложение может взаимодействовать с пользователем. Формы широко используются в веб: это и комментарии на страницах, формы для входа и регистрации, размещение заказов и пр.
В Angular используется два подхода для работы с формами:
- Template-driven forms (шаблонный подход). Как следует из названия, ключевую роль тут играет шаблон компонента.
- Reactive forms (реактивные формы) — это новый подход работы с формами в Angular.
Реактивные формы предоставляют чуть больше возможностей. В принципе, большинство того, что можно сделать в реактивных формах, можно сделать и с помощью шаблонного подхода, но при этом реактивный получится проще и быстрее. Также масштабируемость и расширение функционала у реактивных форм лучше и проще.
В данном разделе мы рассмотрим шаблонный подход в теории. В нашем практическом примере мы будем работать с реактивным подходом (в следующем разделе).
Подготовка формы
Для начала нам нужно изменить шаблон нашего файла:
<div class="container">
<div class="row">
<h3 class="col-md-12 mr-3 mt-2">{{ title }}</h3>
<div class="col-md-12">
<table class="table" [class.text-center]="'text-center'">
<thead [ngClass]="headClass()">
<tr>
<th>ID</th>
<th>Название</th>
<th>Цена</th>
<th>Кол-во</th>
</tr>
</thead>
<ng-container *ngFor="let product of products">
<tr *ngIf="product.quantity > 0">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td [ngStyle]="getStyles(product.price)">${{ product.price }}</td>
<td [ngSwitch]="product.quantity">
<span class="badge" [ngClass]="quantityBagde(product.quantity)">{{ product.quantity }}</span><br>
<span *ngSwitchCase="1">Остался последний</span>
<span *ngSwitchCase="2">Осталось всего два</span>
<span *ngSwitchCase="3">Осталось еще три</span>
<span *ngSwitchCase="5">Товара достаточно</span>
<span *ngSwitchDefault>Значение по умолчанию</span>
</td>
</tr>
</ng-container>
</table> <!-- /.table -->
</div>
<div class="col-md-12 mt-2 add-new-item">
<form (ngSubmit)="submitForm(form)" #form="ngForm">
<div class="form-group">
<label for="title">Название</label>
<input type="text" id="title" class="form-control" ngModel name="title">
</div>
<div class="form-group">
<label for="price">Цена</label>
<input type="number" id="price" class="form-control" ngModel name="price">
</div>
<div class="form-group">
<label for="quantity">Количество (шт.)</label>
<input type="number" id="quantity" class="form-control" ngModel name="quantity">
</div>
<button class="btn btn-success" type="submit">Добавить</button>
</form>
</div>
</div> <!-- /row -->
</div>
Основное, что мы тут добавили это форма. Каждому элементу input формы мы добавили директиву ngModel и атрибут name. Также самой форме добавили обработчик submitForm() события ngSubmit. В качестве параметра этому обработчику передается локальная переменная #form, которая содержит экземпляр директивы ngForm.
Если элементу формы не добавить директиву ngModel, то он значение этого поля не будет передано в форму. Немного позже вы увидите, что будет возвращать локальная переменная #form, мы ее выведем в консоль.
Также небольшие изменения есть в файле стилей, чтобы придать «немного красоты» нашей форме:
.add-new-item {
background-color: #f8f8f8;
padding: 20px;
}
И также добавим изменения в основной файл компонента:
import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { DataService } from '../shared/data.service';
...
getStyles(price) {
return {
fontSize: '1.1rem',
'letterSpacing.px': 1,
backgroundColor: price > 300 ? 'green' : 'red'
}
}
submitForm(form: NgForm) {
console.log(form);
}
}
Тут мы импортируем модуль NgForm и описываем метод submitForm(). Пока он просто будет выводить в консоль данные нашей формы.
Следующий обязательный шаг — это подключение модуля FormsModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { AllProductsComponent } from './all-products/all-products.component';
import { DataService } from './shared/data.service'
@NgModule({
declarations: [
AppComponent,
AllProductsComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [DataService],
bootstrap: [AppComponent]
})
export class AppModule { }
Итак, давайте в форму попробуем добавить новый товар и посмотрим, что у нас выводится в консоли:

Как видите, тут много различных свойств. Некоторые из них мы будем использовать для валидации формы. Самое простое, что сейчас можно сделать, это добавить атрибут required какому-либо из полей. Если это поле оставить пустым, то после сабмита свойство status: "INVALID", а также свойство valid: false

Валидация формы
Давайте всем полям нашей формы добавим атрибут required и будем добавлять атрибут disabled кнопке сабмита, если наша форма не валидна. Так как все данные о форме у нас хранятся в локальной переменной #form (а у нас это объект), соответственно все свойства нам доступны через эту переменную. Значит добавим [disabled]="!form.valid" — отрицание ! значит, что атрибут будет добавляться если свойство valid имеет значение false (т.е. не валидна)
...
<form (ngSubmit)="submitForm(form)" #form="ngForm">
<div class="form-group">
<label for="title">Название</label>
<input type="text" id="title" class="form-control" ngModel name="title" required>
</div>
<div class="form-group">
<label for="price">Цена</label>
<input type="number" id="price" class="form-control" ngModel name="price" required>
</div>
<div class="form-group">
<label for="quantity">Количество (шт.)</label>
<input type="number" id="quantity" class="form-control" ngModel name="quantity" required>
</div>
<button class="btn btn-success" type="submit" [disabled]="!form.valid">Добавить</button>
</form>
...
Теперь кнопка Добавить будет не активна до тех пор, пока мы не заполним все поля формы.
Angular присваивает элементам формы различные классы в зависимости от того валидные ли там данные, заполнял ли пользователь эти поля или нет и т.д. К примеру, нашему полю Название Angular добавляет классы ng-untouched ng-pristine ng-invalid — это значит что поле не тронуто и не валидно. После того как мы введем валидные данные, классы изменятся на ng-dirty ng-valid ng-untouched и как только мы убираем фокус с поля ввода классы поменяются на ng-dirty ng-valid ng-touched.

Исходя из этого мы можем добавлять различные классы элементам формы, в зависимости от того успешна валидация или нет. Давайте добавим локальные переменные #title, #price, #quantity и передать им значение ngModel, они будут возвращать объект, похожий на объект формы тот, что мы выводили в консоль.
...
<form (ngSubmit)="submitForm(form)" #form="ngForm">
<div class="form-group">
<label for="title">Название</label>
<input type="text" id="title" class="form-control" ngModel name="title" required #title="ngModel">
<p *ngIf="title.invalid && title.touched" class="small">Поле обязательно для заполнения</p>
</div>
<div class="form-group">
<label for="price">Цена</label>
<input type="number" id="price" class="form-control" ngModel name="price" required #price="ngModel">
<p *ngIf="price.invalid && price.touched" class="small">Поле обязательно для заполнения</p>
</div>
<div class="form-group">
<label for="quantity">Количество (шт.)</label>
<input type="number" id="quantity" class="form-control" ngModel name="quantity" required #quantity="ngModel">
<p *ngIf="quantity.invalid && quantity.touched" class="small">Поле обязательно для заполнения</p>
</div>
<button class="btn btn-success" type="submit" [disabled]="!form.valid">Добавить</button>
</form>
...
Далее добавим под каждым инпутом сообщение в случае если поле не проходит валидацию. Его мы будем выводить если в например price.invalid && price.touched, т.е. поле invalid и touched (отредактировано и фокус с поля убран).
И добавим изменения в файл стилей:
.add-new-item {
background-color: #f8f8f8;
padding: 20px;
}
input.ng-touched.ng-invalid {
border: 1px solid #c00;
}
Результат:

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