# EmbedProvider advanced (Angular)

> Advanced EmbedProviderComponent patterns for Angular: route visibility, delay policies, group configuration, programmatic control, and best practices.

URL: /embed/integration/embed-angular-provider-advanced
Markdown: /embed/integration/embed-angular-provider-advanced.md



# Angular — Advanced Provider Usage [#angular--advanced-provider-usage]

> Advanced `EmbedProviderComponent` patterns — route-based visibility, delay policies, group configuration, programmatic control, and best practices.

***

## Table of Contents [#table-of-contents]

1. [Quick Start](#quick-start)
2. [EmbedProviderComponent — Props Reference](#embedprovidercomponent--props-reference)
3. [embedButtonVisibilityConfig](#embedbuttonvisibilityconfig)
4. [EmbedButtonComponent Props](#embedbuttoncomponent-props)
5. [EmbedInitService](#embedinitservice)
6. [Usage Patterns](#usage-patterns)
7. [Best Practices](#best-practices)

***

## Quick Start [#quick-start]

```typescript
// 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');
  }
}
```

```typescript
// 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:**

1. Call `EmbedInitService.initialize(apiKey)` in `AppComponent.ngOnInit()`.
2. Wrap your layout with `<revrag-embed-provider>`.
3. Keep `currentPath` in sync with the Angular router.
4. Add the CSS to `angular.json` styles array.

***

## EmbedProviderComponent — Props Reference [#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 [#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:

```typescript
this.router.events
  .pipe(filter((e): e is NavigationEnd => e instanceof NavigationEnd))
  .subscribe((e) => {
    this.currentPath = e.urlAfterRedirects;
  });
```

***

## embedButtonVisibilityConfig [#embedbuttonvisibilityconfig]

Use this for per-route or per-group behavior — different delays, policies, and continuity settings.

```typescript
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',
    },
  ],
};
```

```html
<revrag-embed-provider
  [currentPath]="currentPath"
  [includeScreens]="allScreens"
  [embedButtonVisibilityConfig]="visibilityConfig"
>
  <router-outlet></router-outlet>
</revrag-embed-provider>
```

### EmbedButtonVisibilityConfig [#embedbuttonvisibilityconfig-1]

| Field            | Type                       | Description                                               |
| ---------------- | -------------------------- | --------------------------------------------------------- |
| `defaultDelayMs` | `number`                   | Fallback delay when a group matches but has no `delayMs`. |
| `groups`         | `EmbedButtonGroupConfig[]` | Per-group configuration array.                            |

### EmbedButtonGroupConfig [#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 [#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 [#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 [#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 [#positionconfig]

```typescript
interface PositionConfig {
  bottom?: string;    // e.g. '24px'
  right?: string;     // e.g. '24px'
  left?: string;
  top?: string;
  transform?: string;
  zIndex?: number;
}
```

### Example [#example]

```html
<revrag-embed-provider
  [currentPath]="currentPath"
  [includeScreens]="['/offers']"
  widgetPositioning="fixed"
  widgetSide="right"
  [widgetBottomOffset]="80"
>
  <router-outlet></router-outlet>
</revrag-embed-provider>
```

***

## EmbedInitService [#embedinitservice]

Initializes the SDK. Call **once** in `AppComponent.ngOnInit()` before any widget renders.

```typescript
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 [#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 [#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 [#usage-patterns]

### Pattern 1 — Manual conditional rendering [#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.

```typescript
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.
&#x2A;*Cons:** Pathnames are hardcoded next to `*ngIf`; adding/removing screens means editing the same conditional.

***

### Pattern 2 — EmbedProvider with `includeScreens` [#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.

```typescript
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"`:

```html
<revrag-embed-provider
  [currentPath]="currentPath"
  [includeScreens]="['/offers', '/help']"
  matchMode="startsWith"
>
```

***

### Pattern 3 — Object-based config [#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).

```typescript
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 [#config-from-a-service-or-environment]

```typescript
// 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`:

```typescript
// environments/environment.ts
export const environment = {
  embedApiKey: 'your_api_key',
  embedScreens: ['/offers', '/help', '/support'],
};
```

```typescript
// app.component.ts
import { environment } from '../environments/environment';

ngOnInit(): void {
  this.embedInit.initialize(environment.embedApiKey);
}
```

```html
<revrag-embed-provider
  [currentPath]="currentPath"
  [includeScreens]="embedScreens"
>
```

```typescript
embedScreens = environment.embedScreens;
```

***

### Pattern 4 — Tab-based navigation (no router) [#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`.

```typescript
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` [#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.

```typescript
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 [#pattern-6--startswith-for-nested-routes]

**Best for:** feature areas with nested routes where the widget should appear on any sub-page.

```typescript
@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 [#pattern-7--ngmodule-based-apps]

For applications that have not migrated to standalone components, import `EmbedModule` in your root module:

```typescript
// 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`) [#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:

```html
<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 [#best-practices]

1. **Keep `currentPath` in sync** — Always update it from `NavigationEnd.urlAfterRedirects`, not `NavigationStart`, to ensure the URL reflects the fully resolved route.

2. **Use `matchMode: 'startsWith'` for nested routes** — Matches `/help` plus any sub-path like `/help/faq`. Use `'exact'` when you need precise control.

3. **Initialize first** — Always call `EmbedInitService.initialize()` in `AppComponent.ngOnInit()` and gate widget rendering on `isInitialized$`.

4. **Handle errors** — Subscribe to `error$` from `EmbedInitService` and surface it to the user or log it.

5. **Add CSS to `angular.json`** — Import `node_modules/@revrag-ai/embed-angular/styles/widget.css` in the `styles` array; rebuild after adding it.

6. **Add `provideAnimations()`** — Place it in `app.config.ts` (standalone) or import `BrowserAnimationsModule` (NgModule). Missing it breaks widget transitions silently.

7. **Use `embedButtonVisibilityConfig` for per-route delay policies** — Prefer per-group delays over a single global `embedButtonDelayMs` in multi-route apps.

8. **`oncePerAppSession` for support screens** — Avoids re-showing the delay on every visit during a session.

9. **`oncePerGroupEntry` for funnels** — Delay only when a user first enters a checkout or onboarding flow; no delay when they move between steps.

10. **`continuous` for multi-step flows** — Prevents the widget from disappearing and reappearing as the user navigates through `/checkout` → `/payment` → `/confirmation`.

11. **Clean up subscriptions** — Unsubscribe from router event subscriptions (or use `takeUntilDestroyed()`) in `ngOnDestroy()` to prevent memory leaks.

12. **Use `embedButtonPosition` for fine-grained placement** — When the default `widgetSide` + `widgetBottomOffset` isn't precise enough, pass an `EmbedButtonPosition` object with explicit pixel values.

***

## Summary [#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 &#x2A;*`EmbedProviderComponent` + `includeScreens`** is the recommended pattern for most Angular apps: one provider, one config list, the widget shown only on the screens you choose.
