Skip to main content

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

  1. Usa async/await: API più pulita e leggibile
  2. Gestisci rejects: Gestisci sempre il caso di annullamento
  3. Dialog piccoli: Mantieni i dialog focalizzati su un'unica azione
  4. Conferme per azioni distruttive: Sempre confermare eliminazioni e modifiche importanti
  5. Accessibilità: Usa header descrittivi e label chiari
  6. Mobile friendly: Usa width responsive (%, vw) invece di px fissi
  7. Loading states: Mostra feedback durante operazioni asincrone

Vedi Anche