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

dynamic-attributes.mddocs/

Dynamic Attributes

System for dynamically setting and managing HTML attributes on component elements at runtime, supporting both declarative attribute binding and imperative attribute manipulation.

Capabilities

Dynamic Attributes Directive

Main directive for dynamically setting HTML attributes on component host elements.

/**
 * Dynamically sets HTML attributes on component elements
 * Automatically updates attributes when the attribute map changes
 */
@Directive({
  selector: '[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]',
  standalone: true,
  exportAs: 'ndcDynamicAttributes'
})
export class DynamicAttributesDirective implements DoCheck {
  /** Attributes for ndc-dynamic components */
  @Input() ndcDynamicAttributes?: AttributesMap | null;
  
  /** Attributes for NgComponentOutlet components */
  @Input() ngComponentOutletNdcDynamicAttributes?: AttributesMap | null;
  
  /**
   * Manually set an attribute on the host element
   * @param name - Attribute name
   * @param value - Attribute value
   * @param namespace - Optional namespace for the attribute
   */
  setAttribute(name: string, value: string, namespace?: string): void;
  
  /**
   * Manually remove an attribute from the host element
   * @param name - Attribute name to remove
   * @param namespace - Optional namespace for the attribute
   */
  removeAttribute(name: string, namespace?: string): void;
}

Dynamic Attributes Module

NgModule that provides dynamic attributes functionality for traditional module-based applications.

/**
 * Module for dynamic attributes functionality
 * Includes component outlet integration
 */
@NgModule({
  imports: [DynamicAttributesDirective, ComponentOutletInjectorModule],
  exports: [DynamicAttributesDirective, ComponentOutletInjectorModule]
})
export class DynamicAttributesModule {}

Types

/**
 * Map of attribute names to their string values
 * Used for declarative attribute binding
 */
interface AttributesMap {
  [key: string]: string;
}

Usage Examples:

import { Component } from '@angular/core';
import { DynamicComponent, DynamicAttributesDirective, AttributesMap } from 'ng-dynamic-component';

// Basic dynamic attributes with ndc-dynamic
@Component({
  standalone: true,
  imports: [DynamicComponent, DynamicAttributesDirective],
  template: `
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicAttributes]="attributes">
    </ndc-dynamic>
  `
})
export class BasicAttributesExampleComponent {
  component = MyComponent;
  
  attributes: AttributesMap = {
    'data-testid': 'dynamic-component',
    'aria-label': 'Dynamic content area',
    'role': 'region',
    'class': 'theme-dark border-rounded',
    'style': 'min-height: 200px; background: #f5f5f5;'
  };
}

// Dynamic attribute changes
@Component({
  template: `
    <div class="controls">
      <button (click)="toggleTheme()">Toggle Theme</button>
      <button (click)="updateAccessibility()">Update Accessibility</button>
      <button (click)="addCustomAttributes()">Add Custom Attrs</button>
    </div>
    
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicAttributes]="currentAttributes"
      #dynamicAttrs="ndcDynamicAttributes">
    </ndc-dynamic>
    
    <button (click)="imperativeUpdate(dynamicAttrs)">Imperative Update</button>
  `
})
export class DynamicAttributesExampleComponent {
  component = ContentComponent;
  
  private theme: 'light' | 'dark' = 'light';
  private accessibilityLevel: 'basic' | 'enhanced' = 'basic';
  private customAttrsEnabled = false;

  get currentAttributes(): AttributesMap {
    const attrs: AttributesMap = {
      'data-theme': this.theme,
      'class': `theme-${this.theme} component-wrapper`,
      'role': 'main'
    };

    // Add accessibility attributes
    if (this.accessibilityLevel === 'enhanced') {
      attrs['aria-live'] = 'polite';
      attrs['aria-describedby'] = 'component-description';
      attrs['tabindex'] = '0';
    }

    // Add custom attributes
    if (this.customAttrsEnabled) {
      attrs['data-custom'] = 'enabled';
      attrs['data-timestamp'] = Date.now().toString();
      attrs['data-version'] = '1.0.0';
    }

    return attrs;
  }

  toggleTheme() {
    this.theme = this.theme === 'light' ? 'dark' : 'light';
  }

  updateAccessibility() {
    this.accessibilityLevel = this.accessibilityLevel === 'basic' ? 'enhanced' : 'basic';
  }

  addCustomAttributes() {
    this.customAttrsEnabled = !this.customAttrsEnabled;
  }

  imperativeUpdate(directiveRef: DynamicAttributesDirective) {
    // Manually set attributes using directive methods
    directiveRef.setAttribute('data-manual', 'true');
    directiveRef.setAttribute('data-updated', new Date().toISOString());
    
    // Set namespaced attribute
    directiveRef.setAttribute('custom-attr', 'value', 'custom-namespace');
    
    // Remove an attribute
    setTimeout(() => {
      directiveRef.removeAttribute('data-manual');
    }, 3000);
  }
}

Working with NgComponentOutlet

Use dynamic attributes with Angular's built-in component outlet:

import { ComponentOutletIoDirective, DynamicAttributesDirective } from 'ng-dynamic-component';

@Component({
  standalone: true,
  imports: [ComponentOutletIoDirective, DynamicAttributesDirective],
  template: `
    <ng-container 
      *ngComponentOutlet="selectedComponent"
      [ngComponentOutletNdcDynamicAttributes]="outletAttributes"
      [ngComponentOutletNdcDynamicInputs]="inputs">
    </ng-container>
  `
})
export class OutletAttributesExampleComponent {
  selectedComponent = DashboardComponent;
  
  inputs = {
    title: 'Dashboard',
    refreshRate: 5000
  };
  
  outletAttributes: AttributesMap = {
    'data-component': 'dashboard',
    'aria-label': 'Dashboard content',
    'class': 'outlet-component dashboard-theme',
    'data-refresh-rate': '5000'
  };
}

Conditional and Computed Attributes

Create dynamic attributes based on component state and conditions:

@Component({
  template: `
    <div class="status-indicators">
      <span>Loading: {{ isLoading }}</span>
      <span>Error: {{ hasError }}</span>
      <span>Theme: {{ currentTheme }}</span>
    </div>
    
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicAttributes]="computedAttributes">
    </ndc-dynamic>
  `
})
export class ConditionalAttributesComponent {
  component = DataComponent;
  
  isLoading = false;
  hasError = false;
  currentTheme = 'light';
  userRole = 'user';
  featureFlags = ['feature-a', 'feature-b'];

  get computedAttributes(): AttributesMap {
    const attrs: AttributesMap = {};

    // Base attributes
    attrs['data-component'] = 'data-display';
    attrs['data-theme'] = this.currentTheme;
    
    // Conditional attributes based on state
    if (this.isLoading) {
      attrs['aria-busy'] = 'true';
      attrs['data-loading'] = 'true';
      attrs['class'] = 'component-loading';
    }
    
    if (this.hasError) {
      attrs['aria-invalid'] = 'true';
      attrs['data-error'] = 'true';
      attrs['class'] = (attrs['class'] || '') + ' component-error';
    }
    
    // Role-based attributes
    if (this.userRole === 'admin') {
      attrs['data-admin'] = 'true';
      attrs['data-permissions'] = 'all';
    }
    
    // Feature flag attributes
    if (this.featureFlags.includes('feature-a')) {
      attrs['data-feature-a'] = 'enabled';
    }
    
    // Computed styles
    attrs['style'] = this.getComputedStyles();
    
    return attrs;
  }

  private getComputedStyles(): string {
    const styles: string[] = [];
    
    if (this.isLoading) {
      styles.push('opacity: 0.6');
      styles.push('pointer-events: none');
    }
    
    if (this.hasError) {
      styles.push('border: 2px solid red');
    }
    
    if (this.currentTheme === 'dark') {
      styles.push('background: #2d2d2d');
      styles.push('color: #ffffff');
    }
    
    return styles.join('; ');
  }

  simulateLoading() {
    this.isLoading = true;
    setTimeout(() => {
      this.isLoading = false;
      this.hasError = Math.random() > 0.7; // 30% chance of error
    }, 2000);
  }
}

Accessibility and ARIA Attributes

Dynamically manage accessibility attributes for better user experience:

@Component({
  template: `
    <div class="accessibility-controls">
      <label>
        <input type="checkbox" [(ngModel)]="screenReaderOptimized" />
        Screen Reader Optimized
      </label>
      <label>
        <input type="checkbox" [(ngModel)]="highContrast" />
        High Contrast
      </label>
      <label>
        <input type="range" min="1" max="5" [(ngModel)]="complexityLevel" />
        Complexity Level: {{ complexityLevel }}
      </label>
    </div>
    
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicAttributes]="accessibilityAttributes">
    </ndc-dynamic>
  `
})
export class AccessibilityAttributesComponent {
  component = InteractiveComponent;
  
  screenReaderOptimized = false;
  highContrast = false;
  complexityLevel = 3;

  get accessibilityAttributes(): AttributesMap {
    const attrs: AttributesMap = {
      'role': 'application',
      'aria-label': 'Interactive content area'
    };

    if (this.screenReaderOptimized) {
      attrs['aria-live'] = 'polite';
      attrs['aria-atomic'] = 'true';
      attrs['aria-relevant'] = 'additions text';
      attrs['aria-describedby'] = 'sr-instructions';
    }

    if (this.highContrast) {
      attrs['data-high-contrast'] = 'true';
      attrs['class'] = 'high-contrast-mode';
    }

    // Complexity-based attributes
    attrs['data-complexity'] = this.complexityLevel.toString();
    
    if (this.complexityLevel <= 2) {
      attrs['aria-label'] = 'Simple interactive content';
      attrs['data-ui-mode'] = 'simplified';
    } else if (this.complexityLevel >= 4) {
      attrs['aria-label'] = 'Advanced interactive content with multiple features';
      attrs['data-ui-mode'] = 'advanced';
      attrs['aria-expanded'] = 'false';
    }

    return attrs;
  }
}

Data Attributes for Testing and Analytics

Use dynamic attributes for testing identifiers and analytics tracking:

@Component({
  template: `
    <ndc-dynamic 
      [ndcDynamicComponent]="component"
      [ndcDynamicAttributes]="testingAndAnalyticsAttributes">
    </ndc-dynamic>
  `
})
export class TestingAttributesComponent implements OnInit {
  component = TestableComponent;
  
  private sessionId = '';
  private userId = '';
  private experimentVariant = '';

  ngOnInit() {
    this.sessionId = this.generateSessionId();
    this.userId = this.getCurrentUserId();
    this.experimentVariant = this.getExperimentVariant();
  }

  get testingAndAnalyticsAttributes(): AttributesMap {
    const attrs: AttributesMap = {
      // Testing attributes
      'data-testid': 'dynamic-component',
      'data-qa': 'main-content',
      'data-component-type': this.component.name,
      
      // Analytics attributes
      'data-analytics-id': 'dynamic-content',
      'data-session-id': this.sessionId,
      'data-user-id': this.userId,
      'data-page-section': 'main',
      
      // A/B testing attributes
      'data-experiment': 'layout-test',
      'data-variant': this.experimentVariant,
      
      // Performance tracking
      'data-track-performance': 'true',
      'data-component-load-time': Date.now().toString()
    };

    return attrs;
  }

  private generateSessionId(): string {
    return 'session-' + Math.random().toString(36).substr(2, 9);
  }

  private getCurrentUserId(): string {
    // Get from authentication service
    return 'user-123';
  }

  private getExperimentVariant(): string {
    // Get from A/B testing service
    return Math.random() > 0.5 ? 'variant-a' : 'variant-b';
  }
}

Module Usage

For NgModule-based applications:

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

@NgModule({
  imports: [
    DynamicAttributesModule
  ],
  // Components can now use dynamic attributes
})
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