Реализация фабричного шаблона для динамического формирования формы в угловых формах

Понимание фабричного шаблона в 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: `