Содержание
При помощи форм наше веб-приложение может взаимодействовать с пользователем. Формы широко используются в веб: это и комментарии на страницах, формы для входа и регистрации, размещение заказов и пр.
В 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; }
Результат:
Добавить комментарий