import { Component, signal } from '@angular/core'; @Component({ selector: 'app-dynamic-loader', standalone: true, template: ` <ng-container *ngIf="currentComponent() as comp"> <ng-container *ngComponentOutlet="comp"></ng-container> </ng-container> <button (click)="toggle()">Toggle Component</button> ` }) export class DynamicLoaderComponent { private compA = class { static ɵcmp = { selectors: [['comp-a']], template: function() { document.write('Component A Loaded'); } }; }; private compB = class { static ɵcmp = { selectors: [['comp-b']], template: function() { document.write('Component B Loaded'); } }; }; currentComponent = signal(this.compA); toggle() { this.currentComponent.set(this.currentComponent() === this.compA ? this.compB : this.compA); } }
The signal currentComponent holds the class of the component to load. Initially, it is compA, so 'Component A Loaded' is rendered. When the button is clicked, the signal updates to compB, causing Angular to load and render 'Component B Loaded'. Signals work well with Angular's reactive templates, so the dynamic component updates correctly.
Option A correctly uses inject(ViewContainerRef) to get the container reference and then calls createComponent() to load the component dynamically. Option A incorrectly uses new which is not allowed for Angular dependencies. Option A injects the wrong token. Option A calls a non-existent method loadComponent.
import { Component, ViewChild, ViewContainerRef } from '@angular/core'; @Component({ selector: 'app-host', template: `<ng-template #container></ng-template>` }) export class HostComponent { @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef; ngAfterViewInit() { this.container.createComponent(SomeComponent); } } @Component({ selector: 'app-some', template: `<p>Some works!</p>` }) export class SomeComponent {}
Angular needs the component to be declared in an NgModule or marked as standalone to create it dynamically. Since SomeComponent is neither declared nor standalone, Angular throws a runtime error when trying to create it.
import { Component, signal } from '@angular/core'; @Component({ selector: 'app-toggle', standalone: true, template: ` <ng-container *ngComponentOutlet="current()"></ng-container> <button (click)="toggle()">Toggle</button> ` }) export class ToggleComponent { comp1 = class { static ɵcmp = { selectors: [['comp-1']], template: function() { document.write('First Component'); } }; }; comp2 = class { static ɵcmp = { selectors: [['comp-2']], template: function() { document.write('Second Component'); } }; }; current = signal(this.comp1); toggle() { this.current.set(this.current() === this.comp1 ? this.comp2 : this.comp1); } }
The toggle method switches the current component between comp1 and comp2. Starting with comp1, one toggle switches to comp2, the second toggle switches back to comp1. So after two toggles, 'First Component' is displayed again.
Angular 17+ allows signals to hold component classes. When used with *ngComponentOutlet, the template updates reactively as the signal changes. This removes the need for NgModules in many dynamic loading scenarios, simplifying the process.