AutoForm - Dynamic Form Rendering
AutoForm è un sistema potente per generare dinamicamente form Angular da definizioni JSON. Permette di creare form complessi con validazione, visibilità condizionale e rendering automatico senza scrivere codice template.
Panoramica
AutoForm è composto da tre parti principali:
- FormModel: Definizione JSON della struttura del form
- FormBuilderService: Servizio che costruisce reactive form dai modelli
- TessAutoFormComponent: Componente che renderizza automaticamente i form
Vantaggi
- ✅ Type-safe: Supporto completo TypeScript
- ✅ Dinamico: Crea form a runtime da dati API
- ✅ Riutilizzabile: Condividi definizioni di form tra applicazioni
- ✅ Validazione: Validatori built-in e custom
- ✅ Condizionale: Mostra/nascondi campi basandosi sui valori del form
- ✅ Accessibile: Conforme WCAG 2.1 con supporto ARIA
- ✅ Flessibile: Layout responsive basato su grid
Quick Start
1. Definisci il Form Model
import { FormModel } from '@tess-ui-library/core';
const userFormModel: FormModel = {
id: 'user-registration',
title: 'Registrazione Utente',
description: 'Compila i tuoi dati',
fields: [
{
key: 'firstName',
type: 'text',
label: 'Nome',
validation: { required: true },
},
{
key: 'email',
type: 'email',
label: 'Email',
validation: {
required: true,
asyncValidator: 'uniqueEmail',
},
},
{
key: 'age',
type: 'number',
label: 'Età',
validation: {
min: 18,
max: 120,
},
},
],
};
2. Costruisci il Form
import { Component, inject, OnInit } from '@angular/core';
import { FormBuilderService, FormBind } from '@tess-ui-library/core';
@Component({
selector: 'app-registration',
standalone: true,
template: `
@if (formBind) {
<tess-auto-form
[model]="userFormModel"
[formBind]="formBind"
(submit)="onSubmit($event)"
></tess-auto-form>
}
`,
})
export class RegistrationComponent implements OnInit {
private formBuilder = inject(FormBuilderService);
userFormModel = userFormModel;
formBind?: FormBind;
ngOnInit() {
const result = this.formBuilder.build(this.userFormModel);
if (result.isSuccess) {
this.formBind = result.value;
} else {
console.error('Errori di costruzione form:', result.errors);
}
}
onSubmit(data: any) {
console.log('Form inviato:', data);
}
}
Form Model
L'interfaccia FormModel definisce la struttura del form.
Proprietà FormModel
interface FormModel {
id: string; // Identificatore unico
title?: string; // Titolo del form
description?: string; // Descrizione del form
fields: FormFieldModel[];// Array di campi
layout?: GridLayout; // Configurazione layout grid
submitLabel?: string; // Testo bottone submit
cancelLabel?: string; // Testo bottone cancel
showResetButton?: boolean; // Mostra bottone reset
metadata?: Record<string, any>; // Metadata custom
}
Proprietà FormFieldModel
interface FormFieldModel {
key: string; // Chiave campo (deve corrispondere alla proprietà JSON)
type: ControlType; // Tipo di controllo
label: string; // Label del campo
placeholder?: string; // Testo placeholder
defaultValue?: any; // Valore di default
validation?: FieldValidation; // Regole di validazione
visibleIf?: (formValue: any) => boolean; // Condizione di visibilità
disabled?: boolean; // Stato disabilitato
helpText?: string; // Testo di aiuto
layout?: GridLayout; // Layout grid per questo campo
options?: FormSelectOption[]; // Opzioni per select/radio
cssClass?: string; // Classi CSS aggiuntive
attributes?: Record<string, any>; // Attributi custom
}
Tipi di Controllo
type ControlType =
| 'text'
| 'email'
| 'password'
| 'number'
| 'textarea'
| 'select'
| 'multiselect'
| 'checkbox'
| 'radio'
| 'switch'
| 'date'
| 'datetime';
Validazione
AutoForm supporta validatori built-in e custom.
Validatori Built-in
validation: {
required: true,
minLength: 5,
maxLength: 100,
min: 0,
max: 100,
pattern: /^[A-Z]/, // o string: '^[A-Z]'
messages: {
required: 'Questo campo non può essere vuoto',
minLength: 'Troppo corto!',
}
}
Validatori Custom
validation: {
custom: (value: string) => {
if (value && value.includes('admin')) {
return { forbidden: { value } };
}
return null;
}
}
Validatori Asincroni
Registra validatori asincroni con AsyncValidatorRegistry:
// Nell'inizializzazione dell'app
constructor(private validatorRegistry: AsyncValidatorRegistry) {
this.validatorRegistry.registerAsyncValidator(
'uniqueEmail',
CommonAsyncValidators.uniqueEmail((email) =>
this.userService.checkEmailExists(email)
)
);
}
// Nel form model
{
key: 'email',
type: 'email',
label: 'Email',
validation: {
required: true,
asyncValidator: 'uniqueEmail' // Riferimento al validatore registrato
}
}
Visibilità Condizionale
Usa visibleIf per mostrare/nascondere campi basandosi sui valori del form.
{
key: 'hasDiscount',
type: 'checkbox',
label: 'Applica Sconto',
},
{
key: 'discountCode',
type: 'text',
label: 'Codice Sconto',
visibleIf: (formValue) => formValue.hasDiscount === true
}
Comportamento Visibilità
- I campi nascosti sono disabilitati automaticamente
- I campi nascosti hanno il loro valore impostato a null
- I campi nascosti sono esclusi da
extract()per default - I cambiamenti di visibilità scatenano l'output
visibilityChange
Layout & Styling
AutoForm usa un sistema CSS Grid a 12 colonne.
Layout a livello Form
const formModel: FormModel = {
id: 'my-form',
layout: {
columns: 12 // Colonne totali (default: 12)
},
fields: [...]
};
Layout a livello Campo
{
key: 'fullName',
type: 'text',
label: 'Nome Completo',
layout: {
span: 12 // Larghezza completa
}
},
{
key: 'firstName',
type: 'text',
label: 'Nome',
layout: {
span: 6 // Metà larghezza
}
},
{
key: 'lastName',
type: 'text',
label: 'Cognome',
layout: {
span: 6 // Metà larghezza
}
}
API Reference
FormBuilderService
Metodi
build<T>(model: FormModel): FormResult<FormBind<T>>- Costruisce un reactive form da un FormModel
- Ritorna FormResult con successo o fallimento
FormBind
Proprietà
form: FormGroup- Il FormGroup reattivo generato
Metodi
patch(data: Partial<T>): void- Aggiorna i valori del formextract(options?: ExtractOptions): T- Estrae i dati del form in JSONreset(): void- Resetta ai valori di defaultisFieldVisible(key: string): boolean- Controlla se un campo è visibileonVisibilityChange(key: string, callback): () => void- Subscribe ai cambiamenti di visibilità
TessAutoFormComponent
Inputs
model(required): Definizione del form modelformBind(required): FormBind da FormBuilderServiceshowSubmit(default: true): Mostra bottone submitshowCancel(default: true): Mostra bottone cancelshowReset(default: false): Mostra bottone resetsubmitLabel: Override label bottone submitcancelLabel: Override label bottone cancelresetLabel(default: 'Reset'): Label bottone reset
Outputs
submit: Form inviato (emette il valore del form)cancel: Bottone cancel cliccatoreset: Bottone reset cliccatovalueChange: Valore del form cambiatovisibilityChange: Visibilità campo cambiata
Esempi
Form di Registrazione
const registrationForm: FormModel = {
id: 'registration',
title: 'Crea Account',
fields: [
{
key: 'username',
type: 'text',
label: 'Username',
validation: {
required: true,
minLength: 3,
asyncValidator: 'uniqueUsername',
},
layout: { span: 12 }
},
{
key: 'email',
type: 'email',
label: 'Email',
validation: {
required: true,
asyncValidator: 'uniqueEmail',
},
layout: { span: 12 }
},
{
key: 'password',
type: 'password',
label: 'Password',
validation: {
required: true,
minLength: 8,
},
layout: { span: 6 }
},
{
key: 'confirmPassword',
type: 'password',
label: 'Conferma Password',
validation: {
required: true,
custom: (value, formValue) => {
return value === formValue?.password
? null
: { passwordMismatch: true };
}
},
layout: { span: 6 }
},
],
submitLabel: 'Registrati',
cancelLabel: 'Annulla'
};
Best Practices
- Valida i modelli in anticipo: Usa
FormModelValidatorper catturare errori prima della costruzione - Registra i validatori asincroni: Registra tutti i validatori asincroni all'avvio dell'app
- Usa TypeScript: Definisci interfacce per i tuoi dati del form
- Gestisci gli errori: Controlla sempre
result.isSuccessprima di usare il form - Cleanup: Disiscriviti dai cambiamenti di visibilità in
ngOnDestroy - Performance: Usa
OnPushchange detection - Accessibilità: Fornisci label e testi di aiuto significativi