Статьи

Быстрое создание простых, но мощных угловых форм

Формы являются неотъемлемой частью многих веб-приложений и являются наиболее распространенным способом ввода и редактирования текстовых данных. Фронтальные JavaScript-фреймворки, такие как Angular , часто имеют свои собственные идиоматические способы создания и проверки форм, с которыми вам нужно справиться, чтобы быть продуктивными.

Angular позволяет упростить эту общую задачу, предоставляя два типа форм, которые вы можете создавать:

  • Шаблонно-управляемые формы — простые формы, которые можно сделать довольно быстро.
  • Реактивные формы — более сложные формы, которые дают вам больший контроль над элементами в форме.

В этой статье мы сделаем простой пример формы для каждого метода, чтобы увидеть, как это делается.

Предпосылки

Читать JavaScript: от новичка до ниндзя, 2-е издание
Веселое, всеобъемлющее и практическое руководство по современному использованию JavaScript

Вам не нужно знать все детали того, как создать приложение Angular, чтобы понять полезность фреймворка, когда дело доходит до форм. Однако, если вы хотите лучше понять Angular, вы можете взглянуть на эту серию статей SitePoint о создании приложения CRUD с Angular .

Требования

Мы будем использовать Bootstrap в этом уроке. Это не является неотъемлемой частью приложения Angular, но оно поможет нам еще больше оптимизировать наши усилия, предоставляя готовые стили.

Вот как вы можете добавить его в свое приложение:

  1. Откройте командную строку и перейдите в папку вашего проекта

  2. Введите npm install bootstrap@next . Это добавит последнюю версию начальной загрузки в проект

  3. Отредактируйте файл .angular-cli.json и добавьте ссылку на CSS-файл Bootstrap

     "apps": [ "styles": [ "../node_modules/bootstrap/dist/css/bootstrap.css" ] ] 

    Мы не будем использовать файл Bootstrap JavaScript в этом приложении.

  4. Как управляемые шаблоном формы, так и реактивные формы требуют FormsModule . Следует добавить в приложение в app.module :

     import { FormsModule } from '@angular/forms'; @NgModule({ imports: [ BrowserModule, FormsModule ] }) 

После этого мы можем приступить к самим формам.

Шаблонно-управляемые формы

Предположим, вы хотите создать простую форму как можно быстрее. Например, вам нужна форма регистрации компании. Как вы можете создать форму?

Первым шагом является создание <form> в вашем представлении.

 <form #companyForm="ngForm"> 

Нам нужно изменить этот тег двумя способами, чтобы отправить форму и использовать информацию из полей ввода в нашем компоненте:

  • Мы объявим переменную шаблона, используя директиву ngForm .
  • Мы ngSubmit событие ngSubmit с методом, который создадим в нашем компоненте.
 <form #companyForm="ngForm" (ngSubmit)="submitCompany(companyForm.form);"> 

Мы создадим метод submitCompany в компоненте чуть позже. Он будет вызван при companyForm.form формы, и мы передадим ей данные из формы через companyForm.form .

Нам также нужна кнопка отправки, независимо от содержания формы. Мы будем использовать несколько классов Bootstrap для стилизации кнопки. Рекомендуется отключить кнопку до того, как будут выполнены все требования проверки данных. Мы можем использовать переменную шаблона, которую мы создали для формы, чтобы добиться этого. Мы companyForm отключенное свойство с действительным свойством объекта companyForm . Таким образом, кнопка будет отключена, если форма недействительна.

 <button class="btn btn-primary" [disabled]="!companyForm.valid">Submit</button> 

Предположим, что наша простая форма будет иметь два поля — поле ввода для названия компании и поле раскрывающегося списка для отрасли компании.

Создание форм ввода

Сначала мы создаем поле ввода для имени:

 <input type="text" class="form-control" name="company-name"> 

Прямо сейчас у нас есть стандартный ввод с атрибутами type, name и class. Что нам нужно сделать, чтобы использовать угловой подход на нашем входе?

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

Сейчас ngModel время упомянуть, что ngModel требует, чтобы поле ввода имело имя, или элемент управления формы должен быть определен как автономный в ngModelOptions . Это не проблема, потому что наша форма уже имеет имя. Angular будет использовать атрибут name, чтобы различать объекты управления.

Кроме того, мы должны указать переменную шаблона для input: #nameField в этом случае. Angular установит nameField в директиву ngModel которая применяется к полю ввода. Мы будем использовать это позже для проверки поля ввода. Эта переменная также позволит нам выполнить действие, основанное на значении поля, пока мы набираем его.

Теперь наш вклад выглядит так:

 <input type="text" class="form-control" name="company-name" ngModel #nameField="ngModel"> 

Это почти то же самое, но с несколькими ключевыми изменениями.

Проверка

Предположим, мы хотим, чтобы поле названия компании было обязательным и имело минимальную длину 3 символа. Это означает, что мы должны добавить required атрибуты и атрибуты minlength к нашему входу:

 <input type="text" class="form-control" name="company-name" ngModel #nameField="ngModel" required minlength="3"> 

Звучит достаточно просто, правда? Нам также нужно будет отобразить сообщение об ошибке, если какое-либо из этих двух требований не будет выполнено. Angular позволяет нам проверить значение ввода и отобразить соответствующее сообщение об ошибке перед отправкой формы.

Мы можем выполнить такую ​​проверку, пока пользователь печатает в форме. Прежде всего, рекомендуется отображать ошибку только после того, как пользователь начал взаимодействовать с формой. Нет смысла отображать сообщение об ошибке сразу после загрузки страницы. Вот почему мы вставим все сообщения об ошибках для этого ввода в следующем div:

 <div *ngIf="nameField.touched && nameField.errors"></div> 

Директива ngIf позволяет нам показывать div, только когда определенное условие истинно. Здесь мы снова будем использовать nameField шаблона nameField потому что она связана с вводом. В нашем случае div будет виден только в том случае, если к входу прикоснулись и возникла проблема с ним. Хорошо, а как же сами сообщения об ошибках?

Мы поместим еще один div внутри вышеупомянутого для каждого сообщения об ошибке, которое мы хотим. Мы создадим новый div для сообщения об ошибке и снова используем переменную шаблона nameField :

 <div class="alert alert-danger" *ngIf="nameField.errors.required"> The company name is required </div> 

Мы используем классы начальной загрузки «alert alert-danger» для стилизации текстового поля. Переменная nameField имеет свойство errors , которое содержит объект с парами ключ-значение для всех текущих ошибок. Директива ngIf позволяет нам показывать это сообщение об ошибке только тогда, когда не выполняется условие ‘required’. Мы будем использовать тот же подход для сообщения об ошибке минимальной длины.

 <div class="alert alert-danger" *ngIf="nameField.errors.minlength"> The company name should be at least 3 characters long </div> 

Этот div будет виден только тогда, когда minlength требования не будут выполнены. здесь мы можем сделать сообщение об ошибке немного более динамичным.

В настоящее время мы указали минимальную длину в двух местах — в атрибуте ввода и текстовом поле. Мы можем улучшить это, заменив жестко закодированный «3» на свойство minlength объекта minlength следующим образом:

 <div class="alert alert-danger" *ngIf="nameField.errors.minlength"> The company name should be at least {{ nameField.errors.minlength.requiredLength }} characters long </div> 

Таким образом, номер минимальной длины в сообщении об ошибке будет зависеть от атрибута minlength .

Теперь мы сделаем то же самое с полем раскрывающегося списка для отрасли компании:

 <select class="form-control" name="company-industry" ngModel #industryField="ngModel" required> 

Мы создадим список параметров для раскрывающегося списка в компоненте, связанном с этим представлением, чтобы избежать жесткого кодирования значений в HTML.

 export class ContactFormComponent implements OnInit { industries = [ {id: 1, name: "Agriculture"}, {id: 2, name: "Manufacturing"}, {id: 3, name: "Energy"}, {id: 4, name: "Transportation"}, {id: 5, name: "Finance"} ]; } 

Теперь мы можем перечислить все параметры в представлении через директиву ngFor . Он создаст тег параметра для каждого элемента в массиве industries из компонента.

 <option *ngFor="let industry of industries" [value]="industry.id"> {{ industry.name }} </option> 

Проверка для этого поля довольно проста и похожа на проверку для поля названия компании:

 <div class="alert alert-danger" *ngIf="industryField.touched && !industryField.valid"> The industry is required </div> 

Теперь наша форма готова к отправке. Ранее мы связывали событие ngSubmit с методом submitCompany ; давайте перейдем к компоненту и добавим это сейчас:

 export class ContactFormComponent implements OnInit { submitCompany(form){ console.log(form.value); alert("The form was submitted"); form.reset(); } } 

Параметр form будет содержать все данные из формы. С другой стороны, form.value будет содержать только объект со значениями полей в форме.

Здесь я просто запишу результат в консоль, но вы можете делать с ним все, что захотите. Я добавил предупреждение с сообщением, чтобы сообщить пользователю, что форма была отправлена. Это не обязательно, но рекомендуется показывать какие-то уведомления. form.reset() вернет форму в исходное состояние после отправки, что означает, что поля будут очищены.

Хорошо, давайте посмотрим, как должна выглядеть наша форма:
https://sitepoint-editors.github.io/company-registration-form/

Реактивные формы

Другой вид формы, который вы можете создать — это реактивная форма, которая позволяет вам явно создавать управляющие объекты для полей формы самостоятельно. Этот подход является хорошим выбором, когда вы создаете более сложную форму и хотите иметь больший контроль над ее поведением.

Предположим, нам нужно создать форму регистрации учетной записи, в которой будет два поля для адреса электронной почты и пароля. Мы будем использовать Bootstrap для стилизации этой формы.

Первым шагом является импорт класса ReactiveFormsModule в app.module потому что это необходимо для всех реактивных форм:

 import { ReactiveFormsModule } from "@angular/forms"; @NgModule({ imports: [ ReactiveFormsModule ] }) 

Затем нам нужно импортировать FormGroup и FormControl в компонент для нашей страницы, чтобы явно определить наши объекты управления:

 import { FormGroup, FormControl } from "@angular/forms"; 

Теперь мы должны создать экземпляр класса FormGroup и указать все поля в нашей форме. Проще говоря, мы перечислим пары ключ-значение. Ключи будут именами полей, а значения будут объектами формы.

 accountForm = new FormGroup({ email: new FormControl(), password: new FormControl(); 

Далее мы должны создать форму. Нам снова понадобится <form> . Мы добавим к FormGroup директиву FormGroup и accountForm форму HTML с accountForm группы формы accountForm мы создали в компоненте:

 <form [formGroup]="accountForm"></form> 

Далее мы создадим поле ввода электронной почты. Мы применим к formControlName директиву formControlName и formControlName ей соответствующий ключ в списке элементов управления, которые мы создали в компонентах email .

 <input type="text" class="form-control" id="email" formControlName="email"> 

Мы сделаем то же самое для поля пароля:

 <input type="text" id="password" class="form-control" formControlName="password"> 

Проверка

Следующим шагом является добавление проверки в форму. Мы не будем использовать какие-либо атрибуты HTML, такие как «обязательные», как в случае форм на основе шаблонов. Вместо этого мы должны назначить все валидаторы при создании объектов управления формой.

Мы вернемся к компоненту, где мы определили нашу accountForm . Все методы валидатора для реактивных форм определены в классе Validators , который мы должны импортировать:

 import { FormGroup, FormControl, Validators } from "@angular/forms"; 

Затем мы назначим валидаторы элементам управления в нашем контроллере. Формат следующий:

  form = new FormGroup({ fieldname: new FormControl( initial value, synchronous validators, asynchronous validators) }); 

Предположим, что поля электронной почты и пароль будут обязательными. Мы также должны проверить, является ли электронная почта действительной. Кроме того, пароль должен содержать как минимум одну заглавную букву, одну строчную букву и одну цифру. Таким образом, мы будем использовать required валидаторы и валидаторы pattern из класса Validators для обоих полей. Мы оставим их начальные значения в виде пустой строки.

 form = new FormGroup({ email: new FormControl("", [Validators.required, Validators.pattern('[a-zA-z0-9_\.]+@[a-zA-Z]+\.[a-zA-Z]+')]), password: new FormControl("", [Validators.required, Validators.pattern('^(?=.*[0-9])(?=.*[az])(?=.*[AZ]).{8,}$')]) }); 

Теперь нам нужно перейти к шаблону и добавить сообщения проверки. Мы сделаем это так же, как и с шаблонно-управляемыми формами. Однако мы получим доступ к объектам управления другим способом. В нашем компоненте мы можем определить свойство, которое дает нам доступ к элементу управления в виде:

 get email(){ return this.accountForm.get("email"); } 

Мы можем получить доступ к этому свойству в нашем шаблоне. Это означает, что вместо того, чтобы писать this.accountForm.get("email") каждый раз, когда мы хотим указать проверочное сообщение, мы можем использовать только email .

 <div *ngIf="email.touched && email.errors"> <div class="alert alert-danger" *ngIf="email.errors.required"> The email is required </div> </div> <div *ngIf="email.errors"> <div class="alert alert-danger" *ngIf="email.errors.pattern"> The email is not valid </div> </div> 

Таким образом, сообщение «Требуется электронная почта» появится после того, как пользователь коснулся формы и оставил ее пустой, а сообщение «Электронная почта недействительна» появится, когда пользователь печатает. Мы можем отображать сообщения проверки для поля пароля таким же образом.

Давайте перейдем к отправке нашей реактивной формы. Во-первых, мы можем отключить кнопку отправки аналогично тому, который мы использовали в форме на основе шаблона:

 <button class="btn btn-primary" type="submit" [disabled]="!accountForm.valid">Sign up</button> 

Нам также нужно привязать событие ngSubmit к функции, которая будет вызываться при ngSubmit .

 <form [formGroup]="accountForm" (ngSubmit)="signup()"> 

Затем нам нужно определить эту функцию в контроллере:

 signup(){ console.log(this.accountForm.value); alert('The form was submitted'); this.accountForm.reset(); } 

А пока мы покажем представленные данные в консоли. Мы очистим поля формы после отображения подтверждающего сообщения.

Асинхронная проверка

Было бы здорово, если бы мы могли проверить, используется ли уже отправленное пользователем электронное письмо. Мы можем выполнить такую ​​проверку, даже если пользователь печатает, если мы используем асинхронный валидатор.

Мы будем использовать поддельный API для целей этой демонстрации — JSON Placeholder . Это полезный инструмент для тестирования приложения, поскольку он предоставляет различные виды данных. Например, он может предоставить список пользователей с электронными письмами, который мы представим как список существующих пользователей для нашего демонстрационного приложения. Вы можете отправлять и получать запросы на него так же, как и в реальном API.

Мы создадим службу в нашем приложении, которая подключается к этому JSON API и присоединяет асинхронный валидатор к полю электронной почты. Таким образом, мы сможем проверить, используется ли уже электронная почта.

Сначала мы создадим сервис. Мы можем сделать это через Angular CLI

 ng g service server.service 

Затем мы должны добавить сервис в app.module чтобы мы могли использовать его в приложении:

 import { ServerService } from "./server.service"; @NgModule({ providers: [ ServerService ], bootstrap: [AppComponent] }) 

В нашем сервисе нам нужно импортировать классы Injectable, Http и Observable а также операторы map и filter RxJS. Затем мы укажем URL нашего тестового API. После того, как мы получим результаты, мы отфильтруем их, чтобы увидеть, есть ли у пользователя электронная почта, совпадающая с той, которую набрал пользователь, которую мы передадим ему при выполнении запроса.

 @Injectable() export class ServerService { private url = "http://jsonplaceholder.typicode.com/users"; constructor(private http: Http) { } checkUsers(email: string) { return this.http .get(this.url) .map(res => res.json()) .map(users => users.filter(user => user.email === email)) .map(users => !users.length); } } 

Теперь нам нужно создать валидатор, который будет использовать этот сервис для проверки электронной почты. Мы создадим новый файл машинописного текста custom.validators.ts . Это позволит нам более эффективно разделять наш код и повторно использовать валидатор. Там мы импортируем классы AbstractControl и ValidationErrors а также ServerService .

 import { AbstractControl, ValidationErrors } from '@angular/forms'; import { ServerService } from './server.service'; export class Customvalidators{ static checkDuplicateEmail(serverService: ServerService) { return (control: AbstractControl) => { return serverService.checkUsers(control.value).map(res => { return res ? null : { duplicateEmail: true }; }); }; } } 

Мы создаем экземпляр нашего serverService и вызываем созданный в нем метод checkUsers . Пользовательские валидаторы должны возвращать null если все в порядке, или объект с парами ключ-значение, которые иначе описывают ошибку.

Теперь перейдем к нашему компоненту, чтобы применить асинхронный валидатор к полю электронной почты. Нам также придется импортировать ServerService в компонент и создать его экземпляр, чтобы выполнить запрос к нашему тестовому API.

 import { ServerService } from "../server.service"; constructor(private serverService: ServerService){ } accountForm = new FormGroup({ email: new FormControl("", synchronous validators, Customvalidators.checkDuplicateEmail(this.serverService)) }); 

Осталось только добавить сообщение проверки

 <div *ngIf="email.errors"> <div class="alert alert-danger" *ngIf="email.errors.duplicateEmail"> The email is already in use </div> </div> 

Теперь посмотрим, как выглядит наша форма.
https://sitepoint-editors.github.io/account-registration-form/

Завершение

Как вы можете видеть, Angular позволяет вам делать несколько простых трюков с формами. Вы можете не только быстро создавать простые формы, создавая их на основе шаблонов, но и реализовывать в них сложные функции, если это необходимо.