Destroy dynamically generated components in onDestroy & replace deprecated createComponent

This commit is contained in:
Alexandre Vryghem
2023-12-28 20:42:21 +01:00
parent b28e24fda3
commit 6497a156aa
5 changed files with 78 additions and 54 deletions

View File

@@ -61,10 +61,10 @@ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridE
], ],
}, },
); );
(this.compRef.instance as any).object = this.object; this.compRef.setInput('object',this.object);
(this.compRef.instance as any).index = this.index; this.compRef.setInput('index', this.index);
(this.compRef.instance as any).linkType = this.linkType; this.compRef.setInput('linkType', this.linkType);
(this.compRef.instance as any).listID = this.listID; this.compRef.setInput('listID', this.listID);
} }
ngOnDestroy(): void { ngOnDestroy(): void {

View File

@@ -77,28 +77,27 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S
this.dso = this.linkService.resolveLink(this.dso, followLink('item')); this.dso = this.linkService.resolveLink(this.dso, followLink('item'));
this.item$ = (this.dso.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()); this.item$ = (this.dso.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload());
this.item$.pipe(take(1)).subscribe((item: Item) => { this.item$.pipe(take(1)).subscribe((item: Item) => {
const component: GenericConstructor<Component> = this.getComponent(item); const component: GenericConstructor<Component> = this.getComponent(item);
const viewContainerRef = this.listableObjectDirective.viewContainerRef; const viewContainerRef = this.listableObjectDirective.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
this.compRef = viewContainerRef.createComponent( this.compRef = viewContainerRef.createComponent(
component, { component, {
index: 0, index: 0,
injector: undefined, injector: undefined,
projectableNodes: [ projectableNodes: [
[this.badges.nativeElement], [this.badges.nativeElement],
[this.buttons.nativeElement], [this.buttons.nativeElement],
], ],
}, },
); );
(this.compRef.instance as any).object = item; this.compRef.setInput('object', item);
(this.compRef.instance as any).index = this.index; this.compRef.setInput('index', this.index);
(this.compRef.instance as any).linkType = this.linkType; this.compRef.setInput('linkType', this.linkType);
(this.compRef.instance as any).listID = this.listID; this.compRef.setInput('listID', this.listID);
this.compRef.changeDetectorRef.detectChanges(); this.compRef.changeDetectorRef.detectChanges();
} });
);
} }
ngOnDestroy(): void { ngOnDestroy(): void {

View File

@@ -1,4 +1,4 @@
import { Component, ComponentFactoryResolver, ElementRef, ViewChild, OnInit } from '@angular/core'; import { Component, ElementRef, ViewChild, OnInit, OnDestroy, ComponentRef } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { map, mergeMap, take, tap } from 'rxjs/operators'; import { map, mergeMap, take, tap } from 'rxjs/operators';
@@ -37,6 +37,7 @@ import { SupervisionOrder } from '../../../../../core/supervision-order/models/s
import { PaginatedList } from '../../../../../core/data/paginated-list.model'; import { PaginatedList } from '../../../../../core/data/paginated-list.model';
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service'; import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
import { hasValue } from '../../../../../shared/empty.util';
@listableObjectComponent(WorkspaceItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch) @listableObjectComponent(WorkspaceItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch)
@Component({ @Component({
@@ -47,7 +48,7 @@ import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service
/** /**
* The component for displaying a grid element for an workflow item on the admin workflow search page * The component for displaying a grid element for an workflow item on the admin workflow search page
*/ */
export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent<WorkspaceItemSearchResult, WorkspaceItem> implements OnInit { export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent<WorkspaceItemSearchResult, WorkspaceItem> implements OnDestroy, OnInit {
/** /**
* The item linked to the workspace item * The item linked to the workspace item
@@ -79,9 +80,13 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends
*/ */
@ViewChild('buttons', { static: true }) buttons: ElementRef; @ViewChild('buttons', { static: true }) buttons: ElementRef;
/**
* The reference to the dynamic component
*/
protected compRef: ComponentRef<Component>;
constructor( constructor(
public dsoNameService: DSONameService, public dsoNameService: DSONameService,
private componentFactoryResolver: ComponentFactoryResolver,
private linkService: LinkService, private linkService: LinkService,
protected truncatableService: TruncatableService, protected truncatableService: TruncatableService,
private themeService: ThemeService, private themeService: ThemeService,
@@ -100,24 +105,24 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends
this.dso = this.linkService.resolveLink(this.dso, followLink('item')); this.dso = this.linkService.resolveLink(this.dso, followLink('item'));
this.item$ = (this.dso.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()); this.item$ = (this.dso.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload());
this.item$.pipe(take(1)).subscribe((item: Item) => { this.item$.pipe(take(1)).subscribe((item: Item) => {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent(item)); const component: GenericConstructor<Component> = this.getComponent(item);
const viewContainerRef = this.listableObjectDirective.viewContainerRef; const viewContainerRef = this.listableObjectDirective.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent( this.compRef = viewContainerRef.createComponent(
componentFactory, component, {
0, index: 0,
undefined, projectableNodes: [
[
[this.badges.nativeElement], [this.badges.nativeElement],
[this.buttons.nativeElement] [this.buttons.nativeElement]
]); ],
(componentRef.instance as any).object = item; });
(componentRef.instance as any).index = this.index; this.compRef.setInput('object', item);
(componentRef.instance as any).linkType = this.linkType; this.compRef.setInput('index', this.index);
(componentRef.instance as any).listID = this.listID; this.compRef.setInput('linkType', this.linkType);
componentRef.changeDetectorRef.detectChanges(); this.compRef.setInput('listID', this.listID);
this.compRef.changeDetectorRef.detectChanges();
} }
); );
@@ -130,6 +135,13 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends
}); });
} }
ngOnDestroy(): void {
if (hasValue(this.compRef)) {
this.compRef.destroy();
this.compRef = undefined;
}
}
/** /**
* Fetch the component depending on the item's entity type, view mode and context * Fetch the component depending on the item's entity type, view mode and context
* @returns {GenericConstructor<Component>} * @returns {GenericConstructor<Component>}

View File

@@ -1,5 +1,4 @@
import { import {
ComponentFactoryResolver,
ComponentRef, ComponentRef,
Directive, Directive,
Input, Input,
@@ -12,6 +11,7 @@ import { PlacementArray } from '@ng-bootstrap/ng-bootstrap/util/positioning';
import { ContextHelpWrapperComponent } from './context-help-wrapper/context-help-wrapper.component'; import { ContextHelpWrapperComponent } from './context-help-wrapper/context-help-wrapper.component';
import { PlacementDir } from './context-help-wrapper/placement-dir.model'; import { PlacementDir } from './context-help-wrapper/placement-dir.model';
import { ContextHelpService } from './context-help.service'; import { ContextHelpService } from './context-help.service';
import { hasValue } from './empty.util';
export interface ContextHelpDirectiveInput { export interface ContextHelpDirectiveInput {
content: string; content: string;
@@ -43,7 +43,6 @@ export class ContextHelpDirective implements OnChanges, OnDestroy {
constructor( constructor(
private templateRef: TemplateRef<any>, private templateRef: TemplateRef<any>,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
private componentFactoryResolver: ComponentFactoryResolver,
private contextHelpService: ContextHelpService private contextHelpService: ContextHelpService
) {} ) {}
@@ -53,19 +52,21 @@ export class ContextHelpDirective implements OnChanges, OnDestroy {
this.contextHelpService.add({id: this.dsContextHelp.id, isTooltipVisible: false}); this.contextHelpService.add({id: this.dsContextHelp.id, isTooltipVisible: false});
if (this.wrapper === undefined) { if (this.wrapper === undefined) {
const factory this.wrapper = this.viewContainerRef.createComponent(ContextHelpWrapperComponent);
= this.componentFactoryResolver.resolveComponentFactory(ContextHelpWrapperComponent);
this.wrapper = this.viewContainerRef.createComponent(factory);
} }
this.wrapper.instance.templateRef = this.templateRef; this.wrapper.setInput('templateRef', this.templateRef);
this.wrapper.instance.content = this.dsContextHelp.content; this.wrapper.setInput('content', this.dsContextHelp.content);
this.wrapper.instance.id = this.dsContextHelp.id; this.wrapper.setInput('id', this.dsContextHelp.id);
this.wrapper.instance.tooltipPlacement = this.dsContextHelp.tooltipPlacement; this.wrapper.setInput('tooltipPlacement', this.dsContextHelp.tooltipPlacement);
this.wrapper.instance.iconPlacement = this.dsContextHelp.iconPlacement; this.wrapper.setInput('iconPlacement', this.dsContextHelp.iconPlacement);
} }
ngOnDestroy() { ngOnDestroy() {
this.clearMostRecentId(); this.clearMostRecentId();
if (hasValue(this.wrapper)) {
this.wrapper.destroy();
this.wrapper = undefined;
}
} }
private clearMostRecentId(): void { private clearMostRecentId(): void {

View File

@@ -1,4 +1,4 @@
import { Component, Input, ViewChild, ComponentFactoryResolver, OnInit } from '@angular/core'; import { Component, Input, ViewChild, OnInit, ComponentRef, OnDestroy } from '@angular/core';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { import {
getAdvancedComponentByWorkflowTaskOption getAdvancedComponentByWorkflowTaskOption
@@ -15,7 +15,7 @@ import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths';
templateUrl: './advanced-workflow-actions-loader.component.html', templateUrl: './advanced-workflow-actions-loader.component.html',
styleUrls: ['./advanced-workflow-actions-loader.component.scss'], styleUrls: ['./advanced-workflow-actions-loader.component.scss'],
}) })
export class AdvancedWorkflowActionsLoaderComponent implements OnInit { export class AdvancedWorkflowActionsLoaderComponent implements OnDestroy, OnInit {
/** /**
* The name of the type to render * The name of the type to render
@@ -28,8 +28,12 @@ export class AdvancedWorkflowActionsLoaderComponent implements OnInit {
*/ */
@ViewChild(AdvancedWorkflowActionsDirective, { static: true }) claimedTaskActionsDirective: AdvancedWorkflowActionsDirective; @ViewChild(AdvancedWorkflowActionsDirective, { static: true }) claimedTaskActionsDirective: AdvancedWorkflowActionsDirective;
/**
* The reference to the dynamic component
*/
protected compRef: ComponentRef<Component>;
constructor( constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private router: Router, private router: Router,
) { ) {
} }
@@ -40,16 +44,24 @@ export class AdvancedWorkflowActionsLoaderComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
const comp = this.getComponentByWorkflowTaskOption(this.type); const comp = this.getComponentByWorkflowTaskOption(this.type);
if (hasValue(comp)) { if (hasValue(comp)) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp);
const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
viewContainerRef.createComponent(componentFactory); this.compRef = viewContainerRef.createComponent(comp);
} else { } else {
void this.router.navigate([PAGE_NOT_FOUND_PATH]); void this.router.navigate([PAGE_NOT_FOUND_PATH]);
} }
} }
/**
* Destroy the dynamically created component
*/
ngOnDestroy(): void {
if (hasValue(this.compRef)) {
this.compRef.destroy();
this.compRef = undefined;
}
}
getComponentByWorkflowTaskOption(type: string): any { getComponentByWorkflowTaskOption(type: string): any {
return getAdvancedComponentByWorkflowTaskOption(type); return getAdvancedComponentByWorkflowTaskOption(type);
} }