Skip to content

Commit b28e24f

Browse files
Merge branch 'memory-leak-fixes_contribute-7.4' into memory-leak-fixes_contribute-7.6
# Conflicts: # src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts # src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts # src/app/shared/metadata-representation/metadata-representation-loader.component.ts # src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts # src/app/shared/theme-support/themed.component.ts
2 parents 404ccd9 + 480c7a6 commit b28e24f

7 files changed

Lines changed: 95 additions & 58 deletions

File tree

src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ComponentFactoryResolver, ElementRef, OnInit, ViewChild } from '@angular/core';
1+
import { Component, ElementRef, OnInit, ViewChild, ComponentRef, OnDestroy } from '@angular/core';
22
import { Item } from '../../../../../core/shared/item.model';
33
import { ViewMode } from '../../../../../core/shared/view-mode.model';
44
import {
@@ -14,6 +14,7 @@ import { GenericConstructor } from '../../../../../core/shared/generic-construct
1414
import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive';
1515
import { ThemeService } from '../../../../../shared/theme-support/theme.service';
1616
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
17+
import { hasValue } from '../../../../../shared/empty.util';
1718

1819
@listableObjectComponent(ItemSearchResult, ViewMode.GridElement, Context.AdminSearch)
1920
@Component({
@@ -24,17 +25,18 @@ import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service
2425
/**
2526
* The component for displaying a list element for an item search result on the admin search page
2627
*/
27-
export class ItemAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> implements OnInit {
28+
export class ItemAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> implements OnDestroy, OnInit {
2829
@ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective;
2930
@ViewChild('badges', { static: true }) badges: ElementRef;
3031
@ViewChild('buttons', { static: true }) buttons: ElementRef;
3132

33+
protected compRef: ComponentRef<Component>;
34+
3235
constructor(
3336
public dsoNameService: DSONameService,
3437
protected truncatableService: TruncatableService,
3538
protected bitstreamDataService: BitstreamDataService,
3639
private themeService: ThemeService,
37-
private componentFactoryResolver: ComponentFactoryResolver,
3840
) {
3941
super(dsoNameService, truncatableService, bitstreamDataService);
4042
}
@@ -44,23 +46,32 @@ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridE
4446
*/
4547
ngOnInit(): void {
4648
super.ngOnInit();
47-
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent());
49+
const component: GenericConstructor<Component> = this.getComponent();
4850

4951
const viewContainerRef = this.listableObjectDirective.viewContainerRef;
5052
viewContainerRef.clear();
5153

52-
const componentRef = viewContainerRef.createComponent(
53-
componentFactory,
54-
0,
55-
undefined,
56-
[
57-
[this.badges.nativeElement],
58-
[this.buttons.nativeElement]
59-
]);
60-
(componentRef.instance as any).object = this.object;
61-
(componentRef.instance as any).index = this.index;
62-
(componentRef.instance as any).linkType = this.linkType;
63-
(componentRef.instance as any).listID = this.listID;
54+
this.compRef = viewContainerRef.createComponent(
55+
component, {
56+
index: 0,
57+
injector: undefined,
58+
projectableNodes: [
59+
[this.badges.nativeElement],
60+
[this.buttons.nativeElement],
61+
],
62+
},
63+
);
64+
(this.compRef.instance as any).object = this.object;
65+
(this.compRef.instance as any).index = this.index;
66+
(this.compRef.instance as any).linkType = this.linkType;
67+
(this.compRef.instance as any).listID = this.listID;
68+
}
69+
70+
ngOnDestroy(): void {
71+
if (hasValue(this.compRef)) {
72+
this.compRef.destroy();
73+
this.compRef = undefined;
74+
}
6475
}
6576

6677
/**

src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ComponentFactoryResolver, ElementRef, ViewChild } from '@angular/core';
1+
import { Component, ElementRef, ViewChild, ComponentRef, OnDestroy, OnInit } from '@angular/core';
22
import { Item } from '../../../../../core/shared/item.model';
33
import { ViewMode } from '../../../../../core/shared/view-mode.model';
44
import {
@@ -24,6 +24,7 @@ import { take } from 'rxjs/operators';
2424
import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
2525
import { ThemeService } from '../../../../../shared/theme-support/theme.service';
2626
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
27+
import { hasValue } from '../../../../../shared/empty.util';
2728

2829
@listableObjectComponent(WorkflowItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch)
2930
@Component({
@@ -34,7 +35,7 @@ import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service
3435
/**
3536
* The component for displaying a grid element for an workflow item on the admin workflow search page
3637
*/
37-
export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent<WorkflowItemSearchResult, WorkflowItem> {
38+
export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent<WorkflowItemSearchResult, WorkflowItem> implements OnDestroy, OnInit {
3839
/**
3940
* Directive used to render the dynamic component in
4041
*/
@@ -55,9 +56,10 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S
5556
*/
5657
public item$: Observable<Item>;
5758

59+
protected compRef: ComponentRef<Component>;
60+
5861
constructor(
5962
public dsoNameService: DSONameService,
60-
private componentFactoryResolver: ComponentFactoryResolver,
6163
private linkService: LinkService,
6264
protected truncatableService: TruncatableService,
6365
private themeService: ThemeService,
@@ -75,28 +77,37 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S
7577
this.dso = this.linkService.resolveLink(this.dso, followLink('item'));
7678
this.item$ = (this.dso.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload());
7779
this.item$.pipe(take(1)).subscribe((item: Item) => {
78-
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent(item));
80+
const component: GenericConstructor<Component> = this.getComponent(item);
7981

8082
const viewContainerRef = this.listableObjectDirective.viewContainerRef;
8183
viewContainerRef.clear();
8284

83-
const componentRef = viewContainerRef.createComponent(
84-
componentFactory,
85-
0,
86-
undefined,
87-
[
88-
[this.badges.nativeElement],
89-
[this.buttons.nativeElement]
90-
]);
91-
(componentRef.instance as any).object = item;
92-
(componentRef.instance as any).index = this.index;
93-
(componentRef.instance as any).linkType = this.linkType;
94-
(componentRef.instance as any).listID = this.listID;
95-
componentRef.changeDetectorRef.detectChanges();
85+
this.compRef = viewContainerRef.createComponent(
86+
component, {
87+
index: 0,
88+
injector: undefined,
89+
projectableNodes: [
90+
[this.badges.nativeElement],
91+
[this.buttons.nativeElement],
92+
],
93+
},
94+
);
95+
(this.compRef.instance as any).object = item;
96+
(this.compRef.instance as any).index = this.index;
97+
(this.compRef.instance as any).linkType = this.linkType;
98+
(this.compRef.instance as any).listID = this.listID;
99+
this.compRef.changeDetectorRef.detectChanges();
96100
}
97101
);
98102
}
99103

104+
ngOnDestroy(): void {
105+
if (hasValue(this.compRef)) {
106+
this.compRef.destroy();
107+
this.compRef = undefined;
108+
}
109+
}
110+
100111
/**
101112
* Fetch the component depending on the item's entity type, view mode and context
102113
* @returns {GenericConstructor<Component>}

src/app/shared/loading/themed-loading.component.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, ComponentFactoryResolver, ChangeDetectorRef } from '@angular/core';
1+
import { Component, Input, ChangeDetectorRef } from '@angular/core';
22
import { ThemedComponent } from '../theme-support/themed.component';
33
import { LoadingComponent } from './loading.component';
44
import { ThemeService } from '../theme-support/theme.service';
@@ -20,11 +20,10 @@ export class ThemedLoadingComponent extends ThemedComponent<LoadingComponent> {
2020
protected inAndOutputNames: (keyof LoadingComponent & keyof this)[] = ['message', 'showMessage', 'spinner'];
2121

2222
constructor(
23-
protected resolver: ComponentFactoryResolver,
2423
protected cdr: ChangeDetectorRef,
2524
protected themeService: ThemeService
2625
) {
27-
super(resolver, cdr, themeService);
26+
super(cdr, themeService);
2827
}
2928

3029
protected getComponentName(): string {

src/app/shared/metadata-representation/metadata-representation-loader.component.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ComponentFactoryResolver, Inject, Input, OnInit, ViewChild, OnChanges, SimpleChanges, ComponentRef, ViewContainerRef, ComponentFactory } from '@angular/core';
1+
import { Component, Inject, Input, OnInit, ViewChild, OnChanges, OnDestroy, SimpleChanges, ComponentRef, ViewContainerRef } from '@angular/core';
22
import {
33
MetadataRepresentation,
44
MetadataRepresentationType
@@ -18,8 +18,7 @@ import { ThemeService } from '../theme-support/theme.service';
1818
/**
1919
* Component for determining what component to use depending on the item's entity type (dspace.entity.type), its metadata representation and, optionally, its context
2020
*/
21-
export class MetadataRepresentationLoaderComponent implements OnInit, OnChanges {
22-
21+
export class MetadataRepresentationLoaderComponent implements OnDestroy, OnInit, OnChanges {
2322
/**
2423
* The item or metadata to determine the component for
2524
*/
@@ -55,7 +54,6 @@ export class MetadataRepresentationLoaderComponent implements OnInit, OnChanges
5554
];
5655

5756
constructor(
58-
private componentFactoryResolver: ComponentFactoryResolver,
5957
private themeService: ThemeService,
6058
@Inject(METADATA_REPRESENTATION_COMPONENT_FACTORY) private getMetadataRepresentationComponent: (entityType: string, mdRepresentationType: MetadataRepresentationType, context: Context, theme: string) => GenericConstructor<any>,
6159
) {
@@ -65,7 +63,9 @@ export class MetadataRepresentationLoaderComponent implements OnInit, OnChanges
6563
* Set up the dynamic child component
6664
*/
6765
ngOnInit(): void {
68-
this.instantiateComponent();
66+
if (hasNoValue(this.compRef)) {
67+
this.instantiateComponent();
68+
}
6969
}
7070

7171
/**
@@ -88,12 +88,12 @@ export class MetadataRepresentationLoaderComponent implements OnInit, OnChanges
8888
}
8989

9090
private instantiateComponent(changes?: SimpleChanges): void {
91-
const componentFactory: ComponentFactory<MetadataRepresentationListElementComponent> = this.componentFactoryResolver.resolveComponentFactory(this.getComponent());
91+
const component: GenericConstructor<MetadataRepresentationListElementComponent> = this.getComponent();
9292

9393
const viewContainerRef: ViewContainerRef = this.mdRepDirective.viewContainerRef;
9494
viewContainerRef.clear();
9595

96-
this.compRef = viewContainerRef.createComponent(componentFactory);
96+
this.compRef = viewContainerRef.createComponent(component);
9797

9898
if (hasValue(changes)) {
9999
this.ngOnChanges(changes);
@@ -102,6 +102,13 @@ export class MetadataRepresentationLoaderComponent implements OnInit, OnChanges
102102
}
103103
}
104104

105+
ngOnDestroy(): void {
106+
if (hasValue(this.compRef)) {
107+
this.compRef.destroy();
108+
this.compRef = undefined;
109+
}
110+
}
111+
105112
/**
106113
* Fetch the component depending on the item's entity type, metadata representation type and context
107114
* @returns {string}

src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
22
Component,
3-
ComponentFactoryResolver,
43
EventEmitter,
54
Input,
5+
OnDestroy,
66
OnInit,
77
Output,
88
ViewChild,
@@ -27,7 +27,7 @@ import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-ac
2727
* Component for loading a ClaimedTaskAction component depending on the "option" input
2828
* Passes on the ClaimedTask to the component and subscribes to the processCompleted output
2929
*/
30-
export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges {
30+
export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges, OnDestroy {
3131
/**
3232
* The item object that belonging to the ClaimedTask object
3333
*/
@@ -73,14 +73,13 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges {
7373
'processCompleted',
7474
];
7575

76-
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
77-
}
78-
7976
/**
8077
* Fetch, create and initialize the relevant component
8178
*/
8279
ngOnInit(): void {
83-
this.instantiateComponent();
80+
if (hasNoValue(this.compRef)) {
81+
this.instantiateComponent();
82+
}
8483
}
8584

8685
/**
@@ -105,12 +104,10 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges {
105104
private instantiateComponent(changes?: SimpleChanges): void {
106105
const comp = this.getComponentByWorkflowTaskOption(this.option);
107106
if (hasValue(comp)) {
108-
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp);
109-
110107
const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef;
111108
viewContainerRef.clear();
112109

113-
this.compRef = viewContainerRef.createComponent(componentFactory);
110+
this.compRef = viewContainerRef.createComponent(comp);
114111

115112
if (hasValue(changes)) {
116113
this.ngOnChanges(changes);
@@ -135,4 +132,11 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges {
135132
});
136133
}
137134
}
135+
136+
ngOnDestroy(): void {
137+
if (hasValue(this.compRef)) {
138+
this.compRef.destroy();
139+
this.compRef = undefined;
140+
}
141+
}
138142
}

src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges
125125
* Setup the dynamic child component
126126
*/
127127
ngOnInit(): void {
128-
this.instantiateComponent(this.object);
128+
if (hasNoValue(this.compRef)) {
129+
this.instantiateComponent(this.object);
130+
}
129131
}
130132

131133
/**
@@ -147,7 +149,11 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges
147149
}
148150
}
149151

150-
ngOnDestroy() {
152+
ngOnDestroy(): void {
153+
if (hasValue(this.compRef)) {
154+
this.compRef.destroy();
155+
this.compRef = undefined;
156+
}
151157
this.subs
152158
.filter((subscription) => hasValue(subscription))
153159
.forEach((subscription) => subscription.unsubscribe());

src/app/shared/theme-support/themed.component.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
SimpleChanges,
77
OnInit,
88
OnDestroy,
9-
ComponentFactoryResolver,
109
ChangeDetectorRef,
1110
OnChanges,
1211
HostBinding,
@@ -47,7 +46,6 @@ export abstract class ThemedComponent<T> implements OnInit, OnDestroy, OnChanges
4746
@HostBinding('attr.data-used-theme') usedTheme: string;
4847

4948
constructor(
50-
protected resolver: ComponentFactoryResolver,
5149
protected cdr: ChangeDetectorRef,
5250
protected themeService: ThemeService,
5351
) {
@@ -120,8 +118,9 @@ export abstract class ThemedComponent<T> implements OnInit, OnDestroy, OnChanges
120118
}
121119

122120
this.lazyLoadSub = this.lazyLoadObs.subscribe(([simpleChanges, constructor]: [SimpleChanges, GenericConstructor<T>]) => {
123-
const factory = this.resolver.resolveComponentFactory(constructor);
124-
this.compRef = this.vcr.createComponent(factory, undefined, undefined, [this.themedElementContent.nativeElement.childNodes]);
121+
this.compRef = this.vcr.createComponent(constructor, {
122+
projectableNodes: [this.themedElementContent.nativeElement.childNodes],
123+
});
125124
if (hasValue(simpleChanges)) {
126125
this.ngOnChanges(simpleChanges);
127126
} else {

0 commit comments

Comments
 (0)