Table of Contents
Comprendre le modèle d'usine en angulaire
Les applications angulaires modernes nécessitent souvent des formulaires qui s'adaptent à différents rôles d'utilisateur, schémas de données ou environnements de configuration. La création manuelle d'instances pour chaque permutation conduit à un code dupliqué, à des cas de commutation fragiles et à une logique de validation enchevêtrée. Le Factory Pattern s'adresse à cela en fournissant un mécanisme centralisé pour produire des objets de forme basés sur un type ou une configuration défini.
L'usine encapsule la logique d'invocation, de sorte que les composants n'ont plus besoin de savoir quelle classe de forme doit inocaliser ou comment assembler les commandes. Cette séparation des préoccupations rend la base de code plus modulaire, testable et évolutive, surtout lorsque le nombre de types de forme augmente au-delà d'une poignée. Le modèle s'intègre naturellement dans le système d'injection de dépendance Angular, vous permettant d'enregistrer les fonctions d'usine en tant que fournisseurs et de les injecter lorsque nécessaire.
Pourquoi utiliser le modèle d'usine pour les formes dynamiques?
Angulaire fournit déjà le pour gérer des scénarios de forme complexes, mais le modèle d'usine élimine la plaque de chaudière procédurale qui accompagne souvent la création de la forme. Voici les principaux avantages:
- Réduit la duplication de code Au lieu de répéter des constructions similaires à travers des composants ou des services, vous centralisez la logique dans une méthode d'usine. Chaque type de formulaire est construit par une classe de constructeurs dédiée, donc vous n'écrivez sa logique qu'une seule fois.
- Enhances maintainability Ajouter un nouveau type de formulaire signifie créer une nouvelle classe de constructeur et l'enregistrer dans l'usine. Aucun composant existant n'a besoin de changer, tant qu'il demande des formulaires par type.
- Permet des configurations de formulaires flexibles. L'usine peut accepter des paramètres supplémentaires (p. ex. permissions d'utilisateur, local ou drapeaux de fonctions) qui influencent les commandes incluses ou la façon dont elles sont validées.
- Supporte les interfaces utilisateur dynamiques. Lorsque le type de formulaire est déterminé à l'exécution (par exemple, à partir d'un enregistrement de base de données ou d'un paramètre de route), l'usine le rend trivial d'invoquer le bon à la volée.
- Améliore la testabilité. Chaque constructeur de formulaire peut être testé indépendamment. L'usine elle-même devient une simple cartographie qui peut être simulée ou étalonnée dans les tests de composants.
Ces avantages sont particulièrement précieux dans les applications d'entreprise qui doivent supporter de nombreux flux de travail distincts, chacun avec son propre ensemble de champs et de règles de validation.
Étapes de mise en œuvre essentielles
Étape 1: Définir le dénombrement et les interfaces de type de formulaire
Commencez par définir un ensemble de types de formulaires. L'utilisation d'un enum prévient les typos et fournit un contrat clair:
export enum FormType {
Login = 'login',
Registration = 'registration',
Feedback = 'feedback',
Contact = 'contact'
}
Ensuite, définissez une interface que tous les constructeurs de formes mettront en œuvre. Cela garantit que l'usine peut les traiter polymorphement:
import { FormGroup } from '@angular/forms';
export interface FormBuilderInterface {
build(config?: any): FormGroup;
}
Le paramètre optionnel permet aux constructeurs de personnaliser le formulaire en fonction des données d'exécution, comme les valeurs de pré-remplissage ou les règles de validation par rôle utilisateur.
Étape 2: Créer des constructeurs de formulaires de béton
Chaque constructeur de béton implémente et retourne un correctement configuré]. Par exemple, un constructeur de formulaire de connexion pourrait ressembler à ceci:
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]
});
}
}
Un constructeur de formulaire d'inscription pourrait inclure des contrôles supplémentaires et une validation croisée sur le terrain :
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 };
}
}
Cette structure s'équilibre proprement : chaque constructeur se concentre uniquement sur ses propres détails de formulaire, et vous pouvez réutiliser la logique en composant des constructeurs ou en utilisant des fonctions d'aide.
Étape 3: Construire la classe d'usine
La classe usine est responsable de la cartographie d'un type de formulaire à son constructeur correspondant. Une implémentation simple utilise une instruction de commutation ou un objet de carte:
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);
}
}
L'enregistrement des constructeurs à l'intérieur du constructeur est simple, mais pour les applications plus grandes, vous pouvez préférer utiliser Angular , injection de dépendance pour fournir des constructeurs. Cette approche est couverte plus tard.
Étape 4: Utiliser l'usine dans les composants
Avec l'usine en place, les composants peuvent demander une instance de formulaire sans connaître les détails de sa construction. Par exemple, un composant qui gère plusieurs types de formulaires basés sur un paramètre de route:
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: `