在 NgRx 中,如何在遵循最佳实践的同时在整个应用程序状态中保留 Type<any>?

In NgRx How Can I Retain Type<any> Throughout My App State While Following Best Practices?

使用 Angular 11.1.2 和 rxjs 6.6.2

我想制作一个动态显示组件列表的应用程序。这件作品是我独立完成的。我现在面临的问题围绕着通过 NgRx 过渡到状态管理。

我正在使用以下模型:

import { Type } from '@angular/core';

export enum Position { Default, Top, Bottom }

export class NodeModel {
  constructor(
    public type: Type<any>,
    public position: Position
  ) {}
}

它通过一系列 *ngFor@Input() 被馈送到服务中以生成动态组件:

@Component({
  selector: 'app-node-body',
  template: '<ng-container #container></ng-container>',
  styleUrls: ['./node-body.component.scss']
})
export class NodeBodyComponent implements AfterViewInit {
  @ViewChild('container', { read: ViewContainerRef, static: false }) viewRef;
  @Input() type: Type<any>;
  
  constructor(private nodeBodyService: NodeBodyService) { }
  
  ngAfterViewInit(): void {
    this.nodeBodyService.createDynamicComponent(this.type, this.viewRef);
  }
}
@Injectable()
export class NodeBodyService {
  constructor(private cfr: ComponentFactoryResolver) {}
  
  createDynamicComponent<T>(component: Type<T>, viewRef: ViewContainerRef): ComponentRef<any> {
    const factory = this.cfr.resolveComponentFactory<T>(component);
    return viewRef.createComponent(factory);
  }
}

此时应用程序正确生成了动态组件。
在将我的方法切换为使用 NgRx 开始监视我的应用程序状态时,我遇到了问题。以下方法不允许我在状态和相应的 body.component.ts 中都看不到 Type<any> 值,因此动态组件无法呈现。

// node-list.actions.ts
export const SET_NODES = '[Node List] Set Nodes';

export class SetNodes implements Action {
  readonly type = SET_NODES;
  constructor(public payload: NodeModel[]) {}
}

export type NodeListActions = SetNodes;
// node-list.reducer.ts
export interface State {
  nodes: NodeModel[];
}

export function nodeListReducer(
  state: State,
  action: NodeListActions.NodeListActions
) {
  switch (action.type) {
    case NodeListActions.SET_NODES:
      return {
        ...state,
        nodes: action.payload
      };
    default:
      return state;
  }
}
import * as fromApp from './store/app.reducer';
import * as NodeListActions from './node-list/store/node-list.actions';

@Component({
  selector: 'app-root',
  template: '<app-node-list [nodes]="nodes"></app-node-list>',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  nodes: NodeModel[] = [
    new NodeModel(SuperHeaderComponent, Position.Default),
    new NodeModel(IntroductionComponent, Position.Bottom)
  ];
  
  constructor(private store: Store<fromApp.AppState>) {}
  
  ngOnInit(): void {
    this.store.dispatch(new NodeListActions.SetNodes(this.nodes));
  }
}

这是调度 SET_NODES 操作后显示的内容:

{
  nodeList: {
    nodes: [
      {
        position: 0
      },
      {
        position: 2
      }
    ]
  }
}

我认识到在运行时使用 Type<any> 会产生 { position: 0, type: Function },这不是“类似状态”的。所以我尝试了 Map<string, Type<any>> 之类的类型映射,同时更改了 NodeModel

import { Type } from '@angular/core';

export enum Position { Default, Top, Bottom }

export class NodeModel {
  constructor(
    public type: string, // <-- Switched this so that it is visible in state
    public position: Position
  ) {}
}

这确实解决了我现在显示我想在我的应用程序状态中看到的内容的问题,但我仍然无法呈现我的动态组件,因为出现以下错误:

ERROR TypeError: Cannot add property __NG_ELEMENT_ID__, object is not extensible

根据我目前所读的内容,如果我愿意,我似乎可以关闭此错误,但这不是最佳做法。

我是否通过尝试存储 Type<any> 或什至引用类型的想法来正确地接近状态管理?
如果这不是适当的状态管理,我将如何监控动态组件及其各自的状态;因为当我解决这个问题时,我会寻求将状态从动态组件传送回根?

我设法在不牺牲状态不变性的情况下解决了我的问题。我最终出错的地方是假设我没有在我的状态对象中存储 Type<any>

Type<any> 存储在我的应用程序状态中,但由于它是 Function 我无法在 redux devtools 中看到它。我将状态对象调整为以下内容:

export interface Node {
  type: string;
  position: Position;
}

然后我更新了我的服务以使用类型映射:

@Injectable()
export class NodeBodyService {
  
  constructor(private cfr: ComponentFactoryResolver) {}
  
  nodeTypes: Map<string, Type<any>> = new Map([
    [SuperHeaderComponent.name, SuperHeaderComponent],
    [IntroductionComponent.name, IntroductionComponent]
  ]);
  
  createDynamicComponent(component: string, viewRef: ViewContainerRef): void {
    const nodeType: Type<any> = this.nodeTypes.get(component);
    viewRef.createComponent(this.cfr.resolveComponentFactory(nodeType));
  }
}

我现在可以看到呈现的动态组件和相应的状态