Skip to content

Surfaces

ModalService

Bit.BlazorUI

BitModalService shows a BitModal with any content - a component, a Type or markup - from anywhere in the app: a service, a view model, a handler. Each Show hands back a BitModalReference that closes the modal, completes with its answer and says whether the user dismissed it. A BitModalParameters customizes the modal around the content with the same options as BitModal, plus a close guard and a navigation policy.

Notes

Mount one BitModalContainer in the layout, in the same interactive render mode as the components that show modals - a modal shown with no container is not rendered, and the service logs it. Register the service Scoped on Blazor Server; a singleton is only safe on WebAssembly and Hybrid. For a ready-made alert or confirmation, BitMessageBoxService (Extras) is built on this service.

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

Show<T> renders the component as the content; the content closes its own modal through the cascaded BitModalReference. BitModalParameters adds the chrome: HeaderText also names the dialog for screen readers - without it, give the modal an AriaLabel.

Markup as content

Show also takes a RenderFragment, for content not worth a component of its own, or a RenderFragment<BitModalReference>, built from the modal's own reference so the markup can close it. The reference's Content stays null, since markup is no component instance.

Awaiting a result

CloseWith answers the modal, and Result / GetResult<T> complete with that answer. A dismissal answers null (the type's default), so IsDismissed tells "walked away" from "answered with nothing". BitModalContentParameters<T> passes the content's parameters checked by the compiler. For a question, IsAlert announces an alertdialog and AutoFocus starts on the least destructive answer.

Answer: [-]

Reaching the content

The content is created when the container renders, after Show returns. GetContentAsync<T> waits for Rendered and hands the instance back. The parameters factory overload goes the other way: it receives the reference before the content is built, so a content parameter can use it - a callback that closes this very modal, or its id.

The content reported: [-]

Guarding the close

CanClose is asked before the user closes the modal (close button, overlay, Escape) and by TryClose; answering false keeps it open. Close, CloseWith, CloseAll and a close on navigation are the app's own and skip it. Type in the field, then try to close.

Last attempt: [-]

Updating an open modal

Update changes an open modal: a lambda changes only what it names - so the content, which never saw its caller's set, can retitle its own modal - and a new set replaces the whole one. After mutating a parameters object in place, call Refresh.

Persistent modals

A persistent modal outlives its container: the service keeps it and hands it to the next container that mounts - even one mounting after the modal was shown. Any other modal closes, with a null result, when its container goes. Show one of each, then unmount the container. (The container's Service keeps this example's modals apart from the rest of the page's.)

Container: [mounted]
Persistent modal: [never shown]
Ordinary modal: [never shown]

Closing on navigation

Modals close when the path changes - a query string or fragment does not count. CloseOnNavigation = false keeps one open across routes (a sign-in prompt, a running upload); set it on the container's ModalParameters for all of them.


Modal: [never shown]
Modal that stays: [never shown]

Tracking open modals

OpenModals is a snapshot of the open modals, GetModal finds one by id and CloseAll closes them all (a sign-out). OnAddModal / OnCloseModal report every modal shown and closed, whoever showed it. IsContainerAvailable says whether a Show would render now.

Open: [0]   Shown: [0]   Closed: [0]   Container mounted: [True]

Cascading parameters

A BitParams around the container hands a BitModalParams to every modal it renders, and to every BitModal in markup below it. They are defaults: the container's ModalParameters and each showing's own parameters win over them.

Custom container

A container of your own derives from BitModalContainer - which keeps its Service, the merging of the parameters and CloseOnNavigation - and writes only its markup, rendering the modals through RenderModals: all of them, or each inside markup of its own through a template that renders the modal it is handed (item.Modal). Either one keys each modal by its Id and hands it what it expects from its container, including what lets it play its exit animation: a container that cascades the reference and the parameters by hand takes a closed modal out of the page at once.

Open in this container: [0]

External Icons

CloseIcon takes the close button's icon from an external library (FontAwesome, Bootstrap Icons, ...) and wins over CloseIconName, a built-in Fluent UI icon.

Style & Class

Style and Class reach the root of the modal; Styles and Classes reach each part. The public --bit-Modal-* variables (see the BitModal page) inherit, so a Style sets them for one modal, and the container's ModalParameters for all of them.

RTL

Dir = BitDir.Rtl lays the modal out right-to-left, chrome included; Start/End positions follow along.

API

Every parameter, public member, sub-class and enum this component exposes.

BitModalService parameters

Name Type Default value Description
OnAddModal event Func<BitModalReference, Task>? Raised for every modal shown through this service, whoever showed it.
OnCloseModal event Func<BitModalReference, Task>? Raised once for every modal of this service that closes, however it closed: answered or closed by the application, dismissed by the user, closed by a navigation or by its container going away.
IsContainerAvailable bool Whether a BitModalContainer is currently mounted for this service, i.e. whether a Show call right now would actually render its modal. It reflects live state rather than registration in DI.
OpenModals IReadOnlyList<BitModalReference> A snapshot of the modals this service currently has open, in the order they were opened. It holds what the mounted container renders, plus the persistent modals that are still waiting for a container to mount.
GetModal BitModalReference? (string? id) The open modal with the given id, or null when there is none - it was closed, or the id belongs to another service.
Close Task (BitModalReference modal) Closes an already opened modal using its reference, with a null result. This is the application closing the modal, so the CanClose guard is not asked.
Close Task (BitModalReference modal, object? result) Closes an already opened modal using its reference, with the result its Result task completes with. The CanClose guard is not asked.
TryClose Task<bool> (BitModalReference modal, object? result) Asks a modal to close and reports whether it did: a modal whose CanClose guard turns the close down stays open and this answers false.
CloseAll Task Closes every modal this service currently has open, each with a null result, the last one opened first - so each hands the focus back to the modal under it, and the last to the page. The CanClose guards are not asked.
Refresh Task (BitModalReference? modal) Re-renders the open modals, invalidating their memoized merged parameters. Call it after mutating modal parameters in place, which doesn't change any object reference and is therefore not detected on its own. Without an argument it refreshes every open modal.
Show Task<BitModalReference> (Dictionary<string, object>? parameters) Shows a new BitModal with a custom component with parameters as its content.
Show Task<BitModalReference> (BitModalParameters? modalParameters) Shows a new BitModal with a custom component as its content with custom parameters for the modal.
Show Task<BitModalReference> (Dictionary<string, object>? parameters, BitModalParameters? modalParameters, bool persistent) Shows a new BitModal with a custom component as its content with custom parameters for the custom component and the modal. A persistent modal survives a container remount and is injected into the next container that mounts.
Show Task<BitModalReference> (Type componentType, Dictionary<string, object>? parameters, BitModalParameters? modalParameters, bool persistent) Shows a new BitModal with a component whose type is only known at run time as its content, for the callers that pick their content from a map or a route. Throws an ArgumentException for a type that is not a Blazor component.
Show Task<BitModalReference> (RenderFragment content, BitModalParameters? modalParameters, bool persistent) Shows a new BitModal with the given markup as its content, for the content that is not worth a component of its own. The reference's Content stays null for such a modal, since markup is not a component instance.
Show Task<BitModalReference> (RenderFragment<BitModalReference> content, BitModalParameters? modalParameters, bool persistent) Shows a new BitModal with markup built from the modal's own reference as its content, so the markup can close and answer the modal it is in: modal => @<BitButton OnClick="modal.Close">Got it</BitButton>.
Show Task<BitModalReference> (Func<BitModalReference, Dictionary<string, object>?> parametersFactory, BitModalParameters? modalParameters, bool persistent) Shows a new BitModal, building the content component's parameters from a factory that receives the modal reference. Use this overload when a parameter needs the reference itself, such as an OnClose callback that closes this very modal.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.