有没有办法在Angular中动态插入现有组件?
我有一种情况,Angular认为它需要重新创建组件,而不仅仅是使用旧组件,这会导致组件在拖动到界面中的另一个位置后“刷新”。我有一个树,从中生成组件。以下是my tree的简化版本:有没有办法在Angular中动态插入现有组件?,angular,Angular,我有一种情况,Angular认为它需要重新创建组件,而不仅仅是使用旧组件,这会导致组件在拖动到界面中的另一个位置后“刷新”。我有一个树,从中生成组件。以下是my tree的简化版本: { "left": { "type": "FirstComponent" }, "right": { "left": { "type": "SecondComponent" }, "right": { "type": "ThirdComponent
{
"left": {
"type": "FirstComponent"
},
"right": {
"left": {
"type": "SecondComponent"
},
"right": {
"type": "ThirdComponent"
}
}
}
将零部件拖动到另一个位置后:
{
"left": {
"left": {
"type": "FirstComponent"
},
"right": {
"type": "ThirdComponent"
}
},
"right": {
"type": "SecondComponent"
}
}
我在DockComponent中动态插入组件,如下所示:
private panelRef: ComponentRef<any>;
ngOnChanges() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.panel.type);
this.dynamicInsert.clear();
this.panelRef = this.dynamicInsert.createComponent(componentFactory);
}
不幸的是,这不起作用,因为我的组件是使用如下树递归创建的:
<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>
[edit3]
终于成功了。我正在使用AfterViewChecked钩子查看是否已初始化面板。如果没有,我们就重新使用旧的。AfterViewChecked给出了更改检测的问题,所以您需要调用detectChanges()!接受伊利亚的回答,因为它帮助我找到了答案
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();
}
ngOnInit():void{
//分离dockTree中的更改面板
this.dockTreeSub=this.dockerService.getTreeChange().subscribe((值)=>{
如果(值!=''){
如果(this.dynamicSert.length>0){
这个.detachPanel();
}
}
});
如果(!this.panel.ref&&this.dynamicInsert.length@组件({
模板:“动态”,
})
导出类DynamicComponent{}
@组成部分({
选择器:“你好”,
模板:`
交换`,
})
导出类HelloComponent{
@ViewChild('firstContainer',{read:ViewContainerRef})firstContainer:ViewContainerRef;
@ViewChild('secondContainer',{read:ViewContainerRef})secondContainer:ViewContainerRef;
工厂;
构造函数(解析程序:ComponentFactoryResolver){
this.factory=resolver.resolveComponentFactory(DynamicComponent);
}
恩戈尼尼特(){
this.firstContainer.createComponent(this.factory);
this.secondContainer.createComponent(this.factory);
}
互换{
const first=this.firstContainer.get(0);
const second=this.secondContainer.get(0);
此.firstContainer.detach(0);
此.secondContainer.detach(0);
this.firstContainer.insert(第二个);
此.secondContainer.insert(第一个);
}
}
我尝试过这样做。问题是我正在使用树递归创建插入视图的组件。当ngOnInit为我的DockComponent启动时,旧的ViewRef已全部销毁。我无法像这样进行真正的交换,因为交换并不总是发生在同一个组件中。答案应该包括more不仅仅是一个代码片段。解释为什么你的建议有效,为什么OP的代码无效,以及两者的不同之处。这需要更多的挖掘,但这是答案的核心。解决方案很复杂。如果在销毁组件之前将ViewRef
与当前ViewContainerRef
分离会怎么样(在Ngondestory中)?这样,ViewRef
就不应该被销毁。并且在父组件的ngondestory
中销毁它们以防止内存泄漏。为什么面板没有初始化?我订阅了一个可观察的,如果我更改树,我会调用它。因此每个面板都会分离它们的ViewRef。每个实际有更改的面板都会经过init和所有内容,但并非所有面板都由更改检测触发。
<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>
// 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);
}
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();
}
@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);
}
}