Angular — Advanced Provider Usage
Advanced
EmbedProviderComponentpatterns — route-based visibility, delay policies, group configuration, programmatic control, and best practices.
Table of Contents
- Quick Start
- EmbedProviderComponent — Props Reference
- embedButtonVisibilityConfig
- EmbedButtonComponent Props
- EmbedInitService
- Usage Patterns
- Best Practices
Quick Start
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { RouterModule } from '@angular/router';
import { EmbedInitService } from '@revrag-ai/embed-angular';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterModule],
template: `<router-outlet></router-outlet>`,
})
export class AppComponent implements OnInit {
constructor(private embedInit: EmbedInitService) {}
ngOnInit(): void {
this.embedInit.initialize('your_api_key');
}
}// app-shell.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';
import { EmbedProviderComponent } from '@revrag-ai/embed-angular';
@Component({
selector: 'app-shell',
standalone: true,
imports: [CommonModule, RouterModule, EmbedProviderComponent],
template: `
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="['/offers', '/help', '/support']"
matchMode="exact"
>
<router-outlet></router-outlet>
</revrag-embed-provider>
`,
})
export class AppShellComponent {
currentPath = '/';
constructor(private router: Router) {
this.router.events
.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
.subscribe((e) => (this.currentPath = e.urlAfterRedirects));
}
}Required steps:
- Call
EmbedInitService.initialize(apiKey)inAppComponent.ngOnInit(). - Wrap your layout with
<revrag-embed-provider>. - Keep
currentPathin sync with the Angular router. - Add the CSS to
angular.jsonstyles array.
EmbedProviderComponent — Props Reference
Selector: revrag-embed-provider
| Input | Type | Default | Description |
|---|---|---|---|
currentPath | string | undefined | undefined | Highest-priority path override. Keep in sync with the Angular router (or active tab). When omitted the widget is always hidden. |
includeScreens | string[] | [] | Paths where the embed button is visible. Empty = never shown via provider (use EmbedButtonComponent directly). |
matchMode | 'exact' | 'startsWith' | 'exact' | exact — path must match exactly. startsWith — path and all sub-routes (e.g. /help matches /help/faq). |
embedButtonDelayMs | number | 0 | Global delay (ms) before showing the button. Applies when no group config matches. |
embedButtonVisibilityConfig | EmbedButtonVisibilityConfig | undefined | undefined | Per-group config for delays and continuity. See below. |
embedButtonPosition | EmbedButtonPosition | undefined | undefined | Override the button's pixel position (bottom, right). |
widgetPositioning | 'fixed' | 'embedded' | 'fixed' | fixed — viewport-fixed. embedded — in normal document flow. |
widgetSide | 'left' | 'right' | undefined | undefined | Horizontal alignment of the widget. |
widgetBottomOffset | number | 0 | Extra pixels to raise the widget from the bottom. |
widgetClassName | string | '' | Extra CSS class applied to the widget container. |
Path detection
Unlike React's EmbedProvider (which auto-detects window.location.pathname), Angular's EmbedProviderComponent requires you to supply currentPath explicitly because Angular's router is service-based. Always keep it in sync with NavigationEnd events:
this.router.events
.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
.subscribe((e) => {
this.currentPath = e.urlAfterRedirects;
});embedButtonVisibilityConfig
Use this for per-route or per-group behavior — different delays, policies, and continuity settings.
import { EmbedButtonVisibilityConfig } from '@revrag-ai/embed-angular';
visibilityConfig: EmbedButtonVisibilityConfig = {
defaultDelayMs: 1500,
groups: [
{
id: 'perScreen',
screens: ['/offers'],
continuity: 'perScreen',
delayMs: 2000,
delayPolicy: 'perScreen',
},
{
id: 'oncePerGroup',
screens: ['/checkout', '/payment'],
continuity: 'perScreen',
delayMs: 2000,
delayPolicy: 'oncePerGroupEntry',
},
{
id: 'oncePerSession',
screens: ['/support'],
continuity: 'perScreen',
delayMs: 2000,
delayPolicy: 'oncePerAppSession',
},
],
};<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="allScreens"
[embedButtonVisibilityConfig]="visibilityConfig"
>
<router-outlet></router-outlet>
</revrag-embed-provider>EmbedButtonVisibilityConfig
| Field | Type | Description |
|---|---|---|
defaultDelayMs | number | Fallback delay when a group matches but has no delayMs. |
groups | EmbedButtonGroupConfig[] | Per-group configuration array. |
EmbedButtonGroupConfig
| Field | Type | Description |
|---|---|---|
id | string | Unique ID for the group (used for session/continuity logic). |
screens | string[] | Paths belonging to this group. |
continuity | 'perScreen' | 'continuous' | See EmbedButtonContinuity below. |
delayMs | number | Delay (ms) before showing the button when this group matches. |
delayPolicy | EmbedButtonDelayPolicy | When to apply the delay. See below. |
EmbedButtonContinuity
| Value | Behavior |
|---|---|
'perScreen' | Treat each screen in the group as a separate visit. Delays apply per screen according to delayPolicy. |
'continuous' | Once you enter the group, the button stays visible while navigating within the group. No delay on subsequent screens in the same group. |
EmbedButtonDelayPolicy
| Value | When delay applies |
|---|---|
'perScreen' | Every time you land on any screen in the group. |
'oncePerGroupEntry' | Only when first entering the group from outside. No delay when moving between screens within the group. |
'oncePerAppSession' | Only the first time you visit any screen in this group during the session. Page refresh resets. |
EmbedButtonComponent Props
Props for the standalone <revrag-embed-button> component (also forwarded internally by EmbedProviderComponent).
| Input | Type | Default | Description |
|---|---|---|---|
positioning | 'fixed' | 'embedded' | 'fixed' | fixed — viewport-fixed. embedded — in-flow. |
side | 'left' | 'right' | 'center' | undefined | undefined | Auto-position in bottom-left, bottom-right, or bottom-center. |
bottomOffset | number | 0 | Extra offset from bottom (e.g. for nav bars). |
position | PositionConfig | undefined | undefined | Fine-grained CSS position override. |
className | string | '' | Additional CSS class for the button container. |
PositionConfig
interface PositionConfig {
bottom?: string; // e.g. '24px'
right?: string; // e.g. '24px'
left?: string;
top?: string;
transform?: string;
zIndex?: number;
}Example
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="['/offers']"
widgetPositioning="fixed"
widgetSide="right"
[widgetBottomOffset]="80"
>
<router-outlet></router-outlet>
</revrag-embed-provider>EmbedInitService
Initializes the SDK. Call once in AppComponent.ngOnInit() before any widget renders.
import { EmbedInitService } from '@revrag-ai/embed-angular';
constructor(private embedInit: EmbedInitService) {}
// Initialize
await this.embedInit.initialize('your_api_key', {
baseUrl: 'https://custom.api.example.com', // optional
enableTracker: true, // optional
trackerCallback: (event) => { ... }, // optional
});SDKConfig options
| Field | Type | Description |
|---|---|---|
baseUrl | string | Override API base URL. |
enableTracker | boolean | Enable analytics/tracking. |
trackerCallback | (event: EventPayload) => void | Custom callback for tracking events. |
Return / Observables
| Observable / Getter | Type | Description |
|---|---|---|
isInitialized$ | Observable<boolean> | Emits true after successful init. |
isLoading$ | Observable<boolean> | Emits true while init is in progress. |
error$ | Observable<string | null> | Emits error message if init fails, null otherwise. |
sessionData$ | Observable<SessionStorageData> | Stored session/config for debugging. |
isInitialized | boolean (getter) | Synchronous check. |
Usage Patterns
Pattern 1 — Manual conditional rendering
Best for: apps where you want explicit, imperative control over when the widget appears.
Use *ngIf to conditionally render EmbedButtonComponent based on the current route.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';
import { EmbedButtonComponent } from '@revrag-ai/embed-angular';
@Component({
selector: 'app-shell',
standalone: true,
imports: [CommonModule, RouterModule, EmbedButtonComponent],
template: `
<nav>
<a routerLink="/">Home</a>
<a routerLink="/offers">Offers</a>
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>
<!-- Widget only on /offers -->
<revrag-embed-button
*ngIf="showEmbed"
positioning="fixed"
side="right"
></revrag-embed-button>
`,
})
export class AppShellComponent {
showEmbed = false;
constructor(private router: Router) {
this.router.events
.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
.subscribe((e) => {
this.showEmbed = e.urlAfterRedirects === '/offers';
});
}
}Pros: Simple, explicit, no extra context.
Cons: Pathnames are hardcoded next to *ngIf; adding/removing screens means editing the same conditional.
Pattern 2 — EmbedProvider with includeScreens
Best for: router-based apps where the widget should appear on a known set of routes.
The provider handles route matching, delay logic, and button lifecycle automatically.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';
import { EmbedProviderComponent } from '@revrag-ai/embed-angular';
@Component({
selector: 'app-shell',
standalone: true,
imports: [CommonModule, RouterModule, EmbedProviderComponent],
template: `
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="['/offers', '/help']"
matchMode="exact"
widgetPositioning="fixed"
>
<nav>
<a routerLink="/">Home</a>
<a routerLink="/offers">Offers</a>
<a routerLink="/help">Help</a>
</nav>
<router-outlet></router-outlet>
</revrag-embed-provider>
`,
})
export class AppShellComponent {
currentPath = '/';
constructor(private router: Router) {
this.router.events
.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
.subscribe((e) => (this.currentPath = e.urlAfterRedirects));
}
}To show the widget on /help and all sub-routes like /help/faq, use matchMode="startsWith":
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="['/offers', '/help']"
matchMode="startsWith"
>Pattern 3 — Object-based config
Best for: teams that prefer keeping widget configuration in a separate object (easier to share across components, load from a config service, or drive from environment variables).
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';
import { EmbedProviderComponent } from '@revrag-ai/embed-angular';
const EMBED_CONFIG = {
includeScreens: ['/offers', '/help', '/support'],
matchMode: 'exact' as const,
};
@Component({
selector: 'app-shell',
standalone: true,
imports: [CommonModule, RouterModule, EmbedProviderComponent],
template: `
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="embedConfig.includeScreens"
[matchMode]="embedConfig.matchMode"
widgetPositioning="fixed"
>
<router-outlet></router-outlet>
</revrag-embed-provider>
`,
})
export class AppShellComponent {
currentPath = '/';
embedConfig = EMBED_CONFIG;
constructor(private router: Router) {
this.router.events
.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
.subscribe((e) => (this.currentPath = e.urlAfterRedirects));
}
}Config from a service or environment
// embed.config.ts
export const EMBED_CONFIG = {
includeScreens: (import.meta.env['VITE_EMBED_SCREENS'] ?? '/offers,/help').split(','),
matchMode: 'exact' as const,
};For Angular CLI apps with environment.ts:
// environments/environment.ts
export const environment = {
embedApiKey: 'your_api_key',
embedScreens: ['/offers', '/help', '/support'],
};// app.component.ts
import { environment } from '../environments/environment';
ngOnInit(): void {
this.embedInit.initialize(environment.embedApiKey);
}<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="embedScreens"
>embedScreens = environment.embedScreens;Pattern 4 — Tab-based navigation (no router)
Best for: single-page apps or dashboards that use tab components instead of the Angular router.
Map your active tab to a virtual path and pass it as currentPath. The widget appears whenever currentPath matches a path in includeScreens.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { EmbedProviderComponent } from '@revrag-ai/embed-angular';
interface Tab { id: string; label: string; path: string; }
const TABS: Tab[] = [
{ id: 'dashboard', label: 'Dashboard', path: '/dashboard' },
{ id: 'offers', label: 'Offers', path: '/offers' },
{ id: 'settings', label: 'Settings', path: '/settings' },
];
@Component({
selector: 'app-shell',
standalone: true,
imports: [CommonModule, EmbedProviderComponent],
template: `
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="['/offers']"
widgetPositioning="fixed"
>
<div class="tab-bar">
<button
*ngFor="let tab of tabs"
(click)="setActiveTab(tab.id)"
[class.active]="activeTab === tab.id"
>
{{ tab.label }}
</button>
</div>
<div class="tab-content">
<ng-container [ngSwitch]="activeTab">
<div *ngSwitchCase="'dashboard'">Dashboard content</div>
<div *ngSwitchCase="'offers'">Offers content — widget is visible here</div>
<div *ngSwitchCase="'settings'">Settings content</div>
</ng-container>
</div>
</revrag-embed-provider>
`,
})
export class AppShellComponent {
tabs = TABS;
activeTab = 'dashboard';
get currentPath(): string {
return this.tabs.find((t) => t.id === this.activeTab)?.path ?? '/';
}
setActiveTab(tabId: string): void {
this.activeTab = tabId;
}
}Pattern 5 — With delay and embedButtonVisibilityConfig
Best for: onboarding flows, checkout funnels, or support pages where you want the widget to appear after a delay — but only once per session or per group entry.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';
import {
EmbedProviderComponent,
type EmbedButtonVisibilityConfig,
} from '@revrag-ai/embed-angular';
@Component({
selector: 'app-shell',
standalone: true,
imports: [CommonModule, RouterModule, EmbedProviderComponent],
template: `
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="allScreens"
[embedButtonVisibilityConfig]="visibilityConfig"
widgetPositioning="fixed"
>
<router-outlet></router-outlet>
</revrag-embed-provider>
`,
})
export class AppShellComponent {
currentPath = '/';
allScreens = ['/offers', '/checkout', '/payment', '/support', '/help'];
visibilityConfig: EmbedButtonVisibilityConfig = {
defaultDelayMs: 1500, // fallback if a screen isn't in any group
groups: [
{
// Shows with a 2s delay, every time the user lands here
id: 'perScreen',
screens: ['/offers'],
continuity: 'perScreen',
delayMs: 2000,
delayPolicy: 'perScreen',
},
{
// Shows with a 2s delay only when entering the checkout flow;
// no delay when moving between /checkout and /payment
id: 'checkout-flow',
screens: ['/checkout', '/payment'],
continuity: 'continuous',
delayMs: 2000,
delayPolicy: 'oncePerGroupEntry',
},
{
// Shows with a 2s delay only once per browser session
id: 'support',
screens: ['/support', '/help'],
continuity: 'perScreen',
delayMs: 2000,
delayPolicy: 'oncePerAppSession',
},
],
};
constructor(private router: Router) {
this.router.events
.pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
.subscribe((e) => (this.currentPath = e.urlAfterRedirects));
}
}Pattern 6 — startsWith for nested routes
Best for: feature areas with nested routes where the widget should appear on any sub-page.
@Component({
template: `
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="['/help', '/offers']"
matchMode="startsWith"
widgetPositioning="fixed"
>
<router-outlet></router-outlet>
</revrag-embed-provider>
`,
})
export class AppShellComponent {
// /help, /help/faq, /help/contact → all show the widget
// /offers, /offers/123, /offers/details → all show the widget
}Pattern 7 — NgModule-based Apps
For applications that have not migrated to standalone components, import EmbedModule in your root module:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { EmbedModule } from '@revrag-ai/embed-angular';
import { AppComponent } from './app.component';
import { AppShellComponent } from './app-shell.component';
@NgModule({
declarations: [AppComponent, AppShellComponent],
imports: [
BrowserModule,
BrowserAnimationsModule, // required
EmbedModule, // registers all embed selectors
],
bootstrap: [AppComponent],
})
export class AppModule {}All component selectors (revrag-embed-button, revrag-embed-provider) and inputs remain the same. You do not need to import individual components when EmbedModule is imported.
Pattern 8 — With global delay (embedButtonDelayMs)
Use the simple embedButtonDelayMs input when you want a uniform delay across all included screens without per-group config:
<revrag-embed-provider
[currentPath]="currentPath"
[includeScreens]="['/offers', '/help']"
[embedButtonDelayMs]="3000"
widgetPositioning="fixed"
>
<router-outlet></router-outlet>
</revrag-embed-provider>The widget will wait 3 seconds after any matching route becomes active before appearing.
Best Practices
-
Keep
currentPathin sync — Always update it fromNavigationEnd.urlAfterRedirects, notNavigationStart, to ensure the URL reflects the fully resolved route. -
Use
matchMode: 'startsWith'for nested routes — Matches/helpplus any sub-path like/help/faq. Use'exact'when you need precise control. -
Initialize first — Always call
EmbedInitService.initialize()inAppComponent.ngOnInit()and gate widget rendering onisInitialized$. -
Handle errors — Subscribe to
error$fromEmbedInitServiceand surface it to the user or log it. -
Add CSS to
angular.json— Importnode_modules/@revrag-ai/embed-angular/styles/widget.cssin thestylesarray; rebuild after adding it. -
Add
provideAnimations()— Place it inapp.config.ts(standalone) or importBrowserAnimationsModule(NgModule). Missing it breaks widget transitions silently. -
Use
embedButtonVisibilityConfigfor per-route delay policies — Prefer per-group delays over a single globalembedButtonDelayMsin multi-route apps. -
oncePerAppSessionfor support screens — Avoids re-showing the delay on every visit during a session. -
oncePerGroupEntryfor funnels — Delay only when a user first enters a checkout or onboarding flow; no delay when they move between steps. -
continuousfor multi-step flows — Prevents the widget from disappearing and reappearing as the user navigates through/checkout→/payment→/confirmation. -
Clean up subscriptions — Unsubscribe from router event subscriptions (or use
takeUntilDestroyed()) inngOnDestroy()to prevent memory leaks. -
Use
embedButtonPositionfor fine-grained placement — When the defaultwidgetSide+widgetBottomOffsetisn't precise enough, pass anEmbedButtonPositionobject with explicit pixel values.
Summary
| Pattern | Approach | Best For |
|---|---|---|
| 1 — Manual | *ngIf + EmbedButtonComponent | Explicit imperative control |
| 2 — Provider + screens | EmbedProviderComponent + includeScreens | Router-based apps |
| 3 — Object config | Config constant / service → EmbedProviderComponent | Shared config, env-driven |
| 4 — Tab-based | Virtual paths from active tab state | No-router / dashboard apps |
| 5 — Visibility config | embedButtonVisibilityConfig with groups | Delay + continuity policies |
| 6 — startsWith | matchMode="startsWith" | Nested route areas |
| 7 — NgModule | EmbedModule | Legacy NgModule apps |
| 8 — Global delay | embedButtonDelayMs | Uniform delay, no group logic needed |
Using EmbedProviderComponent + includeScreens is the recommended pattern for most Angular apps: one provider, one config list, the widget shown only on the screens you choose.