我遇到一种情况,Angular 认为它需要重新创建组件,而不仅仅是使用旧组件,这会导致组件在将组件拖到界面中的另一个位置后“刷新”。我有一棵树,我可以从中生成组件。以下是我的树的简化版本:
{
"left": {
"type": "FirstComponent"
},
"right": {
"left": {
"type": "SecondComponent"
},
"right": {
"type": "ThirdComponent"
}
}
}
Run Code Online (Sandbox Code Playgroud)
将组件拖动到另一个位置后:
{
"left": {
"left": {
"type": "FirstComponent"
},
"right": {
"type": "ThirdComponent"
}
},
"right": {
"type": "SecondComponent"
}
}
Run Code Online (Sandbox Code Playgroud)
我将组件动态插入到我的 DockComponent 中,如下所示:
private panelRef: ComponentRef<any>;
ngOnChanges() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.panel.type);
this.dynamicInsert.clear();
this.panelRef = this.dynamicInsert.createComponent(componentFactory);
}
Run Code Online (Sandbox Code Playgroud)
每当我更改树时,树都会重新生成,因此组件是从头开始构建的。我尝试将 this.panelRef.instance 保存在我的 dockTree 中,但 this.panelRef.instance 是只读的,所以我无法用它做任何事情。我尝试保存 this.panelRef.instance 并将 this.panelRef.instance 的所有属性设置为已保存实例的属性。这让一切都变得一团糟。
有没有办法在 Angular 中动态插入现有组件?
[编辑]
根据 Ilia 的回答,我尝试将 ViewRefs 保存在我的树中。
export class DockTreeContainer extends DockTreeNode {
private orientation: Orientation;
private left: DockTreeNode;
private right: DockTreeNode;
}
export class DockTreePanel extends DockTreeNode {
type: Type<DockablePanel>;
ref?: ViewRef;
}
Run Code Online (Sandbox Code Playgroud)
该树存在 DockTreeNode,它可以是包含两个 DockTreeNode 的 DockTreeContainer,也可以是包含面板类型的 DockTreePanel。我添加了 ViewRef,并将其设置在我的 DockComponent 中:
ngOnInit() {
if (this.panel.ref) {
this.insertExistingComponent();
} else {
this.createNewComponent();
}
}
private createNewComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.panel.type);
this.panelRef = this.dynamicInsert.createComponent(componentFactory);
this.initializePanel(this.panelRef.instance);
this.panelRef.instance['panel'] = this.panel;
this.panel.ref = this.dynamicInsert.get(0);
}
private insertExistingComponent() {
this.dynamicInsert.clear();
this.dynamicInsert.insert(this.panel.ref);
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不起作用,因为我的组件是使用树递归创建的,如下所示:
<div class="dock-orientation {{getOrientation()}}">
<div *ngIf="isPanel(dockTree.getLeft()); then leftDock else leftContainer"></div>
<div *ngIf="isPanel(dockTree.getRight()); then rightDock else rightContainer"></div>
</div>
<ng-template #leftDock>
<ct-dock class="dock-node dock" [panel]="leftAsPanel()"></ct-dock>
</ng-template>
<ng-template #rightDock>
<ct-dock class="dock-node dock" [panel]="rightAsPanel()"></ct-dock>
</ng-template>
<ng-template #leftContainer>
<ct-dock-container class="dock-node dock-container" [dockTree]="leftAsContainer()"></ct-dock-container>
</ng-template>
<ng-template #rightContainer>
<ct-dock-container class="dock-node dock-container" [dockTree]="rightAsContainer()"></ct-dock-container>
</ng-template>
Run Code Online (Sandbox Code Playgroud)
移动其中一个面板可以将其放置在树中完全不同的位置,这会导致触发 Angular 的更改检测。当 DockComponent 中的 ngOnInit 被调用时,我试图恢复的保存的引用已经被破坏了。
是否还有办法解决这个问题,或者我应该尝试将所有递归添加到一个组件中,同时希望我的引用不会被破坏?
[编辑2]
因此,我到达了可以在一定时间内实际分离组件的部分,以便稍后可以重新连接它们。然而,所有面板都被分离,但并非所有面板都在初始化,导致一些面板在移动其他面板时消失。
// getTreeChange is an Observable that gets triggered after each change in the dockTree, which detaches all the panels in the application.
ngOnInit(): void {
// Detach our panel on changes in the dockTree
this.dockTreeSub = this.dockerService.getTreeChange().subscribe((value) => {
if (value !== '') {
if (this.dynamicInsert.length > 0) {
this.detachPanel();
}
}
});
}
ngAfterViewInit(): void {
if (this.panel.ref) {
this.insertExistingComponent();
} else {
this.createNewComponent();
}
}
/**
* Creates a new component and inserts it into the Dock
*/
private createNewComponent() {
console.log('No panel ref found, creating new component');
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.panel.type);
this.panelRef = this.dynamicInsert.createComponent(componentFactory);
this.initializePanel(this.panelRef.instance);
this.panelRef.instance['panel'] = this.panel;
}
/**
* Takes the old ViewRef from memory and inserts that into the Dock
*/
private insertExistingComponent() {
if (this.panel.ref.destroyed) {
throw new Error('Found destroyed panel');
}
console.log('Found intact panel, inserting');
this.dynamicInsert.clear();
this.dynamicInsert.insert(this.panel.ref);
}
/**
* Detach the panel from this Dock and save it in the DockTree
*/
private detachPanel() {
console.log('Detaching!');
this.panel.ref = this.dynamicInsert.get(0);
this.dynamicInsert.detach(0);
}
Run Code Online (Sandbox Code Playgroud)
[编辑3]
终于让这个工作了。我正在使用 AfterViewChecked 挂钩来查看我们是否已经初始化了面板。如果没有,我们就重新使用旧的。AfterViewChecked 在更改检测方面出现问题,因此您需要调用 detectorChanges()!接受伊利亚的回答,因为它帮助我弄清楚了。
ngOnInit(): void {
// Detach our panel on changes in the dockTree
this.dockTreeSub = this.dockerService.getTreeChange().subscribe((value) => {
if (value !== '') {
if (this.dynamicInsert.length > 0) {
this.detachPanel();
}
}
});
if (!this.panel.ref && this.dynamicInsert.length <= 0) {
this.createNewComponent();
}
}
/**
* Check if the panel needs to be re-inserted after moving panels
*/
ngAfterViewChecked(): void {
if (this.panel.ref && this.dynamicInsert.length <= 0) {
this.insertExistingComponent();
}
this.changeDetector.detectChanges();
}
Run Code Online (Sandbox Code Playgroud)
@Component({
template: 'dynamic <input>',
})
export class DynamicComponent {}
@Component({
selector: 'hello',
template: `
<ng-container #firstContainer></ng-container>
<ng-container #secondContainer></ng-container>
<button (click)="swap()">swap</button>`,
})
export class HelloComponent {
@ViewChild('firstContainer', {read: ViewContainerRef}) firstContainer: ViewContainerRef;
@ViewChild('secondContainer', {read: ViewContainerRef}) secondContainer: ViewContainerRef;
factory;
constructor(resolver: ComponentFactoryResolver) {
this.factory = resolver.resolveComponentFactory(DynamicComponent);
}
ngOnInit() {
this.firstContainer.createComponent(this.factory);
this.secondContainer.createComponent(this.factory);
}
swap() {
const first = this.firstContainer.get(0);
const second = this.secondContainer.get(0);
this.firstContainer.detach(0);
this.secondContainer.detach(0);
this.firstContainer.insert(second);
this.secondContainer.insert(first);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2527 次 |
| 最近记录: |