Angular Modal Component | ng-hub-ui-modal

Accessible Angular modal component with stacking, fullscreen mode, templates, keyboard handling and CSS variables for standalone apps.

API reference

Here's the full contract for modal: everything you can bind, listen to, project and theme, gathered in one place. Wire up what you need and style what you want — it's standalone and signal-friendly.

Inputs

Dial modal in with 27 inputs. Bind them like any Angular @Input.

NameTypeDefaultDescription
animationboolean—If true, modal opening and closing will be animated. Default: true
ariaLabelledBystring—ID of the element that labels the dialog. Applied as `aria-labelledby` on the modal window.
ariaDescribedBystring—ID of the element that describes the dialog. Applied as `aria-describedby` on the modal window.
closeAriaLabelstring'Close'Accessible name of the dismiss button the library draws in its own header. That button carries no text — its glyph is painted by CSS — so this string is the whole of what a screen reader announces: pass the translated string here, or set the default once on `HubModalConfig`. Default: "Close"
backdropboolean | "static"—If true, the backdrop element will be created. Alternatively, specify "static" for a backdrop which doesn't close the modal on click. Default: true
beforeDismiss() => boolean | Promise<boolean>—Guard called right before the modal is dismissed. Return `false`, a promise resolved with `false`, or a rejected promise, and the dialog stays open.
centeredboolean—If true, the modal will be centered vertically. Default: false
closeOnNavigationbooleantrueDismisses the dialog when the application's URL changes, with `ModalDismissReasons.NAVIGATION`. A dialog belongs to the screen that opened it: left open it covers the next one, and its backdrop swallows every click there. A change of path or query string counts; a change of anchor does not, since it moves the reader inside the same screen. Set it to `false` for a dialog that navigates on its own and has to stay open. Default `true`.
closeButtonbooleantrueWhether the dialog draws the dismiss button in its own header. `false` leaves it out of the DOM rather than hiding it, which is what a dialog with no free way out needs: a button hidden with CSS is still announced by a screen reader and still reached by the tab order. It governs that button alone — `backdrop: 'static'` and `keyboard: false` are the other two ways out. Default `true`.
placementHubModalPlacement—Controls where the modal is placed within the viewport. Default: HubModalPlacement.Center
offcanvasbooleanfalseOpens the dialog as a drawer: flush against the edge named by `placement`, square on that side, stretched to the full height (or width, from the top or bottom), and scrolling in the body so the header and footer stay put. Its width comes from `--hub-modal-offcanvas-width`, not from the size scale — `size: 'lg'` is 800px, which on a narrow window covers the document the drawer is meant to be read against. With no `placement` it opens from the end edge, so a drawer is one decision. Separate from `placement` on purpose: an edge placement alone still gives the floating dialog it always gave.
containerstring | HTMLElement—CSS selector or element the modal windows are appended to. Default: `body`
fullscreenboolean | "sm" | "md" | "lg" | "xl" | "xxl" | string—`true` keeps the dialog fullscreen at every width; a breakpoint name such as `"md"` only below that width. Any other string applies `modal-fullscreen-<value>-down`. Default: false
injectorInjector—Injector used to resolve the dependencies of the content component.
keyboardboolean—If true, the modal will be closed when Escape key is pressed. Default: true
scrollableboolean—Pins the dialog and scrolls its body, instead of letting the dialog grow past the viewport. Default: false
size"sm" | "lg" | "xl" | string—Size of the modal window
variant"primary" | "success" | "danger" | "warning" | "info" | string—Semantic accent of the dialog, for one that means something — a destructive confirmation, a success notice. It re-bases the single `--hub-modal-accent` slot, and the emphasis / subtle / on / border roles derive from it on the dialog itself, so one value recolours the tinted background, the outer edge with its header and footer rules, and the title. The stylesheet ships nine: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `neutral`, `light` and `dark`. Any other string is accepted as well and applies `hub-modal--<name>` to the window, so a custom accent costs one rule in a global stylesheet and no recompilation. A top accent bar belongs to the set too, but ships at zero width: set `--hub-modal-accent-bar-width` on the dialog to turn it on. Without a variant the dialog stays neutral.
windowClassstring—A custom class to append to the modal window
modalDialogClassstring—A custom class to append to the `hub-modal__dialog` element
backdropClassstring—A custom class to append to the modal backdrop
dismissSelectorstring—Custom selector for elements that can trigger the dismissal of the modal window. Default: "[data-dismiss=\"modal\"]"
closeSelectorstring—Custom selector for elements that can trigger the closing of the modal window. Default: "[data-close=\"modal\"]"
headerSelectorstring—Custom selector for the header element of the modal window
footerSelectorstring—Custom selector for the footer element of the modal window
bodySelectorstring—CSS selector for the block whose children become the body. Without it the body is whatever the header and the footer left behind, which depends on the order the content is written in; with it the body is placed deliberately, and anything no slot claimed still follows it.
dataD—Additional data that needs to be passed to the modal window when it is opened

Outputs

React to what modal does — 1 events to hook your logic onto.

NameTypeDescription
dismissanyEmitted by the modal window when the user dismisses the modal, e.g. by pressing Escape, clicking the backdrop, or activating the built-in close button. The payload is the dismiss reason (ModalDismissReasons.ESC, ModalDismissReasons.BACKDROP_CLICK, or a custom value).

Methods

Drive modal from code — 17 methods on its programmatic surface.

NameSignatureReturnsDescription
HubModal.openopen<C, R, D>(content: Type<C> | TemplateRef<any> | string, options?: HubModalOptions<D>): HubModalRef<C, R>HubModalRef<C, R>Opens a new modal window with the given content (component type, TemplateRef, or string) and options, and returns a HubModalRef to control it. Content components can inject HubActiveModal and the HUB_MODAL_DATA token to interact with the modal and read the data payload.
HubModal.dismissAlldismissAll(reason?: any): void—Dismisses all currently displayed modal windows with the supplied reason.
HubModal.hasOpenModalshasOpenModals(): booleanbooleanIndicates if there are currently any open modal windows in the application.
HubModal.activeInstancesactiveInstances: EventEmitter<HubModalRef[]>EventEmitter<HubModalRef[]>Event emitter that emits the list of currently open HubModalRef instances every time a modal is opened or removed.
HubModalRef.closeclose(result?: R): void—Closes the modal with an optional result value. The HubModalRef.result promise is resolved with the provided value.
HubModalRef.dismissdismiss(reason?: any): void—Dismisses the modal with an optional reason value. The HubModalRef.result promise is rejected with the provided value. Honours the beforeDismiss guard when one was configured.
HubModalRef.updateupdate(options: HubModalUpdatableOptions): void—Updates options of an opened modal (aria attributes, centered, placement, fullscreen, backdropClass, size, variant, windowClass, modalDialogClass).
HubModalRef.componentInstancecomponentInstance: C | voidC | voidThe instance of the component used as modal content. Returns undefined when a TemplateRef was used as content or when the modal is closed.
HubModalRef.resultresult: Promise<R>Promise<R>Promise resolved with the close result when the modal is closed and rejected with the dismiss reason when it is dismissed.
HubModalRef.closedclosed: Observable<R>Observable<R>Observable that emits the result passed to close() when the modal is closed.
HubModalRef.dismisseddismissed: Observable<any>Observable<any>Observable that emits the dismiss reason when the modal is dismissed — the value passed to dismiss(), or an internal reason such as backdrop click or Escape.
HubModalRef.shownshown: Observable<void>Observable<void>Observable that emits and completes once the modal is fully visible and its opening animation has finished.
HubModalRef.hiddenhidden: Observable<void>Observable<void>Observable that emits and completes once both the modal window and the backdrop are hidden, animations finished, and their elements removed from the DOM.
HubActiveModal.closeclose(result?: R): void—Closes the modal hosting the content component, resolving the HubModalRef.result promise with the optional result value.
HubActiveModal.dismissdismiss(reason?: any): void—Dismisses the modal hosting the content component, rejecting the HubModalRef.result promise with the optional reason value.
HubActiveModal.updateupdate(options: HubModalUpdatableOptions): void—Updates options of the opened modal from inside the content component.
HubActiveModal.datadata: DDThe typed payload passed through HubModalOptions.data. Equivalent to injecting the HUB_MODAL_DATA token; resolves to null when no data option was provided.

Templates

Make it yours — 1 template slots let you project custom markup.

NameDescriptionExample
Modal TemplateTemplate reference that can be passed to the modal service. The header, body and footer blocks become slots only when `headerSelector`, `bodySelector` and `footerSelector` are given on `open()`; without them the whole template lands in the body.<ng-template #modalTemplate let-close="close" let-dismiss="dismiss"> <div hubModalHeader> <h4 class="hub-modal__title">Modal Title</h4> </div> <div hubModalBody> Modal content goes here </div> <div hubModalFooter> <button hubButton color="secondary" (click)="dismiss()">Cancel</button> <button hubButton color="primary" (click)="close('result')">OK</button> </div> </ng-template> <!-- The three blocks become slots only when the selectors are given: this.modal.open(this.modalTemplate, { headerSelector: '[hubModalHeader]', bodySelector: '[hubModalBody]', footerSelector: '[hubModalFooter]' }); Without them the whole template lands in hub-modal__body. -->