Dialog

The Aurelia dialog plugin - a modular system bridging Aurelia with various UI framework dialog implementations.

Introduction

The Aurelia dialog plugin is a modular and pluggable system that serves as a bridge between Aurelia and different UI framework dialog implementations. Rather than being a single dialog implementation, it provides an extensible architecture where different renderers can be plugged in to support various dialog styles and behaviors.

The plugin comes with two built-in renderer implementations:

  • Standard: Uses the modern HTML5 <dialog> element

  • Classic: A light DOM implementation similar to Aurelia v1 (ideal for migration)

The modular design makes it easy to create custom renderers for specific UI frameworks or design requirements.

Need practical walkthroughs? See the dialog outcome recipes for confirmations, wizards, and guarded modals.

Plugin Architecture

The dialog plugin is built around a modular renderer system that bridges Aurelia with any UI framework:

Core Interfaces

Interface
Purpose
Implementation

IDialogService

Main API for opening/managing dialogs

Single service per app

IDialogController

Controls individual dialog instances

One per dialog

IDialogDomRenderer<TOptions>

Pluggable renderer interface

Standard, Classic, or Custom

IDialogDom

Dialog DOM abstraction

Renderer-specific implementation

Installing The Plugin

Aurelia provides three configuration options for the dialog plugin:

Uses the modern HTML5 <dialog> element with native modal behavior:

Best for: New applications, modern browsers, native accessibility features

Option 2: Classic Configuration (Migration-Friendly)

Uses a light DOM implementation similar to Aurelia v1:

Best for: Migrating from Aurelia v1, custom styling requirements, older browser support

Option 3: Base Configuration (Custom Renderer)

Provides the core infrastructure without a default renderer:

Best for: Custom UI framework integration, specific design requirements

Choosing the Right Configuration

Use this guide to select the best dialog configuration for your project:

Factor
Standard
Classic
Custom

Browser Support

Modern (dialog support)

All browsers

Depends on implementation

Accessibility

Built-in

Manual setup

Manual setup

Styling Control

Limited backdrop

Full control

Full control

Animation Support

Via callbacks

Via callbacks

Full control

Migration from v1

Requires changes

Minimal changes

Complete rewrite

UI Framework Integration

Limited

Some styling

Perfect integration

Global Configuration

Each configuration can be customized to set global defaults for all dialogs. Call .customize() on your chosen configuration:

Configure global defaults for the Standard renderer:

If it's desirable to change some of the default implementations, we can instead use the export named DialogConfiguration and pass in the list of implementation for the main interfaces:

Using The Default Implementations

The Dialog Settings

There are two levels where dialog behaviors can be configured:

  • Global level via IDialogGlobalSettings

  • Single dialog level via dialog service .open() call, or the property settings on a dialog controller.

Normally, the global settings would be changed during the app startup/or before, while the single dialog settings would be changed during the contruction of the dialog view model, via the open method.

  1. An example of configuring the global dialog settings:

    • For the standard implementation: Make all dialogs, by default:

      • show as modal

      • has some basic animation in & out

    • For the classic implementation: Make all dialogs, by default:

      • not dismissable by clicking outside of it, or hitting the ESC key

      • have starting CSS z-index of 5

      • if not locked, closable by hitting the ESC key

  2. An example of configuring a single dialog, via open method of the dialog service:

    • For the standard implementation: Displaying an alert dialog

    • For the classic implementation: Displaying an alert dialog, which has z-index value as 10 to stay on top of all other dialogs, and will be dismissed when the user hits the ESC key.

The main settings that are available in the open method of the dialog service:

  • component can be class reference or instance, or a function that resolves to such, or a promise of such.

  • template can be HTML elements, string or a function that resolves to such, or a promise of such.

  • model the data to be passed to the canActivate and activate methods of the view model if implemented.

  • host allows providing the element which will parent the dialog - if not provided the document body will be used.

  • renderer allows providing a custom renderer to be used, instead of the pre-registered one. This allows a single dialog service to be able to use multiple renderers for different purposes: default for modals, and a different one for notifications, alert etc...

  • container allows specifying the DI Container instance to be used for the dialog. If not provided a new child container will be created from the root one.

  • rejectOnCancel is a boolean that must be set to true if cancellations should be treated as rejection. The reason will be an IDialogCancelError - the property wasCancelled will be set to true and if cancellation data was provided it will be set to the value property.

  • options options passed to the renderer.

Renderer Options Reference

Each renderer supports different configuration options. Here are the complete option sets:

Standard Renderer Options (DialogRenderOptionsStandard)

Option Details:

  • modal: Controls whether the dialog opens as modal (showModal()) or modeless (show()). Modal dialogs block interaction with the rest of the page and display a backdrop.

  • overlayStyle: CSS styling for the ::backdrop pseudo-element (only applies when modal: true). Can be a CSS string or style object.

  • show: Animation callback called when dialog is shown. Return a Promise to wait for animations.

  • hide: Animation callback called when dialog is hidden. Return a Promise to wait for animations.

  • closedby: HTML5 dialog attribute controlling which user actions can close the dialog. See MDN documentation.

Default Global Settings:

Classic Renderer Options (DialogRenderOptionsClassic)

Option Details:

  • lock: When true, prevents dialog dismissal via ESC key or overlay clicks. Takes precedence over keyboard and overlayDismiss.

  • keyboard: Array of keys that close the dialog. 'Escape' cancels, 'Enter' confirms. When lock: false, defaults to ['Enter', 'Escape']. When lock: true, defaults to [].

  • mouseEvent: Which mouse event type triggers overlay dismiss logic.

  • overlayDismiss: When true, clicking outside the dialog cancels it. Defaults to !lock value.

  • startingZIndex: Base z-index for dialog wrapper and overlay elements.

  • show/hide: Animation callbacks with access to DialogDomClassic instance.

Default Global Settings:

DOM Structure Comparison

Uses the native HTML5 <dialog> element:

Characteristics:

  • Native modal behavior with showModal()

  • Built-in ESC key handling

  • Native ::backdrop pseudo-element

  • Accessibility features built-in

  • Limited styling control of backdrop

Service & Controller APIs

IDialogService Interface

IDialogController Interface

DialogOpenPromise Interface

The open() method returns a special promise with additional methods:

Dialog Settings Interface

Complete settings interface with all available options:

An important feature of the dialog plugin is that it is possible to resolve and close a dialog (using cancel/ok/error methods on the controller) in the same context where it's open.

  • Example of controlling the opening and closing of a dialog in promise style:

  • Example of controlling the opening and closing of a dialog using async/await:

By default, when an application is destroyed, the dialog service of that application will also try to close all the open dialogs that are registered with it via closeAll method. It can also be used whenever there's a need to forcefully close all open dialogs, as per following example:

Given an error list, open a dialog for each error, and close all of them after 5 seconds.

If there's no need for the opening result of a dialog, and only the response of it after the dialog has been closed, there is a whenClosed method exposed on the returned promise of the open method of the dialog service, that should help reduce some boilerplate code, per following example:

Template Only Dialogs

The dialog service supports rendering dialogs with only template specified. A template only dialog can be open like the following examples:

Retrieving the dialog controller

By default, the dialog controller of a dialog will be assigned automatically to the property $dialog on the component view model. To specify this in TypeScript, the component class can implement the interface IDialogCustomElementViewModel:

If it's desirable to retrieve the associated dialog controller of a dialog during the constructor of the component, IDialogController can be inject to achieve the same effect:

This means it's also possible to control the dialog from template only dialog via the $dialog property. An example of this is: Open an alert dialog, and display an "Ok" button to close it, without using any component:

The Default Dialog Renderer

By default, the dialog DOM structure is rendered as follow:

The Dialog host element is the target where an application chooses to add the dialog to, this is normally the document body, if not supplied in the settings of the open method of the dialog service.

An example of the html structure when document body is the dialog host:

Centering/Uncentering dialog position

By default, the dialog content host is centered horizontally and vertically. It can be changed via IDialogDom injection:

Note that the contentHost property on a DefaultDialogDom object is the same with the host element of a component. You can inject IDialogDom and retrieve the host element via contentHost property, or inject INode/Element/HTMLElement to retrieve it.

Styling the overlay

  1. For the standard implementation

    The overlay of a <dialog> element is only "rendered" when it's shown as modal. Which means the following example will not "trigger" any overlay:

    as options.modal value is not true.

    When options.modal is true, the overlay can be specified using options.overlayStyle like the following example:

    Note that the overlay of a modal dialog dom can also be configured from the dialog dom itself, like the following example:

  2. For the classic implementation

    By default, the overlay of a dialog is transparent. Though it's often desirable to add 50% opacity and a background color of black to the modal. To achieve this in dialog, retrieve the IDialogDom instance and modify the overlay element style:

Creating Custom Dialog Renderers

The dialog plugin's modular architecture makes it easy to create custom renderers for specific UI frameworks, design systems, or unique requirements. A renderer is responsible for creating the DOM structure and managing the dialog's presentation.

Understanding the Renderer Interface

All dialog renderers must implement the IDialogDomRenderer<TOptions> interface:

The renderer's render method returns an IDialogDom object that provides the dialog's DOM structure:

Note: The IDialogDom interface only requires contentHost. The Standard and Classic implementations extend this with their own properties (root, overlay, etc.).

Example: Creating a Material Design Renderer

Let's create a custom renderer that follows Material Design principles:

Registering Your Custom Renderer

There are several ways to use your custom renderer:

1. As the Default Renderer

Replace the default renderer globally:

2. Per-Dialog Renderer

Use a specific renderer for individual dialogs:

3. Multiple Renderer Support

Register multiple renderers and choose them based on dialog type:

Best Practices for Custom Renderers

  1. TypeScript Support: Define strong types for your renderer options

  2. Accessibility: Ensure proper ARIA attributes and focus management

  3. Animation: Use CSS animations or Web Animations API for smooth transitions

  4. Responsive Design: Handle different screen sizes appropriately

  5. Cleanup: Always implement proper disposal in the dispose() method

  6. Event Handling: Handle keyboard and mouse events according to your design system

  7. Consistent API: Keep the same patterns as built-in renderers for familiarity

This modular approach allows you to integrate any UI framework (Bootstrap, Ant Design, Chakra UI, etc.) or create completely custom dialog behaviors while leveraging Aurelia's powerful dialog lifecycle management.

Error Handling & Promise Rejection

The dialog system includes comprehensive error handling for different scenarios:

DialogCancelError vs DialogCloseError

When rejectOnCancel: true is set, cancellations and errors result in different error types:

Example: Handling Different Outcomes

Using whenClosed for Non-Rejecting Pattern

If you prefer not to use try/catch, use whenClosed():

Animation

Using the IDialogDomAnimator

If you use either the default implementations of the interface IDialogRenderer, then the in/out animations of a dialog can be configured via show/hide the options settings, like the following examples:

  1. Global animations for all dialogs:

  1. Different animation per dialog .open()

Using component lifecycles

The lifecycles attaching and detaching can be used to animate a dialog, as in those lifecycles, if a promise is returned, it will be awaited during the activation/deactivation phases.

An example of animating a dialog on attaching and detaching, with the animation duration of 200 milliseconds:

Component Lifecycles With The Dialog Plugin

In adition to the lifecycle hooks defined in the core templating, the dialog defines additional ones. All dialog specific hooks can return a Promise, that resolves to the appropriate value for the hook, and will be awaited.

.canActivate()

This hook can be used to cancel the opening of a dialog. It is invoked with one parameter - the value of the model setting passed to .open(). To cancel the opening of the dialog return false - null and undefined will be coerced to true.

.activate()

This hook can be used to do any necessary init work. The hook is invoked with one parameter - the value of the model setting passed to .open().

.canDeactivate(result: DialogCloseResult)

This hook can be used to cancel the closing of a dialog. To do so return false - null and undefined will be coerced to true. The passed in result parameter has a property status, indicating if the dialog was closed or cancelled, or the deactivation process itself has been aborted, and an value property with the dialog result which can be manipulated before dialog deactivation.

The DialogCloseResult has the following interface:

Warning When the error method of a DialogController is called this hook will be skipped.

.deactivate(result: DialogCloseResult)

This hook can be used to do any clean up work. The hook is invoked with one result parameter that has a property status, indicating if the dialog was closed (Ok) or cancelled (Cancel), and an value property with the dialog result.

Lifecycle Execution Order

Each dialog instance goes through the complete lifecycle once:

Lifecycle Hooks:

  • Dialog-specific: canActivate, activate, canDeactivate, deactivate

  • Aurelia core: constructor, hydrating, hydrated, created, binding, bound, attaching, attached, detaching, unbinding

Creating child dialog

When there are different groups of dialog functionalities that are often repeated across an application, rather than calling open with different sets of settings and trying to manage it correctly, you can also use the child dialog feature.

To use the child dialog feature, you'll need to register the child dialog configuration and resolve the child dialog with some your specific keys, like the following example:

and then later it can be used:

Beside the above usage, a child dialog service can also be created using the createChild method on the default implementation of DialogService, like the following example:

Aurelia v1 to v2 Migration

The Aurelia v1 dialog implementation is available as the Classic configuration in v2, making migration straightforward.

Quick Migration Steps

  1. Change your registration:

  2. Update property names:

    v1 Property
    v2 Property
    Notes

    viewModel

    component

    Same functionality

    view

    template

    Same functionality

    controller

    $dialog

    Property on view model

    closeResult

    dialog.closed

    Now a promise on controller

  3. Move renderer options:

Breaking Changes Detail

DialogService.open() Changes:

DialogCloseResult Changes:

Complete Migration Example

Last updated

Was this helpful?