我想基于 JSON 配置动态构建 UI。尝试将
ngComponentOutlet
与 AsyncPipe
一起使用,这样我就可以懒惰地 import(...)
组件。我的实现不起作用(请参阅Stackblitz 上的示例)。没有错误,组件也不会初始化。
@Component({
selector: 'app-root',
standalone: true,
imports: [NgComponentOutlet, NgFor, AsyncPipe],
template: `
<ng-container *ngFor="let component of schema">
<ng-content *ngComponentOutlet="load(component.name) | async; inputs: component.inputs"></ng-content>
</ng-container>
`,
})
export class App {
// schema returned from API call
schema = [
{
name: 'Lazy1',
inputs: {
title: 'Lazy 1 Component',
},
},
{
name: 'Lazy2',
inputs: {
title: 'Lazy 2 Component',
},
},
];
componentManifest: Record<string, { load: () => Promise<unknown> }> = {
Lazy1: {
load: () => import('./app/lazy-components/lazy1.component'),
},
Lazy2: {
load: () => import('./app/lazy-components/lazy2.component'),
},
};
async load(component: string) {
return await this.componentManifest[component]
.load()
.then((c) => (c as any)[component]);
}
}
要进行的更改:
ngComponentOutlet
属于ng-container
所以我们可以修改代码来使用它。
componentManifest
的键必须包含组件名称,因为导入具有相同的名称。同样适用于模式数组的 name
属性。
我们可以在构造函数中预处理导入并将其存储在属性
component
中以供异步管道处理。否则,在每次更改检测期间都会不断调用加载方法,这是一个瓶颈。
import {
AsyncPipe,
CommonModule,
NgComponentOutlet,
NgFor,
} from '@angular/common';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { firstValueFrom, from } from 'rxjs';
import 'zone.js';
@Component({
selector: 'app-root',
standalone: true,
imports: [NgComponentOutlet, NgFor, AsyncPipe, CommonModule],
template: `
<ng-container *ngFor="let component of schema">
<ng-container *ngComponentOutlet="(component['component'] | async); inputs: component.inputs"></ng-container>
</ng-container>
`,
})
export class App {
// schema returned from API call
schema: any = [
{
name: 'Lazy1Component',
inputs: {
title: 'Lazy 1 Component',
},
},
{
name: 'Lazy2Component',
inputs: {
title: 'Lazy 2 Component',
},
},
];
componentManifest: Record<string, { load: () => Promise<unknown> }> = {
Lazy1Component: {
load: () => import('./app/lazy-components/lazy1.component'),
},
Lazy2Component: {
load: () => import('./app/lazy-components/lazy2.component'),
},
};
constructor() {
if (this.schema?.length) {
this.schema.forEach((item: any) => {
item['component'] = this.load(item.name);
});
}
}
load(component: string): any {
return from(
this.componentManifest[component].load().then((c: any) => {
console.log(c[component]);
return (c as any)[component];
})
);
}
}
bootstrapApplication(App);