Skip to main content

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:

  1. FormModel: Definizione JSON della struttura del form
  2. FormBuilderService: Servizio che costruisce reactive form dai modelli
  3. 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 form
  • extract(options?: ExtractOptions): T - Estrae i dati del form in JSON
  • reset(): void - Resetta ai valori di default
  • isFieldVisible(key: string): boolean - Controlla se un campo è visibile
  • onVisibilityChange(key: string, callback): () => void - Subscribe ai cambiamenti di visibilità

TessAutoFormComponent

Inputs

  • model (required): Definizione del form model
  • formBind (required): FormBind da FormBuilderService
  • showSubmit (default: true): Mostra bottone submit
  • showCancel (default: true): Mostra bottone cancel
  • showReset (default: false): Mostra bottone reset
  • submitLabel: Override label bottone submit
  • cancelLabel: Override label bottone cancel
  • resetLabel (default: 'Reset'): Label bottone reset

Outputs

  • submit: Form inviato (emette il valore del form)
  • cancel: Bottone cancel cliccato
  • reset: Bottone reset cliccato
  • valueChange: Valore del form cambiato
  • visibilityChange: 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

  1. Valida i modelli in anticipo: Usa FormModelValidator per catturare errori prima della costruzione
  2. Registra i validatori asincroni: Registra tutti i validatori asincroni all'avvio dell'app
  3. Usa TypeScript: Definisci interfacce per i tuoi dati del form
  4. Gestisci gli errori: Controlla sempre result.isSuccess prima di usare il form
  5. Cleanup: Disiscriviti dai cambiamenti di visibilità in ngOnDestroy
  6. Performance: Usa OnPush change detection
  7. Accessibilità: Fornisci label e testi di aiuto significativi

Vedi Anche