JS Interop
How BlazOrbit splits JavaScript into per-feature interop services, and how components opt into behaviors through IJsBehavior interfaces.
Topic-specific interop services
Rather than a single monolithic JS module, interop is split by feature. Each interface
lives in BlazOrbit.Services.JsInterop and has a matching TypeScript
module bundled by Vite into wwwroot/js/.
IThemeJsInterop— reads / writesdata-themeon<html>and persists tolocalStorage.IBehaviorJsInterop— applies lightweight DOM behaviors (e.g. ripples).IPatternJsInterop— date / time / number input patterns.IDropdownJsInterop— positioning + outside-click handling for dropdowns and tooltips.IClipboardJsInterop— copy-to-clipboard.ITextAreaJsInterop— auto-grow textarea sizing.IDraggableJsInterop— pointer capture forBOBDraggable.IColorPickerJsInterop— color picker interactions.IModalJsInterop— scroll lock + focus trap for dialogs and drawers.
All of them are registered as Scoped by AddBlazOrbit() —
you don't need to touch them unless you're building a custom component that reuses one.
The IHas*Behavior pipeline
Some capabilities go beyond CSS — a ripple effect, say, needs a click-time DOM tweak.
Those are expressed as interfaces deriving from IJsBehavior:
IHasRipple, IHasAutoGrow, and so on.
During OnAfterRenderAsync(firstRender), BOBComponentBase walks
the implemented behavior interfaces and runs BOBComponentJsBehaviorBuilder to
invoke the matching JS hook on the rendered element. You get automatic cleanup on dispose.
public partial class MyCustom : BOBComponentBase, IHasRipple
{
[Parameter] public bool DisableRipple { get; set; }
[Parameter] public string? RippleColor { get; set; }
[Parameter] public int? RippleDuration { get; set; }
}
// Renders: --bob-inline-ripple-color / --bob-inline-ripple-duration.
// BOBComponentJsBehaviorBuilder reads IHasRipple state directly and wires up
// the pointer listener — no DOM data-attribute is needed.Directly calling an interop from a component
If you need imperative JS (e.g. to copy to clipboard, focus an input, or toggle a theme), inject the relevant interop:
@inject IClipboardJsInterop Clipboard
@inject IThemeJsInterop Themes
<BOBButton Text="Copy" OnClick="OnCopy" />
<BOBButton Text="Toggle theme" OnClick="OnToggle" />
@code {
private Task OnCopy() =>
Clipboard.CopyTextAsync("Copied from a custom component!");
private Task<string> OnToggle() =>
Themes.ToggleThemeAsync(new[] { "light", "dark" });
}Where the JS lives
TypeScript sources live in src/BlazOrbit/Types/<Feature>/ and are
bundled per feature into wwwroot/js/Types/<Feature>/<Feature>Interop.min.js
by the build pipeline (see the Getting started page for how
that is wired up). The generated JS is referenced by each interop C# class via
IJSRuntime.InvokeAsync.
You don't ship the JS bundles yourself — they're part of the BlazOrbit
NuGet's static web assets and are served under _content/BlazOrbit/js/….
Building a custom component with JS
- Write a TypeScript module and arrange for it to ship with your app (or a feature package).
- Expose an interop service with an interface +
InvokeAsync-based implementation, and register it as scoped. - Inject the service into your component and call it from
OnAfterRenderAsync/ event handlers. - If the behavior should auto-wire to a marker interface, derive an interface from
IJsBehaviorand wire it intoBOBComponentJsBehaviorBuilder.