Формы имеют решающее значение для любого современного интерфейсного приложения и являются функцией, которую мы используем каждый день, даже если не осознаем этого. Формы необходимы для безопасного входа пользователя в приложение, поиска всех доступных отелей в конкретном городе, бронирования такси, составления списка дел и выполнения множества других вещей, к которым мы привыкли. Некоторые формы имеют только пару полей ввода, в то время как другие формы могут иметь массив полей, которые растягиваются на пару страниц или вкладок.
В этом уроке мы поговорим о различных стратегиях, доступных для разработки форм на Angular. Независимо от выбранной вами стратегии, вот что должна охватывать библиотека форм:
- Поддержка двусторонней привязки, чтобы входные управляющие значения были синхронизированы с состоянием компонента.
- Следите за состоянием формы и используйте визуальные подсказки, чтобы пользователь знал, является ли текущее состояние действительным или нет. Например, если имя пользователя содержит недопустимые символы, вокруг поля ввода для имени пользователя должна появиться красная рамка.
- Иметь механизм для правильного отображения ошибок валидации.
- Включите или отключите определенные части формы, если не выполнены некоторые критерии проверки.
Введение в формы в угловых
Angular, будучи полноценной интерфейсной средой, имеет свой собственный набор библиотек для построения сложных форм. В последней версии Angular есть две мощные стратегии построения форм. Они есть:
- управляемые шаблоном формы
- модельные или реактивные формы
Обе технологии принадлежат библиотеке @angular/forms
и основаны на одних и тех же классах управления формой. Тем не менее, они заметно отличаются по своей философии, стилю программирования и технике. Выбор одного зависит от вашего личного вкуса, а также от сложности формы, которую вы пытаетесь создать. На мой взгляд, вы должны сначала попробовать оба подхода, а затем выбрать тот, который соответствует вашему стилю и проекту под рукой.
В первой части учебника будут рассмотрены формы на основе шаблонов с практическим примером: создание формы регистрации с проверкой для всех полей формы. Во второй части этого руководства мы повторим шаги по созданию той же формы, используя вместо этого подход, основанный на моделях.
Шаблонно-управляемые формы
Подход на основе шаблонов — это стратегия, заимствованная из эпохи AngularJS. На мой взгляд, это самый простой метод построения форм. Как это работает? Мы будем использовать некоторые угловые директивы.
Директивы позволяют вам привязывать поведение к элементам в DOM.
— угловая документация
Angular предоставляет специфичные для формы директивы, которые можно использовать для привязки входных данных формы и модели. Специфичные для формы директивы добавляют дополнительную функциональность и поведение к простой форме HTML. Конечным результатом является то, что шаблон заботится о связывании значений с помощью модели и проверки формы.
В этом руководстве мы будем использовать формы на основе шаблонов для создания страницы регистрации приложения. Форма будет охватывать наиболее распространенные элементы формы и различные проверки правильности этих элементов формы. Вот шаги, которые вы будете следовать в этом руководстве.
- Добавьте FormsModule в
app.module.ts
. - Создайте класс для модели User.
- Создайте исходные компоненты и макет для формы регистрации.
- Используйте директивы угловой формы, такие как
ngModel
,ngModelGroup
иngForm
. - Добавьте проверку с использованием встроенных валидаторов.
- Отображать ошибки проверки значимым.
- Обработайте
ngSubmit
формы, используяngSubmit
.
Давайте начнем.
Предпосылки
Код для этого проекта доступен на моем репозитории GitHub . Загрузите почтовый индекс или клонируйте репо, чтобы увидеть его в действии. Если вы предпочитаете начинать с нуля, убедитесь, что у вас установлен Angular CLI. Используйте команду ng
для создания нового проекта.
1
|
$ ng new SignupFormProject
|
Затем создайте новый компонент для RegistrationForm.
1
|
ng generate component SignupForm
|
Замените содержимое app.component.html следующим:
1
|
<app-signup-form> </app-signup-form>
|
Вот структура каталогов для каталога src / . Я удалил некоторые ненужные файлы, чтобы все было просто.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
.
├── app
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── signup-form
│ │ ├── signup-form.component.css
│ │ ├── signup-form.component.html
│ │ └── signup-form.component.ts
│ └── User.ts
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── tsconfig.app.json
└── typings.d.ts
|
Как видите, каталог для компонента SignupForm
был создан автоматически. Вот куда пойдет большая часть нашего кода. Я также создал новый User.ts
для хранения нашей модели User.
Шаблон HTML
Прежде чем мы углубимся в фактический шаблон компонента, нам нужно иметь абстрактное представление о том, что мы создаем. Итак, вот структура формы, которую я имею в виду. Форма регистрации будет иметь несколько полей ввода, элемент select и элемент checkbox.
Вот HTML-шаблон, который мы будем использовать для нашей страницы регистрации.
HTML шаблон
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<div class=»row custom-row»>
<div class= «col-sm-5 custom-container jumbotron»>
<form class=»form-horizontal»>
<fieldset>
<legend>SignUp</legend>
<!— Email Block —>
<div class=»form-group»>
<label for=»inputEmail»>Email</label>
<input type=»text»
id=»inputEmail»
placeholder=»Email»>
</div>
<!— Password Block —>
<div class=»form-group»>
<label for=»inputPassword»>Password</label>
<input type=»password»
id=»inputPassword»
placeholder=»Password»>
</div>
<div class=»form-group»>
<label for=»confirmPassword» >Confirm Password</label>
<input type=»password»
id=»confirmPassword»
placeholder=»Password»>
</div>
<!— Select gender Block —>
<div class=»form-group»>
<label for=»select»>Gender</label>
<select id=»select»>
<option>Male</option>
<option>Female</option>
<option>Other</option>
</select>
</div>
<!— Terms and conditions Block —>
<div class=»form-group checkbox»>
<label>
<input type=»checkbox»> Confirm that you’ve read the Terms and
Conditions
</label>
</div>
<!— Buttons Block —>
<div class=»form-group»>
<button type=»reset» class=»btn btn-default»>Cancel</button>
<button type=»submit» class=»btn btn-primary»>Submit</button>
</div>
</fieldset>
</form>
</div>
</div>
|
Классы CSS, используемые в шаблоне HTML, являются частью библиотеки Bootstrap, используемой для создания красивых вещей. Поскольку это не учебник по дизайну, я не буду много говорить об аспектах CSS формы, если в этом нет необходимости.
Основные настройки формы
Чтобы использовать управляемые шаблоном директивы форм, нам нужно импортировать FormsModule
из @angular/forms
и добавить его в массив app.module.ts
в app.module.ts
.
приложение / app.module.ts
01
02
03
04
05
06
07
08
09
10
11
12
13
|
import { FormsModule } from ‘@angular/forms’;
@NgModule({
.
.
imports: [
BrowserModule,
FormsModule
],
.
.
})
export class AppModule { }
|
Затем создайте класс, который будет содержать все свойства объекта User. Мы можем использовать интерфейс и реализовать его в компоненте или использовать класс TypeScript для модели.
приложение / User.ts
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
export class User {
id: number;
email: string;
//Both the passwords are in a single object
password: {
pwd: string;
confirmPwd: string;
};
gender: string;
terms: boolean;
constructor(values: Object = {}) {
//Constructor initialization
Object.assign(this, values);
}
}
|
Теперь создайте экземпляр класса в компоненте RegistrationForm. Я также объявил дополнительную собственность для пола.
приложение / регистрация форма / регистрация-form.component.ts
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import { Component, OnInit } from ‘@angular/core’;
// Import the User model
import { User } from ‘./../User’;
@Component({
selector: ‘app-signup-form’,
templateUrl: ‘./signup-form.component.html’,
styleUrls: [‘./signup-form.component.css’]
})
export class SignupFormComponent implements OnInit {
//Property for the gender
private gender: string[];
//Property for the user
private user:User;
ngOnInit() {
this.gender = [‘Male’, ‘Female’, ‘Others’];
//Create a new user object
this.user = new User({
email:»», password: { pwd: «» , confirm_pwd: «»},
gender: this.gender[0], terms: false});
}
}
|
Для файла signup-form.component.html я собираюсь использовать тот же шаблон HTML, который обсуждался выше, но с небольшими изменениями. Форма регистрации имеет поле выбора со списком параметров. Хотя это работает, мы сделаем это угловым путем, просматривая список с ngFor
директивы ngFor
.
приложение / регистрация форма / регистрация-form.component.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<div class=»row custom-row»>
<div class= «col-sm-5 custom-container jumbotron»>
<form class=»form-horizontal»>
<fieldset>
<legend>SignUp</legend>
.
.
<!— Gender Block —>
<div class=»form-group»>
<label for=»select»>Gender</label>
<select id=»select»>
<option *ngFor = «let g of gender»
[value] = «g»> {{g}}
</option>
</select>
</div>
.
.
</fieldset>
</form>
</div>
</div>
|
Далее мы хотим связать данные формы с объектом класса пользователя, чтобы при вводе данных регистрации в форму создавался новый объект пользователя, который временно сохраняет эти данные. Таким образом, вы можете синхронизировать представление с моделью, и это называется привязкой.
Есть несколько способов сделать это. Позвольте мне сначала познакомить вас с ngModel
и ngForm
.
ngForm и ngModel
ngForm и ngModel — это угловые директивы, которые необходимы для создания форм на основе шаблонов. Давайте начнем с ngForm
первую очередь. Вот выдержка о ngForm из Angular Docs.
Директива
NgForm
дополняет элементform
дополнительными функциями. Он содержит элементы управления, созданные вами для элементов с директивойngModel
и атрибутомname
, и отслеживает их свойства, включая их достоверность. Он также имеет собственноеvalid
свойство, которое имеет значение true, только если каждый содержащийся элемент управления является действительным
Сначала обновите форму с ngForm
директивы ngForm
:
приложение / регистрация форма / регистрация-form.component.html
1
2
3
4
5
6
|
<form
class=»form-horizontal»
#signupForm = «ngForm»>
.
.
</form>
|
#signupForm
является ссылочной переменной шаблона, которая ссылается на директиву ngForm
которая управляет всей формой. В приведенном ниже примере демонстрируется использование ссылочного объекта ngForm
для проверки.
приложение / регистрация форма / регистрация-form.component.html
1
2
3
4
5
6
|
<button
type=»submit»
class=»btn btn-success»
[disabled]=»!signupForm.form.valid»>
Submit
</button>
|
Здесь signupForm.form.valid
вернет false, если все элементы формы не пройдут соответствующие проверки. Кнопка отправки будет отключена, пока форма не будет действительной.
Что касается связывания шаблона и модели, существует множество способов сделать это, и ngModel
имеет три различных синтаксиса для решения этой ситуации. Они есть:
- [(NgModel)]
- [NgModel]
- ngModel
Давайте начнем с первого.
Двустороннее связывание с использованием [(ngModel)]
[(ngModel)]
выполняет двустороннюю привязку для чтения и записи значений управления вводом. Если используется директива [(ngModel)]
, поле ввода получает начальное значение из класса связанных компонентов и обновляет его обратно всякий раз, когда обнаруживается любое изменение значения элемента управления вводом (при нажатии клавиши и нажатии кнопки). Изображение ниже лучше описывает процесс двустороннего связывания.
Вот код для поля ввода электронной почты:
1
2
3
4
5
6
7
8
|
<div class=»form-group»>
<label for=»inputEmail»>Email</label>
<input type=»text»
[(ngModel)] = «user.email»
id=»inputEmail»
name=»email»
placeholder=»Email»>
</div>
|
[(ngModel)] = "user.email"
привязывает свойство электронной почты пользователя к входному значению. Я также добавил атрибут name и установил name="email"
. Это важно, и вы получите ошибку, если вы не объявили атрибут name при использовании ngModel.
Аналогичным образом добавьте [(ngModel)]
и атрибут уникального имени для каждого элемента формы. Ваша форма должна выглядеть примерно так:
приложение / регистрация форма / регистрация-form.component.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
.
.
.
<div ngModelGroup=»password»>
<div class=»form-group» >
<label for=»inputPassword»>Password</label>
<input type=»password»
[(ngModel)] = «user.password.pwd» name=»pwd»
placeholder=»Password»>
</div>
<div class=»form-group»>
<label for=»confirmPassword» >Confirm Password</label>
<input type=»password»
[(ngModel)] = «user.password.confirmPwd» name=»confirmPwd»
placeholder=»Confirm Password»>
</div>
</div>
<div class=»form-group»>
<label for=»select»>Gender</label>
<select id=»select»
[(ngModel)] = «user.gender» name = «gender»>
<option *ngFor = «let g of gender»
[value] = «g»> {{g}}
</option>
</select>
</div>
.
.
.
|
ngModelGroup
используется для группировки похожих полей формы, чтобы мы могли выполнять проверки только для этих полей формы. Поскольку оба поля пароля связаны, мы поместим их в одну группу ngModelGroup. Если все работает должным образом, user
свойство, связанное с компонентом, должно хранить все значения элемента управления формы. Чтобы увидеть это в действии, добавьте следующее после тега формы:
1
|
{{user |
|
JsonPipe
пользовательское свойство через JsonPipe
чтобы отобразить модель как JSON в браузере. Это полезно для отладки и регистрации. Вы должны увидеть вывод в формате JSON следующим образом.
Значения поступают из вида в модель. А как же наоборот? Попробуйте инициализировать пользовательский объект с некоторыми значениями.
приложение / регистрация форма / регистрация-form.component.ts
1
2
3
4
5
6
7
|
this.user = new User({
//initialized with some data
email:»[email protected]»,
password: { pwd: «» , confirm_pwd: «»},
gender: this.gender[0]
});
|
И они автоматически появляются в виде:
1
2
3
4
|
Синтаксис двустороннего связывания [(ngModel)]
помогает вам создавать формы без особых усилий. Тем не менее, у него есть определенные недостатки; следовательно, есть альтернативный подход, который использует ngModel
или [ngModel]
.
Добавление ngModel в Mix
Когда используется ngModel
, мы фактически несем ответственность за обновление свойства компонента со значениями элемента управления вводом и наоборот. Входные данные не попадают автоматически в пользовательское свойство компонента.
Поэтому замените все экземпляры [(ngModel)] = " "
на ngModel
. Мы сохраним атрибут name
потому что все три версии ngModel нуждаются в атрибуте name
для работы.
приложение / регистрация форма / регистрация-form.component.html
1
2
3
4
5
6
7
8
|
<div class=»form-group»>
<label for=»inputEmail»>Email</label>
<input type=»text»
ngModel
id=»inputEmail»
name=»email»
placeholder=»Email»>
</div>
|
С помощью ngModel
значение атрибута name станет ключом ссылочного объекта signupForm
который мы создали ранее. Так, например, signupForm.value.email
будет хранить контрольное значение для идентификатора электронной почты.
Заменить {{user | json}}
{{user | json}}
с {{signupForm.value | json }}
{{signupForm.value | json }}
потому что здесь хранится все состояние.
Одностороннее связывание с использованием [ngModel]
Что если вам нужно установить начальное состояние из компонента связанного класса? Вот что [ngModel]
делает для вас.
Здесь данные передаются из модели в представление. Внесите следующие изменения в синтаксис для использования односторонней привязки:
приложение / регистрация форма / регистрация-form.component.html
1
2
3
4
5
6
7
8
|
<div class=»form-group»>
<label for=»inputEmail»>Email</label>
<input type=»text»
[ngModel] = «user.email»
id=»inputEmail»
name=»email»
placeholder=»Email»>
</div>
|
Так какой подход выбрать? Если вы используете [(ngModel)]
и ngForm
вместе, вы в конечном итоге будете иметь два состояния для поддержки — user
и signupForm.value
— и это может привести к путанице.
1
2
3
4
5
6
7
8
9
|
{ «email»: «[email protected]»,
«password»: { «pwd»: «thisispassword», «confirm_pwd»: «thisispassword» },
«gender»: «Male»
} //user.value
{ «email»: «[email protected]»,
«password»: { «pwd»: «thisispassword», «confirm_pwd»: «thisispassword» },
«gender»: «Male»
} //signupForm.value
|
Следовательно, я рекомендую использовать метод одностороннего связывания. Но это то, что вам решать.
Проверка и отображение сообщений об ошибках
Вот наши требования к валидации.
- Все формы управления обязательны.
- Отключите кнопку отправки, пока все поля ввода не будут заполнены.
- Поле электронной почты должно содержать идентификатор электронной почты.
- Поле пароля должно иметь минимальную длину 8.
- И пароль, и подтверждение должны совпадать.
Первое легко. Вы должны добавить required
атрибут проверки для каждого элемента формы, например так:
приложение / регистрация форма / регистрация-form.component.html
1
2
3
4
5
|
<input type=»text»
[ngModel] = «user.email» name=»email»
#email = «ngModel»
placeholder=»Email»
required>
|
Помимо required
атрибута я также экспортировал новую справочную переменную шаблона #email
. Это сделано для того, чтобы вы могли получить доступ к элементу управления Angular поля ввода из самого шаблона. Мы будем использовать его для отображения ошибок и предупреждений. Теперь используйте свойство disabled кнопки, чтобы отключить кнопку:
приложение / регистрация форма / регистрация-form.component.html
1
2
3
4
5
6
|
<button
type=»submit»
class=»btn btn-success»
[disabled]=»!signupForm.form.valid»>
Submit
</button>
|
Чтобы добавить ограничение на электронную почту, используйте атрибут pattern, который работает с полями ввода. Шаблоны используются для указания регулярных выражений, подобных приведенному ниже:
1
|
pattern=»[a-z0-9._%+-]+@[a-z0-9.-]+\.[az]{2,3}$»
|
Для поля пароля все, что вам нужно сделать, это добавить minlength=" "
:
приложение / регистрация форма / регистрация-form.component.html
1
2
3
4
5
6
7
8
|
<input type=»password»
ngModel
id=»inputPassword»
name=»pwd»
#pwd = «ngModel»
placeholder=»Password»
minlength=»8″
required>
|
Чтобы отобразить ошибки, я собираюсь использовать условную директиву ngIf
для элемента div. Давайте начнем с поля управления вводом для электронной почты:
приложение / регистрация форма / регистрация-form.component.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
<div class=»form-group»>
<label for=»inputEmail»>Email</label>
<input type=»text»
[ngModel] = «user.email» name=»email»
#email = «ngModel» id=»inputEmail»
placeholder=»Email»
pattern=»[a-z0-9._%+-]+@[a-z0-9.-]+\.[az]{2,3}$»
required>
</div>
<!— This is the error section —>
<div *ngIf=»email.invalid && (email.dirty || email.touched)»
class=»alert alert-danger»>
<div *ngIf = «email.errors?.required»>
Email field can’t be blank
</div>
<div *ngIf = «email.errors?.pattern && email.touched»>
The email id doesn’t seem right
</div>
</div>
|
Здесь много чего происходит. Начнем с первой строки раздела об ошибках.
1
2
|
<div *ngIf=»email.invalid && (email.dirty || email.touched)»
class=»alert alert-danger»>
|
Помните переменную #email
которую мы экспортировали ранее? Он содержит некоторое количество информации о состоянии управления вводом в поле электронной почты. Это включает в себя: email.valid
, email.invalid
, email.dirty
, email.pristine
, email.touched
, email.untouched
и email.errors
. Изображение ниже подробно описывает каждое из этих свойств.
Таким образом, элемент div с *ngIf
будет отображаться только в том случае, если электронная почта недействительна. Тем не менее, пользователь получит сообщение об ошибке, когда поля ввода не заполнены, даже до того, как они получат возможность редактировать форму.
Чтобы избежать этого сценария, мы добавили второе условие. Ошибка будет отображаться только после посещения элемента управления или изменения значения элемента управления .
Вложенные элементы div используются для охвата всех случаев ошибок валидации. Мы используем email.errors
для проверки всех возможных ошибок валидации, а затем отображать их обратно пользователю в виде пользовательских сообщений. Теперь выполните ту же процедуру для других элементов формы. Вот как я закодировал проверку паролей.
приложение / регистрация форма / регистрация-form.component.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<div ngModelGroup=»password» #userPassword=»ngModelGroup» required >
<div class=»form-group»>
<label for=»inputPassword»>Password</label>
<input type=»password»
ngModel name=»pwd»
id=»inputPassword» placeholder=»Password»
minlength =»8″ required>
</div>
<div class=»form-group»>
<label for=»confirmPassword» >Confirm Password</label>
<input type=»password»
ngModel name=»confirmPwd»
id=»confirmPassword» placeholder=»Confirm Password»>
</div>
<div *ngIf=»(userPassword.invalid|| userPassword.value?.pwd != userPassword.value?.confirmPwd) && (userPassword.touched)»
class=»alert alert-danger»>
<div *ngIf = «userPassword.invalid; else nomatch»>
Password needs to be more than 8 characters
</div>
<ng-template #nomatch >
Passwords don’t match
</ng-template>
</div>
</div>
|
Это начинает выглядеть немного грязно. Angular имеет ограниченный набор атрибутов валидатора: required
, minlength
, maxlength
и pattern
. Чтобы охватить любой другой сценарий, такой как сравнение паролей, вам придется полагаться на вложенные условия ngIf
как я делал выше. Или, в идеале, создайте пользовательскую функцию валидатора, о которой я расскажу в третьей части этой серии.
В приведенном выше коде я использовал синтаксис ngIf else
который был представлен в последней версии Angular. Вот как это работает:
1
2
3
4
5
|
<div *ngIf=»isValid;else notvalid»>
Valid content…
</div>
<ng-template #notValid>Not valid content…</ng-template>
|
Отправьте форму с помощью ngSubmit
Мы почти закончили форму. Теперь нам нужно иметь возможность отправить форму, а управление данными формы следует передать методу компонента, скажем, onFormSubmit()
.
приложение / регистрация форма / регистрация-form.component.ts
1
2
3
4
|
<form novalidate
(ngSubmit)=»onFormSubmit(signupForm)»
#signupForm=»ngForm»>
…
|
Теперь для компонента:
приложение / регистрация форма / регистрация-form.component.ts
1
2
3
4
5
6
7
|
…
public onFormSubmit({ value, valid}: { value: User, valid: boolean }) {
this.user = value;
console.log( this.user);
console.log(«valid: » + valid);
}
…
|
Финальная демонстрация
Я поместил окончательную версию приложения в репозиторий GitHub . Вы можете скачать или клонировать его, чтобы попробовать сами. Я добавил несколько классов начальной загрузки, чтобы сделать форму красивой.
Резюме
Мы все здесь закончили. В этом руководстве мы рассмотрели все, что вам нужно знать о создании формы в Angular с использованием подхода на основе шаблонов. Шаблонно-ориентированные формы популярны своей простотой и удобством использования.
Однако, если вам нужно создать форму с большим количеством элементов формы, этот подход станет грязным. Итак, в следующем уроке мы рассмотрим модельный способ построения той же формы.
Поделитесь своими мыслями в комментариях ниже.
Изучите JavaScript: полное руководство
Мы создали полное руководство, которое поможет вам изучить JavaScript , независимо от того, начинаете ли вы как веб-разработчик или хотите изучать более сложные темы.