#angular #typescript #state #ngrx
#angular #typescript #состояние #ngrx
Вопрос:
Использование 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, чтобы начать мониторинг состояния моего приложения. Следующий подход не позволяет мне видеть Type<any>
значение ни в состоянии, ни в соответствующем body.component.ts
, и, следовательно, динамические компоненты не отображаются.
// 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>
или даже идею ссылки на тип для начала?
Если это не подходит для управления состоянием, как я могу отслеживать динамические компоненты и их соответствующее состояние; когда я решу эту проблему, я буду стремиться к тому, чтобы состояние передавалось из динамических компонентов обратно в корень?
Ответ №1:
Мне удалось решить мою проблему, не жертвуя неизменностью состояния. В итоге я ошибся, предполагая, что я не сохранил Type<any>
в своем объекте состояния.
Type<any>
был сохранен в моем состоянии приложения, но из-за того, что он был a 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));
}
}
Теперь я могу видеть визуализацию моих динамических компонентов и соответствующее состояние