Создание динамических форм
Многие формы, например анкеты, могут быть очень похожи друг на друга по формату и назначению. Чтобы быстрее и проще создавать разные версии такой формы, можно создать шаблон динамической формы на основе метаданных, описывающих модель бизнес-объекта. Затем используйте шаблон для автоматического создания новых форм в соответствии с изменениями модели данных.
Этот метод особенно полезен, если у вас есть форма, содержимое которой должно часто меняться, чтобы соответствовать быстро меняющимся требованиям бизнеса и нормативным требованиям. Типичный пример — анкета. Возможно, вам потребуется получать данные от пользователей в разных контекстах. Формат и стиль форм, которые видит пользователь, должны оставаться неизменными, а сами вопросы — меняться в зависимости от контекста.
В этом руководстве вы создадите динамическую форму с простой анкетой. Вы создадите онлайн-приложение для героев, которые ищут работу. Агентство постоянно совершенствует процесс подачи заявок, но благодаря динамической форме вы сможете создавать новые формы на лету, не меняя код приложения.
В руководстве рассматриваются следующие шаги:
- Включение реактивных форм в проекте.
- Создание модели данных для элементов управления формы.
- Заполнение модели примерами данных.
- Разработка компонента для динамического создания элементов управления формы.
В созданной вами форме используются проверка данных и стилизация, чтобы сделать работу пользователей удобнее. Кнопка «Отправить» активна только в том случае, если все введенные пользователем данные допустимы. Недопустимые данные выделяются цветом и сопровождаются сообщениями об ошибках.
Базовую версию можно развить, добавив больше типов вопросов, улучшив отображение и сделав работу пользователей еще удобнее.
Включение реактивных форм в проекте
Динамические формы основаны на реактивных формах.
Чтобы предоставить приложению доступ к директивам реактивных форм, импортируйте ReactiveFormsModule из пакета @angular/forms в нужные компоненты.
Создание модели объектов формы
Для динамической формы требуется модель объектов, способная описать все сценарии, необходимые для работы формы. Пример формы заявки для героев представляет собой набор вопросов — то есть каждый элемент управления в форме должен задавать вопрос и принимать ответ.
Модель данных для такой формы должна представлять вопрос. В примере используется DynamicFormQuestionComponent, в котором вопрос определяется как основной объект модели.
Следующий QuestionBase — это базовый класс для набора элементов управления, которые представляют вопрос и ответ на него в форме.
export class QuestionBase<T> {
value: T | undefined;
key: string;
label: string;
required: boolean;
order: number;
controlType: string;
type: string;
options: {key: string; value: string}[];
constructor(
options: {
value?: T;
key?: string;
label?: string;
required?: boolean;
order?: number;
controlType?: string;
type?: string;
options?: {key: string; value: string}[];
} = {},
) {
this.value = options.value;
this.key = options.key || '';
this.label = options.label || '';
this.required = !!options.required;
this.order = options.order === undefined ? 1 : options.order;
this.controlType = options.controlType || '';
this.type = options.type || '';
this.options = options.options || [];
}
}
Определение классов элементов управления
На основе этого базового класса в примере создаются два новых класса — TextboxQuestion и DropdownQuestion, представляющие разные типы элементов управления. При создании шаблона формы на следующем шаге вы создадите экземпляры этих конкретных типов вопросов, чтобы динамически отображать соответствующие элементы управления.
Тип элемента управления TextboxQuestion представлен в шаблоне формы элементом <input>. Он отображает вопрос и позволяет пользователям вводить ответ. Атрибут type элемента определяется на основе поля type, указанного в аргументе options (например, text, email, url).
import {QuestionBase} from './question-base';
export class TextboxQuestion extends QuestionBase<string> {
override controlType = 'textbox';
}
Элемент управления типа DropdownQuestion отображает список вариантов в раскрывающемся списке.
import {QuestionBase} from './question-base';
export class DropdownQuestion extends QuestionBase<string> {
override controlType = 'dropdown';
}
Формирование групп элементов формы
Динамическая форма использует сервис для создания сгруппированных наборов элементов управления ввода на основе модели формы. Следующий QuestionControlService собирает набор экземпляров FormGroup, использующих метаданные из модели вопросов. Можно указать значения по умолчанию и правила проверки.
import {Injectable} from '@angular/core';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {QuestionBase} from './question-base';
@Injectable()
export class QuestionControlService {
toFormGroup(questions: QuestionBase<string>[]) {
const group: any = {};
questions.forEach((question) => {
group[question.key] = question.required
? new FormControl(question.value || '', Validators.required)
: new FormControl(question.value || '');
});
return new FormGroup(group);
}
}
Формирование содержимого динамической формы
Сама динамическая форма представлена компонентом-контейнером, который вы добавите на следующем шаге. Каждый вопрос в шаблоне компонента формы представлен тегом <app-question>, которому соответствует экземпляр DynamicFormQuestionComponent.
Объект DynamicFormQuestionComponent отвечает за отображение сведений об отдельном вопросе на основе значений связанного с данными объекта вопроса. Форма использует [formGroup] директиву, чтобы связать HTML шаблона с базовыми объектами элементов управления. DynamicFormQuestionComponent создает группы форм и заполняет их элементами управления, определенными в модели вопросов, задавая правила отображения и проверки.
Задача DynamicFormQuestionComponent — отображать типы вопросов, определенные в модели. На этом этапе у вас есть только два типа вопросов, но можно представить множество других. Блок @switch в шаблоне определяет, какой тип вопроса отображать. В переключателе используются директивы с селекторами formControlName и formGroup. Обе директивы определены в ReactiveFormsModule.
Предоставление данных
Для создания отдельной формы также нужен сервис, предоставляющий определенный набор вопросов. В этом упражнении вы создадите QuestionService, который будет предоставлять этот массив вопросов из жестко заданных примеров данных. В реальном приложении сервис мог бы получать данные из серверной системы. Однако главное в том, что вопросы в заявке на работу для героев полностью определяются объектами, возвращаемыми QuestionService. Чтобы адаптировать анкету к меняющимся требованиям, достаточно добавлять, обновлять и удалять объекты в массиве questions.
QuestionService предоставляет набор вопросов в виде массива, привязанного к вопросам input().
import {Injectable} from '@angular/core';
import {DropdownQuestion} from './question-dropdown';
import {QuestionBase} from './question-base';
import {TextboxQuestion} from './question-textbox';
import {of} from 'rxjs';
@Injectable()
export class QuestionService {
// TODO: get from a remote source of question metadata
getQuestions() {
const questions: QuestionBase<string>[] = [
new DropdownQuestion({
key: 'favoriteAnimal',
label: 'Favorite Animal',
options: [
{key: 'cat', value: 'Cat'},
{key: 'dog', value: 'Dog'},
{key: 'horse', value: 'Horse'},
{key: 'capybara', value: 'Capybara'},
],
order: 3,
}),
new TextboxQuestion({
key: 'firstName',
label: 'First name',
value: 'Alex',
required: true,
order: 1,
}),
new TextboxQuestion({
key: 'emailAddress',
label: 'Email',
type: 'email',
order: 2,
}),
];
return of(questions.sort((a, b) => a.order - b.order));
}
}
Создание шаблона динамической формы
Компонент DynamicFormComponent — это точка входа и основной контейнер формы, которая представлена в шаблоне с помощью <app-dynamic-form>.
Компонент DynamicFormComponent отображает список вопросов, связывая каждый из них с элементом <app-question>, соответствующим DynamicFormQuestionComponent.
Отображение формы
Чтобы отобразить экземпляр динамической формы, шаблон оболочки AppComponent передает массив questions, возвращаемый компонентом QuestionService, компоненту-контейнеру формы <app-dynamic-form>.
import {Component, inject} from '@angular/core';
import {AsyncPipe} from '@angular/common';
import {DynamicFormComponent} from './dynamic-form.component';
import {QuestionService} from './question.service';
import {QuestionBase} from './question-base';
import {Observable} from 'rxjs';
@Component({
selector: 'app-root',
template: `
<div>
<h2>Job Application for Heroes</h2>
<app-dynamic-form [questions]="questions$ | async" />
</div>
`,
providers: [QuestionService],
imports: [AsyncPipe, DynamicFormComponent],
})
export class AppComponent {
questions$: Observable<QuestionBase<string>[]> = inject(QuestionService).getQuestions();
}
Такое разделение модели и данных позволяет повторно использовать компоненты для любого типа опроса, если он совместим с моделью объекта вопроса.
Проверка корректности данных
В шаблоне формы используется динамическая привязка метаданных, чтобы отображать форму без каких-либо жестко заданных предположений о конкретных вопросах. Метаданные элементов управления и критерии проверки добавляются динамически.
Чтобы гарантировать корректность введенных данных, кнопка «Сохранить» отключена, пока форма не станет корректной. Когда форма станет корректной, нажмите «Сохранить», и приложение отобразит текущие значения формы в формате JSON.
На следующем рисунке показана готовая форма.
Следующие шаги
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/forms/dynamic-forms