Dialog Service
Il servizio TessDialogService fornisce un'interfaccia unificata per gestire dialogs attraverso diversi UI Kit (PrimeNG, Bootstrap, Material).
Panoramica
TessDialogService è UI-agnostic e utilizza il pattern Adapter per delegare l'implementazione al UI Kit configurato nell'applicazione.
Caratteristiche
- ✅ UI-agnostic: Funziona con PrimeNG, Bootstrap, Material
- ✅ Type-safe: Supporto TypeScript completo
- ✅ Promise-based: API moderna con async/await
- ✅ Configurabile: Opzioni complete per personalizzazione
- ✅ Reattivo: Observable per aggiornamenti real-time
Installazione
import { provideTessUiLibrary } from '@tess-ui-library/core';
export const appConfig: ApplicationConfig = {
providers: [
provideTessUiLibrary({
uiKit: 'primeng', // o 'bootstrap', 'material'
}),
],
};
Uso Base
Importa il Servizio
import { Component, inject } from '@angular/core';
import { TessDialogService } from '@tess-ui-library/core';
@Component({
selector: 'app-example',
standalone: true,
template: `
<button (click)="openDialog()">Apri Dialog</button>
`,
})
export class ExampleComponent {
private dialogService = inject(TessDialogService);
openDialog() {
this.dialogService.open({
header: 'Conferma Azione',
message: 'Sei sicuro di voler continuare?',
acceptLabel: 'Conferma',
rejectLabel: 'Annulla',
}).then((result) => {
if (result.accepted) {
console.log('Confermato');
} else {
console.log('Annullato');
}
});
}
}
API
open(config: DialogConfig): Promise<DialogResult>
Apre un dialog con la configurazione specificata.
DialogConfig
interface DialogConfig {
header?: string; // Titolo del dialog
message?: string; // Messaggio testuale
component?: Type<any>; // Componente custom da mostrare
data?: any; // Dati da passare al componente
width?: string; // Larghezza (es. '500px', '50%')
height?: string; // Altezza
modal?: boolean; // Modale (blocca interazione fuori dialog)
closable?: boolean; // Mostra pulsante chiusura
closeOnEscape?: boolean; // Chiudi con tasto ESC
dismissableMask?: boolean; // Chiudi cliccando fuori
acceptLabel?: string; // Testo pulsante conferma
rejectLabel?: string; // Testo pulsante annulla
showAccept?: boolean; // Mostra pulsante conferma
showReject?: boolean; // Mostra pulsante annulla
acceptIcon?: string; // Icona pulsante conferma
rejectIcon?: string; // Icona pulsante annulla
acceptClass?: string; // CSS class per pulsante conferma
rejectClass?: string; // CSS class per pulsante annulla
styleClass?: string; // CSS class per il dialog
contentStyle?: any; // Stili inline per il contenuto
}
DialogResult
interface DialogResult {
accepted: boolean; // true se confermato
data?: any; // Dati ritornati dal componente custom
}
close(ref: DialogRef, data?: any): void
Chiude un dialog programmaticamente.
const ref = await this.dialogService.open(config);
// ... più tardi
this.dialogService.close(ref, { someData: 'value' });
closeAll(): void
Chiude tutti i dialog aperti.
this.dialogService.closeAll();
Esempi
Dialog di Conferma Semplice
async confirmDelete() {
const result = await this.dialogService.open({
header: 'Conferma Eliminazione',
message: 'Sei sicuro di voler eliminare questo elemento? Questa azione è irreversibile.',
acceptLabel: 'Elimina',
rejectLabel: 'Annulla',
acceptClass: 'p-button-danger',
acceptIcon: 'pi pi-trash',
});
if (result.accepted) {
this.deleteItem();
}
}
Dialog con Componente Custom
// Componente custom
@Component({
selector: 'app-edit-user',
standalone: true,
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="field">
<label>Nome</label>
<input formControlName="name" />
</div>
<div class="field">
<label>Email</label>
<input formControlName="email" />
</div>
<div class="actions">
<button type="button" (click)="onCancel()">Annulla</button>
<button type="submit">Salva</button>
</div>
</form>
`,
})
export class EditUserComponent {
@Inject(DIALOG_DATA) public data: any;
private dialogRef = inject(DialogRef);
form = new FormGroup({
name: new FormControl(this.data?.user?.name || ''),
email: new FormControl(this.data?.user?.email || ''),
});
onSubmit() {
if (this.form.valid) {
this.dialogRef.close({ user: this.form.value });
}
}
onCancel() {
this.dialogRef.close();
}
}
// Apertura dialog
async editUser(user: User) {
const result = await this.dialogService.open({
header: 'Modifica Utente',
component: EditUserComponent,
data: { user },
width: '600px',
modal: true,
});
if (result.data?.user) {
this.updateUser(result.data.user);
}
}
Dialog di Notifica (Informativo)
async showInfo() {
await this.dialogService.open({
header: 'Informazione',
message: 'L\'operazione è stata completata con successo!',
acceptLabel: 'OK',
showReject: false,
acceptClass: 'p-button-success',
});
}
Dialog di Conferma con Promise/Async
async saveChanges() {
const hasChanges = this.form.dirty;
if (hasChanges) {
const result = await this.dialogService.open({
header: 'Modifiche Non Salvate',
message: 'Ci sono modifiche non salvate. Vuoi salvarle prima di uscire?',
acceptLabel: 'Salva',
rejectLabel: 'Scarta',
});
if (result.accepted) {
await this.saveForm();
}
}
this.router.navigate(['/']);
}
Dialog con Loading State
async processData() {
const dialogRef = this.dialogService.open({
header: 'Elaborazione in corso',
message: 'Attendere prego...',
closable: false,
showAccept: false,
showReject: false,
});
try {
await this.dataService.process();
this.dialogService.close(dialogRef);
await this.dialogService.open({
header: 'Successo',
message: 'Elaborazione completata!',
showReject: false,
});
} catch (error) {
this.dialogService.close(dialogRef);
await this.dialogService.open({
header: 'Errore',
message: 'Si è verificato un errore durante l\'elaborazione.',
showReject: false,
acceptClass: 'p-button-danger',
});
}
}
Dialog Guard (CanDeactivate)
Usa i dialog per implementare guard di navigazione:
import { CanDeactivateFn } from '@angular/router';
import { inject } from '@angular/core';
import { TessDialogService } from '@tess-ui-library/core';
export const unsavedChangesGuard: CanDeactivateFn<any> = async (component) => {
if (!component.hasUnsavedChanges()) {
return true;
}
const dialogService = inject(TessDialogService);
const result = await dialogService.open({
header: 'Modifiche Non Salvate',
message: 'Vuoi davvero uscire? Tutte le modifiche non salvate saranno perse.',
acceptLabel: 'Esci',
rejectLabel: 'Rimani',
acceptClass: 'p-button-danger',
});
return result.accepted;
};
// Uso nella route
{
path: 'edit/:id',
component: EditComponent,
canDeactivate: [unsavedChangesGuard],
}
Configurazione UI Kit Specifica
PrimeNG
Quando usi PrimeNG, il dialog utilizza automaticamente p-dialog:
// Nessuna configurazione aggiuntiva necessaria
// Le classi CSS di PrimeNG vengono applicate automaticamente
Bootstrap
Per Bootstrap, assicurati di avere @ng-bootstrap/ng-bootstrap installato:
npm install @ng-bootstrap/ng-bootstrap
Material
Per Material, installa @angular/material:
npm install @angular/material
Best Practices
- Usa async/await: API più pulita e leggibile
- Gestisci rejects: Gestisci sempre il caso di annullamento
- Dialog piccoli: Mantieni i dialog focalizzati su un'unica azione
- Conferme per azioni distruttive: Sempre confermare eliminazioni e modifiche importanti
- Accessibilità: Usa header descrittivi e label chiari
- Mobile friendly: Usa width responsive (%, vw) invece di px fissi
- Loading states: Mostra feedback durante operazioni asincrone