CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ng-dynamic-component

Dynamic components with full life-cycle support for inputs and outputs

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

component-outlet.mddocs/

NgComponentOutlet Enhancement

Directives that extend Angular's built-in NgComponentOutlet with dynamic input/output binding, component reference access, and additional lifecycle management capabilities.

Capabilities

Component Outlet Injector Directive

Provides access to the ComponentRef created by NgComponentOutlet for further manipulation and lifecycle management.

/**
 * Provides ComponentRef access for NgComponentOutlet
 * Enables accessing the component instance created by the outlet
 */
@Directive({
  selector: '[ngComponentOutlet]',
  standalone: true,
  exportAs: 'ndcComponentOutletInjector'
})
export class ComponentOutletInjectorDirective {
  /** 
   * Reference to the component created by NgComponentOutlet
   * Provides access to the component instance and lifecycle methods
   */
  readonly componentRef: ComponentRef<unknown>;
}

Component Outlet I/O Directive

Enables dynamic input/output binding for components created by NgComponentOutlet.

/**
 * Enables dynamic inputs/outputs for NgComponentOutlet
 * Works in conjunction with Angular's built-in outlet directive
 */
@Directive({
  selector: '[ngComponentOutletNdcDynamicInputs],[ngComponentOutletNdcDynamicOutputs]',
  standalone: true,
  exportAs: 'ndcDynamicIo'
})
export class ComponentOutletIoDirective implements DoCheck {
  /** Dynamic inputs for the outlet component */
  @Input() ngComponentOutletNdcDynamicInputs?: InputsType | null;
  
  /** Dynamic outputs for the outlet component */
  @Input() ngComponentOutletNdcDynamicOutputs?: OutputsType | null;
}

Component Outlet Injector Module

NgModule that provides both outlet enhancement directives for traditional module-based applications.

/**
 * Module that exports both outlet enhancement directives
 * Use this in NgModule-based applications
 */
@NgModule({
  imports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective],
  exports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective]
})
export class ComponentOutletInjectorModule {}

Usage Examples:

import { Component, Type, ComponentRef } from '@angular/core';
import { ComponentOutletInjectorDirective, ComponentOutletIoDirective } from 'ng-dynamic-component';

// Basic outlet with component reference access
@Component({
  standalone: true,
  imports: [ComponentOutletInjectorDirective],
  template: `
    <ng-container 
      *ngComponentOutlet="selectedComponent"
      #outlet="ndcComponentOutletInjector">
    </ng-container>
    
    <button (click)="accessComponent(outlet.componentRef)">
      Access Component
    </button>
  `
})
export class OutletBasicExampleComponent {
  selectedComponent: Type<any> = MyComponent;

  accessComponent(componentRef: ComponentRef<any>) {
    console.log('Component instance:', componentRef.instance);
    
    // Directly modify component properties
    componentRef.instance.title = 'Modified Title';
    
    // Trigger change detection
    componentRef.changeDetectorRef.detectChanges();
    
    // Access component methods
    if (typeof componentRef.instance.refresh === 'function') {
      componentRef.instance.refresh();
    }
  }
}

// Outlet with dynamic I/O binding
@Component({
  standalone: true,
  imports: [ComponentOutletIoDirective],
  template: `
    <ng-container 
      *ngComponentOutlet="componentType"
      [ngComponentOutletNdcDynamicInputs]="inputs"
      [ngComponentOutletNdcDynamicOutputs]="outputs">
    </ng-container>
  `
})
export class OutletIoExampleComponent {
  componentType = UserProfileComponent;
  
  inputs = {
    userId: 123,
    editMode: false,
    theme: 'light'
  };
  
  outputs = {
    onSave: (userData: any) => this.saveUser(userData),
    onCancel: () => this.cancelEdit(),
    onProfilePictureChange: (imageUrl: string) => this.updateProfilePicture(imageUrl)
  };

  saveUser(userData: any) {
    console.log('Saving user:', userData);
    // Implement save logic
  }

  cancelEdit() {
    console.log('Edit cancelled');
    this.inputs = { ...this.inputs, editMode: false };
  }

  updateProfilePicture(imageUrl: string) {
    console.log('Profile picture updated:', imageUrl);
    // Handle image update
  }
}

// Combined usage with both directives
@Component({
  standalone: true,
  imports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective],
  template: `
    <ng-container 
      *ngComponentOutlet="currentComponent"
      [ngComponentOutletNdcDynamicInputs]="dynamicInputs"
      [ngComponentOutletNdcDynamicOutputs]="dynamicOutputs"
      #outlet="ndcComponentOutletInjector">
    </ng-container>
    
    <div class="controls">
      <button (click)="refreshComponent(outlet.componentRef)">Refresh</button>
      <button (click)="switchComponent()">Switch Component</button>
    </div>
  `
})
export class OutletCombinedExampleComponent {
  private components = [DashboardComponent, SettingsComponent, ProfileComponent];
  private currentIndex = 0;

  get currentComponent() {
    return this.components[this.currentIndex];
  }

  get dynamicInputs() {
    // Return different inputs based on current component
    switch (this.currentComponent) {
      case DashboardComponent:
        return { userId: 123, refreshInterval: 5000 };
      case SettingsComponent:
        return { theme: 'dark', notifications: true };
      case ProfileComponent:
        return { userId: 123, editMode: false };
      default:
        return {};
    }
  }

  get dynamicOutputs() {
    return {
      onAction: (action: string, data?: any) => this.handleAction(action, data),
      onNavigate: (route: string) => this.navigate(route)
    };
  }

  refreshComponent(componentRef: ComponentRef<any>) {
    if (typeof componentRef.instance.refresh === 'function') {
      componentRef.instance.refresh();
    }
  }

  switchComponent() {
    this.currentIndex = (this.currentIndex + 1) % this.components.length;
    console.log('Switched to:', this.currentComponent.name);
  }

  handleAction(action: string, data?: any) {
    console.log('Action received:', action, data);
  }

  navigate(route: string) {
    console.log('Navigate to:', route);
    // Implement navigation logic
  }
}

Working with Complex Component Hierarchies

Handle nested components and complex injection scenarios:

@Component({
  template: `
    <div class="parent-container">
      <ng-container 
        *ngComponentOutlet="
          parentComponent;
          injector: customInjector;
          content: projectedNodes
        "
        [ngComponentOutletNdcDynamicInputs]="parentInputs"
        [ngComponentOutletNdcDynamicOutputs]="parentOutputs"
        #parentOutlet="ndcComponentOutletInjector">
      </ng-container>
      
      <!-- Child component that depends on parent -->
      <ng-container 
        *ngComponentOutlet="childComponent"
        [ngComponentOutletNdcDynamicInputs]="getChildInputs(parentOutlet.componentRef)"
        [ngComponentOutletNdcDynamicOutputs]="childOutputs">
      </ng-container>
    </div>
  `
})
export class HierarchicalOutletComponent {
  parentComponent = ParentComponent;
  childComponent = ChildComponent;
  
  parentInputs = {
    title: 'Parent Component',
    configuration: { mode: 'advanced' }
  };
  
  parentOutputs = {
    onConfigChange: (config: any) => this.updateConfiguration(config)
  };
  
  childOutputs = {
    onChildEvent: (event: any) => this.handleChildEvent(event)
  };

  constructor(private injector: Injector) {}

  get customInjector() {
    return Injector.create({
      providers: [
        { provide: 'PARENT_CONFIG', useValue: { theme: 'blue' } }
      ],
      parent: this.injector
    });
  }

  get projectedNodes() {
    // Create content to project into parent component
    return [
      // Projected content nodes
    ];
  }

  getChildInputs(parentComponentRef: ComponentRef<any>) {
    // Child inputs depend on parent component state
    return {
      parentData: parentComponentRef?.instance?.data || null,
      inherited: true
    };
  }

  updateConfiguration(config: any) {
    console.log('Parent configuration updated:', config);
  }

  handleChildEvent(event: any) {
    console.log('Child event received:', event);
  }
}

Integration with Angular Router

Use outlet components with router data and parameters:

import { ActivatedRoute } from '@angular/router';

@Component({
  template: `
    <ng-container 
      *ngComponentOutlet="getComponentForRoute()"
      [ngComponentOutletNdcDynamicInputs]="routeInputs"
      [ngComponentOutletNdcDynamicOutputs]="routeOutputs">
    </ng-container>
  `
})
export class RoutedOutletComponent implements OnInit {
  private routeData: any = {};
  private routeParams: any = {};

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    // Subscribe to route data and parameters
    this.route.data.subscribe(data => {
      this.routeData = data;
    });
    
    this.route.params.subscribe(params => {
      this.routeParams = params;
    });
  }

  getComponentForRoute(): Type<any> {
    // Select component based on route data
    return this.routeData.component || DefaultComponent;
  }

  get routeInputs() {
    return {
      ...this.routeParams,
      routeData: this.routeData,
      timestamp: Date.now()
    };
  }

  get routeOutputs() {
    return {
      onRouteAction: (action: string) => this.handleRouteAction(action)
    };
  }

  handleRouteAction(action: string) {
    console.log('Route action:', action);
    // Handle route-specific actions
  }
}

Module Usage

For NgModule-based applications:

import { NgModule } from '@angular/core';
import { ComponentOutletInjectorModule } from 'ng-dynamic-component';

@NgModule({
  imports: [
    ComponentOutletInjectorModule
  ],
  // Component can now use outlet directives
})
export class MyFeatureModule {}

docs

component-outlet.md

core-services.md

dynamic-attributes.md

dynamic-component.md

dynamic-directives.md

index.md

input-output.md

tile.json