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