Toolbar
Beta

Dialog

The FktDialog service provides a powerful and flexible system for creating modal dialogs in your Angular applications. Built with modern Angular signals and reactive patterns, it supports advanced TypeScript inference, custom components, various sizing options, and pre-built confirmation dialogs with seamless data binding.

Import

import {FktDialogService} from "frakton-ng/dialog";

Simple

Basic dialog implementation with simple content and close functionality.

Confirmation

Dialog for confirmation actions with primary and secondary buttons.

Form

Dialog containing form elements for data input and submission.

Custom

Custom styled dialog with unique appearance and behavior.

Small

Compact dialog size perfect for quick confirmations and alerts.

Fullscreen

Full viewport dialog for complex content and detailed forms.

Overview

Comprehensive showcase of all dialog variants and their use cases.

Dialog

Key Features

  • Advanced TypeScript Inference: Automatic type inference for dialog data based on component signal inputs, outputs, and models
  • Signal-Based Architecture: Built for Angular's new signal APIs with full reactivity support
  • Component Agnostic Design: Dialog components work anywhere - in dialogs, overlays, or standalone
  • Flexible Sizing: Configure dialog width, height, positioning, and responsive behavior
  • Backdrop Interaction: Intelligent handling of clicks outside the dialog with customizable behavior
  • Memory Safe: Automatic lifecycle management with proper cleanup and destroy callbacks
  • Performance Optimized: Efficient DOM management and minimal re-renders
  • Multiple Dialogs: Support for multiple dialogs simultaneously with independent management
  • Confirmation Dialogs: Pre-built confirmation dialog with customizable actions and themes
  • Accessibility: ARIA attributes, focus management, and keyboard navigation support

Types

import {ComponentRef, Type} from "@angular/core";
import {FktButtonAction} from 'frakton-ng/button-legacy';

export interface FktDialogOptions<T> {
    component: Type<T>;                    // Angular component to display
    data: FktReactiveComponentData<T>;     // Data to pass to the component with signal support
    panelOptions?: {                       // Panel configuration options
        width?: string;                    // Dialog width (default: '100%')
        height?: string;                   // Dialog height (default: 'fit-content')
        maxHeight?: string;                // Maximum height (default: '90vh')
        maxWidth?: string;                 // Maximum width (default: '1200px')
        padding?: string;                  // Internal padding (default: '1rem')
        borderRadius?: string;             // Border radius (default: '1rem')
        backgroundColor?: string;          // Background color (default: 'white')
        backdropClick?: () => void;        // Backdrop click handler
    }
}

export interface FktDialogRef<T> {
    componentRef: ComponentRef<T>;         // Reference to the dialog component
    close: () => void;                     // Method to close the dialog
}

export interface FktConfirmActionOptions {
    title: string;                         // Dialog title
    description?: string;                  // Optional description
    actions?: {
        primary?: Partial<FktButtonAction>;   // Primary action button
        secondary?: Partial<FktButtonAction>; // Secondary action button
    };
    backdropClick?: () => void;            // Backdrop click handler
}

TypeScript Inference & Component Design

Required: Signal - Based Component APIs

**Important **: The FktDialog service requires components to use Angular's signal-based APIs (input(), output(), model()) for proper TypeScript inference. Decorator-based components (@Input, @Output) are not supported due to TypeScript compilation limitations.

// ✅ REQUIRED: Signal-based component (fully supported)
@Component({
    selector: 'my-dialog-component',
    template: `...`
})
export class MyDialogComponent {
    // Signal inputs - Required for TypeScript inference
    title = input('Default Title');
    userData = input<User>();
    config = input<DialogConfig>({theme: 'light'});

    // Signal outputs - Required for callback type inference
    onSave = output<FormData>();
    onCancel = output<void>();
    onAction = output<{ type: string, payload: any }>();

    // Signal models - Required for two-way binding
    counter = model(0);
    items = model<string[]>([]);
}

// Perfect TypeScript inference when using signal-based components:
const dialogRef = this.dialogService.open({
    component: MyDialogComponent,
    data: {
        // ✅ Full IntelliSense for all inputs
        title: 'Custom Title',          // string - auto-completed
        userData: this.currentUser,     // User type enforced
        config: {
            theme: 'dark'
        },      // DialogConfig type enforced
        // ✅ TypeScript automatically infers callback parameter types
        onSave: (data) => {             // data is automatically typed as FormData
            console.log('Form saved:', data);
            dialogRef.close();
        },
        onCancel: () => {               // void - no parameters
            dialogRef.close();
        },
        onAction: (event) => {          // event is automatically typed as { type: string, payload: any }
            this.handleAction(event.type, event.payload);
        },

        // ✅ Two-way signal binding with type safety
        counter: this.sharedCounter,    // WritableSignal<number>
        items: this.sharedItems,        // WritableSignal<string[]>
    }
});

// ❌ NOT SUPPORTED: Decorator-based components won't compile
@Component({
    selector: 'decorator-component',
    template: `...`
})
export class DecoratorComponent {
    @Input() title: string = 'Default';         // ❌ Won't work
    @Input() userData ?: User;                   // ❌ Won't work
    @Output() onSave = new EventEmitter<FormData>(); // ❌ Won't work
}

// This will result in TypeScript compilation errors:
const willNotCompile = this.dialogService.open({
    component: DecoratorComponent,  // ❌ TypeScript error
    data: { /* ... */} // ❌ No inference available
});

Component Agnostic Design Benefits

Using signal - based APIs makes your dialog components truly ** reusable ** across different contexts:


@Component({
    selector: 'user-profile-form',
    template: `
		<div class= "container">
			<h3> {{title()}}</h3>
			<form (ngSubmit)="handleSubmit()">
				<input [(ngModel)]="name" placeholder="Name" [readonly]="readonly()" />
				<input [(ngModel)]="email" placeholder="Email" [readonly]="readonly()" />
				<div class= "container__actions">
					@if (!readonly()) {
						<button type="submit" [disabled]="!isValid()">Save</button>
						<button type="button" (click)="handleCancel()">Cancel</button>
					}
				</div>
			</form>
		</div>
`,
})
export class UserProfileFormComponent {
    // Signal inputs - work everywhere
    title = input('Edit Profile');
    initialName = input('');
    initialEmail = input('');
    readonly = input(false);

    // Signal outputs - work everywhere
    onSubmit = output<{ name: string, email: string }>();
    onCancel = output<void>();

    // Internal component state
    name = signal('');
    email = signal('');

    ngOnInit() {
        this.name.set(this.initialName());
        this.email.set(this.initialEmail());
    }

    isValid = computed(() =>
        this.name().trim().length > 0 &&
        this.email().includes('@')
    );

    handleSubmit() {
        if (this.isValid()) {
            this.onSubmit.emit({
                name: this.name(),
                email: this.email()
            });
        }
    }

    handleCancel() {
        this.onCancel.emit();
    }
}

// This component works seamlessly in ALL contexts:

// 1. ✅ In FktDialog (with automatic type inference)
this.dialogService.open({
    component: UserProfileFormComponent,
    data: {
        title: 'Create New User',
        initialName: '',
        initialEmail: '',
        readonly: false,
        onSubmit: (userData) => {
            // userData is automatically typed as { name: string, email: string }
            this.userService.create(userData);
            dialogRef.close();
        },
        onCancel: () => dialogRef.close()
    }
});

// 2. ✅ In FktOverlay
this.overlayService.open({
    component: UserProfileFormComponent,
    data: {
        title: 'Edit User',
        initialName: user.name,
        initialEmail: user.email,
        readonly: !canEdit,
        onSubmit: (userData) => this.updateUser(userData),
        onCancel: () => overlayRef.close()
    }
});

// 3. ✅ As standalone component in templates
@Component({
    template: `
	<user-profile-form
		[title]="'User Settings'"
		[initialName]="currentUser.name"
		[initialEmail]="currentUser.email"
		[readonly]="!canEditProfile"
		(onSubmit)="updateProfile($event)"
		(onCancel)="navigateBack()"
	/>
	`
})
export class ProfilePageComponent { /* ... */
}

How the TypeScript Inference Works

The FktDialog service uses advanced TypeScript reflection to analyze your component's signal-based API at compile time:

// The dialog service automatically extracts:
type ComponentInputs<T> = {
    [K in keyof T as T[K] extends InputSignal<any> ? K : never]: InputValue<T[K]>
};

type ComponentOutputs<T> = {
    [K in keyof T as T[K] extends OutputEmitterRef<any> ? K : never]: CallbackFor<T[K]>
};

type ComponentModels<T> = {
    [K in keyof T as T[K] extends ModelSignal<any> ? K : never]: WritableSignal<ModelValue<T[K]>>
};

// Results in perfectly typed dialog data:
type FktDialogData<T> =
    & Partial<ComponentInputs<T>>   // All inputs become optional data properties
    & ComponentOutputs<T>           // All outputs become required callback functions
    & Partial<ComponentModels<T>>; // All models become optional signal bindings

// This magic only works with signal-based APIs!

Migration from Decorator Components

If you have existing decorator - based components, here's how to migrate them:

import {EventEmitter, Input, input, Output, output} from "@angular/core";

// Before: Decorator-based (won't work with FktDialog)
@Component({ /* ... */})
export class OldComponent {
    @Input() title: string = '';
    @Input() data?: MyData;
    @Input() config: Config = {theme: 'light'};
    @Output() save = new EventEmitter<FormData>();
    @Output() cancel = new EventEmitter<void>();
}

// After: Signal-based (works perfectly with FktDialog)
@Component({ /* ... */})
export class NewComponent {
    title = input('');
    data = input<MyData>();
    config = input<Config>({theme: 'light'});
    save = output<FormData>();
    cancel = output<void>();

    // Migration tip: Keep the same logic, just change the API
    handleSave() {
        const formData = this.buildFormData();
        this.save.emit(formData); // Same emit pattern
    }
}

Dialog Sizing Options

The dialog service provides flexible sizing throughpanelOptions:

Predefined Sizes

  • ** Small **: width: '300px' - Perfect for quick confirmations
  • ** Medium **: width: '500px' - Standard forms and content
  • ** Large **: width: '700px' - Detailed forms and complex content
  • ** Extra Large **: width: '900px' - Data tables and comprehensive interfaces
  • ** Full Screen **: width: '90vw', height: '80vh' - Immersive experiences

Responsive Sizing

panelOptions: {
    width: 'min(90vw, 600px)',  // Responsive width
        maxHeight
:
    '80vh',          // Constrain height
        height
:
    'fit-content'       // Adapt to content
}

Advanced Features & Tips

Reactive Data Binding

While not required, signals enable powerful reactive patterns:

// Option 1: Static data (simple and fine for most cases)
data: {
    title: 'Static Title',
        message
:
    'This won\'t change after dialog opens'
}

// Option 2: Reactive data (when you need live updates)
data: {
    title: this.dynamicTitle,      // Signal that can update
        message
:
    this.liveMessage,     // Updates reflect in dialog
        counter
:
    this.sharedCounter    // Two-way binding with models
}

Multiple Dialog Management

class MyComponent {
    private activeDialogs = new Map<string, FktDialogRef<any>>();

    openNamedDialog(id: string, config: any) {
        // Close existing dialog with same ID
        this.closeDialog(id);

        const dialogRef = this.dialogService.open(config);
        this.activeDialogs.set(id, dialogRef);
        return dialogRef;
    }

    closeDialog(id: string) {
        const dialog = this.activeDialogs.get(id);
        if (dialog) {
            dialog.close();
            this.activeDialogs.delete(id);
        }
    }

    closeAllDialogs() {
        this.activeDialogs.forEach(dialog => dialog.close());
        this.activeDialogs.clear();
    }
}

Custom Backdrop Behavior

panelOptions: {
    backdropClick: () => {
        if (this.hasUnsavedChanges()) {
            if (confirm('Discard changes?')) {
                dialogRef.close();
            } else {
                dialogRef.close();
            }
        }
    }
}

Best Practices

Component Design

  • ** Use signal - based APIs **: Required for TypeScript inference and reactivity
  • ** Keep components focused **: Each dialog component should have a single responsibility
  • ** Design for reusability **: Signal - based components work in dialogs, overlays, and standalone
  • ** Handle edge cases **: Consider what happens when users navigate away or refresh

Data Management

  • ** Static vs Reactive **: Use static data for simple dialogs, reactive for live updates
  • ** Callback functions **: Handle dialog outputs by passing callback functions in data
  • ** State management **: Consider using signals for shared state across dialogs
  • ** Validation **: Implement proper form validation and user feedback

Performance & Memory

  • ** Close properly **: Always call close() to prevent memory leaks
  • ** Limit concurrent dialogs **: Avoid opening too many dialogs simultaneously
  • ** Optimize templates **: Keep dialog templates lightweight and efficient
  • ** Lazy loading **: Consider loading heavy content only when needed

User Experience

  • ** Appropriate sizing **: Choose sizes that fit content without overwhelming users
  • ** Clear actions **: Make primary and secondary actions obvious
  • ** Mobile friendly **: Test dialogs on various screen sizes
  • ** Keyboard navigation **: Ensure dialogs are accessible via keyboard
  • ** Focus management **: Handle focus properly when opening and closing

Accessibility

  • ** Focus Management **: Focus is automatically managed when dialogs open and close
  • ** Keyboard Support **: ESC key closes dialogs, Tab navigation works within dialog content
  • ** ARIA Attributes **: Dialogs include proper ARIA roles and labels automatically
  • ** Screen Reader Support **: Dialog content is properly announced to screen readers
  • ** Backdrop Interaction **: Clicking outside the dialog provides expected behavior

Performance

  • ** Efficient Rendering **: Dialogs use efficient DOM management and minimal re - renders
  • ** Memory Safe **: Components are properly destroyed when dialogs close
  • ** Position Optimization **: Dialog positioning calculations are optimized for performance
  • ** Lazy Destruction **: Dialog elements are removed from DOM after closing animation

Common Use Cases

Quick Actions

  • ** Confirmations **: Delete actions, save confirmations, navigation warnings
  • ** Input Collection **: Single field inputs, quick settings, preferences
  • ** Status Updates **: Success messages, error notifications, progress updates

Forms & Data Entry

  • ** User Registration **: Account creation, profile setup, preferences
  • ** Data Collection **: Surveys, feedback forms, contact information
  • ** Content Creation **: Post creation, comment forms, media uploads

Content Display

  • ** Details View **: User profiles, item details, expanded information
  • ** Media Preview **: Image galleries, video players, document viewers
  • ** Help & Documentation **: Tutorials, feature explanations, guidelines

System Operations

  • ** Settings Management **: Application preferences, user settings, system configuration
  • ** Data Management **: Import /export operations, data processing, bulk actions
  • ** Administrative Tasks **: User management, system monitoring, configuration