Embeddable Components Guide

Complete guide to creating and using embeddable widgets in modules

🧩 Embeddable Components Guide

Embeddable components are reusable UI widgets that modules expose for cross-module composition.


🎯 What Are Embeddables?

Embeddable components are standalone Angular components that:


🏗️ Creating an Embeddable

1. Create the Component

import { Component, input, output, signal, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'lib-user-list-widget',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="widget-container">
      <h3>{{ title() }}</h3>
      @for (user of users(); track user.id) {
        <div class="user-item" (click)="selectUser(user)">
          {{ user.name }}
        </div>
      }
    </div>
  `,
  styleUrl: './user-list-widget.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListWidgetComponent {
  // System inputs (required for widget integration)
  readonly widgetId = input<string>('');
  readonly widgetConfig = input<ResolvedWidgetConfig>();
  
  // Component-specific inputs
  readonly limit = input<number>(10);
  readonly roleFilter = input<string | undefined>();
  
  // Component outputs
  readonly userSelected = output<User>();
  
  // Internal state
  readonly users = signal<User[]>([]);
  
  selectUser(user: User): void {
    this.userSelected.emit(user);
  }
}

###2. Register in Module Manifest

@ModuleDecorator({
  id: 'user-management',
  // ...
})
export class UserManagementModule implements BrowserModuleContract {
  readonly manifest: ModuleManifest = {
    embeddableComponents: [
      {
        id: 'users:list-widget',
        componentType: UserListWidgetComponent,
        displayName: 'User List Widget',
        description: 'Compact widget displaying users',
        category: 'widget',
        permissions: ['users:read'],
        inputs: [
          ParameterBuilders.number('limit')
            .defaultValue(10)
            .min(1)
            .max(50)
            .build(),
          ActionParameterBuilder.string('roleFilter')
            .description('Filter by role')
            .build()
        ]
      }
    ]
  };
}

🔌 Using Embeddables

In Another Module

<!-- Static usage -->
<lib-user-list-widget
  [limit]="5"
  [roleFilter]="'admin'"
  (userSelected)="handleUserSelected($event)"
/>

Dynamic Discovery

// Discover embeddable from module registry
const manifest = moduleRegistry.getManifest('user-management');
const widget = manifest.embeddableComponents?.find(c => c.id === 'users:list-widget');

// Create dynamically
const componentRef = await createComponent(widget!.componentType, {
  environmentInjector: injector,
  hostElement: container
});

// Set inputs
componentRef.setInput('limit', 10);

// Subscribe to outputs
componentRef.instance.userSelected.subscribe(user => {
  console.log('User selected:', user);
});

✅ Best Practices

  1. Always Standalone: Embeddables MUST be standalone components.
  2. OnPush Strategy: Use ChangeDetectionStrategy.OnPush.
  3. Signal-Based State: Use signals for reactive state.
  4. Widget System Inputs: Include widgetId and widgetConfig inputs.
  5. Themed: Use CSS custom properties from the theme system.
  6. Error Handling: Handle errors gracefully, don't crash the host.
  7. Accessible: Proper ARIA labels and keyboard navigation.

##📚 Related Documentation


🧠 Property Metadata APIs (AI + Builder)

Embeddables share their dynamic inputs/outputs with the widget builder through the Properties backend. Two new endpoints keep AI generation aligned with reusable property templates:

Example core-sync response:

{
  "success": true,
  "data": {
    "generatedAt": "2025-12-04T21:18:11.421Z",
    "propertyCount": 58,
    "templateCount": 9,
    "properties": [
      {
        "key": "backgroundColor",
        "category": "appearance",
        "controlType": "color",
        "themeVariable": "--ux-surface",
        "aiMetadata": { "importance": "essential", "useCases": ["cards","buttons"] }
      }
    ],
    "templates": [
      {
        "id": "template_appearance",
        "aiSummary": "Standard colors + typography",
        "properties": [...]
      }
    ]
  }
}

Always prefer these endpoints over hard-coded property lists so embeddables remain future proof across modules.


Last Updated: December 3, 2025 Session ID: 3