Передовые технологии производства
Реализация фабричного шаблона для динамического формирования формы в угловых формах
Table of Contents
Понимание фабричного шаблона в Angular
Современные угловые приложения часто требуют форм, которые адаптируются к различным ролям пользователей, схемам данных или конфигурационным средам. Ручное создание экземпляров для каждой перестановки приводит к дублированию кода, хрупким случаям переключения и запутанной логике валидации. Заводской шаблон решает эту проблему, предоставляя централизованный механизм для создания объектов формы на основе определенного типа или конфигурации. По сути, завод действует как формообразующий оркестратор: он принимает спецификацию (например, строку, числовое число или объект конфигурации) и возвращает полностью инициализированный с соответствующими элементами управления, валидаторами и значениями по умолчанию.
Завод инкапсулирует логику инстанциации, поэтому компонентам больше не нужно знать, какой класс формы для инстанцирования или как собирать элементы управления. Это разделение проблем делает кодовую базу более модульной, проверяемой и масштабируемой — особенно когда количество типов форм растет за пределами горстки. Модель естественным образом вписывается в систему впрыска зависимости Angular, позволяя регистрировать заводские функции в качестве поставщиков и вводить их там, где это необходимо.
Зачем использовать заводской шаблон для динамических форм?
Angular уже предоставляет для обработки сложных сценариев формы, но заводской шаблон устраняет процедурный шаблон, который часто сопровождает создание формы.
- Уменьшает дублирование кода. Вместо того, чтобы повторять подобные конструкции между компонентами или сервисами, вы централизуете логику в одном заводском методе. Каждый тип формы построен специальным классом строителей, поэтому вы пишете его логику только один раз.
- Улучшает ремонтопригодность. Добавление нового типа формы означает создание нового класса строителей и регистрацию его на заводе. Никакие существующие компоненты не должны меняться, если они запрашивают формы по типу.
- Разрешает гибкие конфигурации форм. Завод может принимать дополнительные параметры (например, разрешения пользователей, локальные или функциональные флаги), которые влияют на то, какие элементы управления включены или как они проверяются.
- Поддерживает динамические пользовательские интерфейсы. Когда тип формы определяется во время выполнения (например, из записи базы данных или параметра маршрута), завод делает тривиальным инстанцирование правильного на лету.
- Улучшает проверяемость. Каждый конструктор форм может быть протестирован отдельно. Сам завод становится простым отображением, которое можно высмеять или заглушить в тестах компонентов.
Эти преимущества особенно ценны в корпоративных приложениях, которые должны поддерживать множество различных рабочих процессов, каждый со своим набором полей и правилами проверки.
Основные этапы реализации
Шаг 1: Определите тип формы и интерфейсы
Начните с определения набора типов форм. Использование перечня предотвращает опечатки и обеспечивает четкий контракт:
export enum FormType {
Login = 'login',
Registration = 'registration',
Feedback = 'feedback',
Contact = 'contact'
}
Далее определите интерфейс, который будут реализовывать все строители форм. Это гарантирует, что завод сможет обрабатывать их полиморфно:
import { FormGroup } from '@angular/forms';
export interface FormBuilderInterface {
build(config?: any): FormGroup;
}
Дополнительный параметр позволяет разработчикам настраивать форму на основе данных времени выполнения, таких как значения предварительной заполнения или правила корректировки проверки на роль пользователя.
Шаг 2: Создайте бетонные конструкции
Каждый бетоностроитель реализует и возвращает правильно настроенный . Например, строитель формы входа может выглядеть так:
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
export class LoginFormBuilder implements FormBuilderInterface {
private fb: FormBuilder;
constructor() {
this.fb = new FormBuilder();
}
build(): FormGroup {
return this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]],
rememberMe: [false]
});
}
}
Создатель регистрационной формы может включать в себя дополнительные элементы управления и проверку поперечного поля:
export class RegistrationFormBuilder implements FormBuilderInterface {
private fb: FormBuilder;
constructor() {
this.fb = new FormBuilder();
}
build(): FormGroup {
const form = this.fb.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
confirmPassword: ['', Validators.required]
}, { validators: this.matchPasswords });
return form;
}
private matchPasswords(group: FormGroup): { [key: string]: any } | null {
const password = group.get('password')?.value;
const confirm = group.get('confirmPassword')?.value;
return password === confirm ? null : { mismatch: true };
}
}
Эта структура масштабируется чисто: каждый строитель фокусируется исключительно на деталях своей формы, и вы можете повторно использовать логику, составляя строителей или используя вспомогательные функции.
Шаг 3: Постройте заводской класс
Заводской класс отвечает за отображение типа формы на соответствующий конструктор.Простая реализация использует выключатель или объект карты:
import { Injectable } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormType } from './form-type.enum';
import { LoginFormBuilder } from './login-form.builder';
import { RegistrationFormBuilder } from './registration-form.builder';
import { FeedbackFormBuilder } from './feedback-form.builder';
@Injectable({ providedIn: 'root' })
export class FormFactory {
private builders = new Map<FormType, FormBuilderInterface>();
constructor() {
// Register builders
this.builders.set(FormType.Login, new LoginFormBuilder());
this.builders.set(FormType.Registration, new RegistrationFormBuilder());
this.builders.set(FormType.Feedback, new FeedbackFormBuilder());
}
createForm(type: FormType, config?: any): FormGroup {
const builder = this.builders.get(type);
if (!builder) {
throw new Error(`No builder registered for form type: ${type}`);
}
return builder.build(config);
}
}
Регистрация строителей внутри конструктора проста, но для более крупных приложений вы можете предпочесть использовать впрыск зависимости Angular, чтобы обеспечить строителей.
Шаг 4: Используйте фабрику в комплектующих
При наличии завода компоненты могут запрашивать экземпляр формы, не зная деталей его конструкции. Например, компонент, который обрабатывает несколько типов форм на основе параметра маршрута:
import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormFactory } from './form.factory';
import { FormType } from './form-type.enum';
@Component({
selector: 'app-dynamic-form',
template: `