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

input-output.mddocs/

Input/Output Management

Type-safe system for binding inputs and outputs to dynamic components at runtime, supporting both function handlers and complex output expressions with additional arguments and custom contexts.

Capabilities

Dynamic I/O Directive

Main directive for managing dynamic inputs and outputs on ndc-dynamic components.

/**
 * Manages dynamic inputs and outputs for ndc-dynamic components
 * Automatically updates component properties and event handlers when inputs change
 */
@Directive({
  selector: '[ndcDynamicInputs],[ndcDynamicOutputs]',
  standalone: true,
  exportAs: 'ndcDynamicIo'
})
export class DynamicIoDirective implements DoCheck {
  /** Dynamic input bindings for the component */
  @Input() ndcDynamicInputs?: InputsType | null;
  
  /** Dynamic output bindings for the component */
  @Input() ndcDynamicOutputs?: OutputsType | null;
}

I/O Service

Core service for managing dynamic inputs and outputs with lifecycle management.

/**
 * Core service for managing dynamic inputs and outputs
 * Handles property updates and event subscription management
 */
@Injectable()
export class IoService implements OnDestroy {
  /**
   * Updates component inputs and outputs
   * @param inputs - Object mapping property names to values
   * @param outputs - Object mapping event names to handlers
   */
  update(inputs?: InputsType | null, outputs?: OutputsType | null): void;
}

/**
 * Configuration options for IoService
 */
@Injectable({ providedIn: 'root' })
export class IoServiceOptions {
  /** Whether to track output changes for optimization */
  trackOutputChanges: boolean = false;
}

I/O Factory Service

Factory service for creating IoService instances with custom configuration.

/**
 * Factory for creating IoService instances
 * Enables custom configuration per component instance
 */
@Injectable({ providedIn: 'root' })
export class IoFactoryService {
  /**
   * Creates a new IoService instance
   * @param componentInjector - Component injector providing ComponentRef access
   * @param ioOptions - Optional configuration for the service
   * @returns Configured IoService instance
   */
  create(
    componentInjector: DynamicComponentInjector,
    ioOptions?: IoServiceOptions & IoFactoryServiceOptions
  ): IoService;
}

/**
 * Additional options for IoFactoryService.create method
 */
interface IoFactoryServiceOptions {
  /** Optional injector override */
  injector?: Injector;
}

Usage Examples:

import { Component } from '@angular/core';
import { DynamicComponent, DynamicIoDirective, InputsType, OutputsType } from 'ng-dynamic-component';

// Basic input/output binding
@Component({
  standalone: true,
  imports: [DynamicComponent, DynamicIoDirective],
  template: `
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicInputs]="inputs"
      [ndcDynamicOutputs]="outputs">
    </ndc-dynamic>
  `
})
export class BasicIoExampleComponent {
  component = MyFormComponent;
  
  inputs: InputsType = {
    title: 'User Registration',
    required: true,
    placeholder: 'Enter your name',
    maxLength: 50
  };
  
  outputs: OutputsType = {
    onSubmit: (formData: any) => this.handleSubmit(formData),
    onCancel: () => this.handleCancel(),
    onChange: (value: string) => console.log('Value changed:', value)
  };

  handleSubmit(formData: any) {
    console.log('Form submitted:', formData);
    // Process form submission
  }

  handleCancel() {
    console.log('Form cancelled');
    // Handle cancellation
  }
}

// Dynamic input/output changes
@Component({
  template: `
    <button (click)="updateInputs()">Update Inputs</button>
    <button (click)="toggleOutputs()">Toggle Outputs</button>
    
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicInputs]="currentInputs"
      [ndcDynamicOutputs]="currentOutputs">
    </ndc-dynamic>
  `
})
export class DynamicIoExampleComponent {
  component = DataDisplayComponent;
  
  private inputVariations: InputsType[] = [
    { title: 'Variation 1', theme: 'light', showHeader: true },
    { title: 'Variation 2', theme: 'dark', showHeader: false },
    { title: 'Variation 3', theme: 'auto', showHeader: true }
  ];
  
  private currentVariation = 0;
  private outputsEnabled = true;

  get currentInputs(): InputsType {
    return this.inputVariations[this.currentVariation];
  }

  get currentOutputs(): OutputsType | null {
    return this.outputsEnabled ? {
      onItemClick: (item: any) => console.log('Item clicked:', item),
      onSelectionChange: (selection: any[]) => console.log('Selection:', selection)
    } : null;
  }

  updateInputs() {
    this.currentVariation = (this.currentVariation + 1) % this.inputVariations.length;
  }

  toggleOutputs() {
    this.outputsEnabled = !this.outputsEnabled;
  }
}

Types

Core Types

/**
 * Type for dynamic input bindings
 * Maps property names to their values
 */
interface InputsType {
  [k: string]: unknown;
}

/**
 * Type for dynamic output bindings
 * Maps event names to handler expressions
 */
interface OutputsType {
  [k: string]: OutputExpression | undefined;
}

/**
 * Union type for output expressions
 * Can be a simple handler function or complex expression with arguments
 */
type OutputExpression = EventHandler | OutputWithArgs;

/**
 * Type for event handler functions
 * @template T - The event data type
 */
type EventHandler<T = unknown> = (event: T) => unknown;

/**
 * Generic function type for handlers with arguments
 */
type AnyFunction = (...args: unknown[]) => unknown;

Advanced Output Types

/**
 * Output expression with additional arguments
 * Enables passing extra data to event handlers
 */
interface OutputWithArgs {
  /** The event handler function */
  handler: AnyFunction;
  /** Optional arguments to pass to the handler */
  args?: unknown[];
}

Usage Examples:

// Using OutputWithArgs for complex event handling
const complexOutputs: OutputsType = {
  // Simple handler
  onSimpleEvent: (data: any) => console.log(data),
  
  // Handler with additional arguments
  onComplexEvent: {
    handler: (event: any, userId: number, context: string) => {
      console.log('Event:', event, 'User:', userId, 'Context:', context);
    },
    args: [123, 'dashboard']
  }
};

Event Context and Configuration

Event Argument Configuration

/**
 * Default factory for event argument token
 * @returns The default event argument name
 */
function defaultEventArgumentFactory(): string;

/**
 * Token for configuring event argument name in output expressions
 * Default value is '$event'
 */
const IoEventArgumentToken: InjectionToken<string>;

/**
 * @deprecated Use IoEventArgumentToken instead
 * Deprecated since v10.4.0
 */
const EventArgumentToken: InjectionToken<string>;

Event Context Providers

/**
 * Token for providing custom context for output handlers
 * Allows injecting additional data available to all event handlers
 */
const IoEventContextToken: InjectionToken<unknown>;

/**
 * Token for providing StaticProvider that configures IoEventContextToken
 * Enables dynamic context configuration per component
 */
const IoEventContextProviderToken: InjectionToken<StaticProvider>;

Usage Examples:

// Custom event argument configuration
@Component({
  providers: [
    { provide: IoEventArgumentToken, useValue: '$data' }
  ],
  template: `
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicOutputs]="outputs">
    </ndc-dynamic>
  `
})
export class CustomEventArgComponent {
  outputs = {
    // Now '$data' is used instead of '$event'
    onClick: '$data.preventDefault(); handleClick($data)'
  };
}

// Custom event context
@Component({
  providers: [
    { 
      provide: IoEventContextToken, 
      useValue: { userId: 123, permissions: ['read', 'write'] }
    }
  ],
  template: `<!-- Dynamic component with custom context -->`
})
export class ContextExampleComponent { }

Component I/O Abstract Service

ComponentIO Service

/**
 * Abstract service for setting inputs and getting outputs on dynamic components
 * Extensible interface for custom I/O management implementations
 */
@Injectable({
  providedIn: 'root',
  useClass: ClassicComponentIO
})
export abstract class ComponentIO {
  /**
   * Sets an input property on a component
   * @param componentRef - Reference to the component
   * @param name - Name of the input property
   * @param value - Value to set
   */
  abstract setInput<T, K extends ComponentInputKey<T>>(
    componentRef: ComponentRef<T>,
    name: K,
    value: T[K]
  ): void;

  /**
   * Gets an output observable from a component
   * @param componentRef - Reference to the component
   * @param name - Name of the output property
   * @returns Observable of the output events
   */
  abstract getOutput<T, K extends ComponentInputKey<T>>(
    componentRef: ComponentRef<T>,
    name: K
  ): Observable<unknown>;
}

/**
 * Type constraint for component input property names
 * Ensures type safety when setting component inputs
 */
type ComponentInputKey<T> = keyof T & string;

docs

component-outlet.md

core-services.md

dynamic-attributes.md

dynamic-component.md

dynamic-directives.md

index.md

input-output.md

tile.json