Skip to content

Commit e17a74c

Browse files
refactor/AB#82357_update-angular-fix-memory-leak-issues-and-other-fixes fix: add missing subscription teardown logic part 1
1 parent 0a42b98 commit e17a74c

File tree

24 files changed

+383
-180
lines changed

24 files changed

+383
-180
lines changed

apps/back-office/src/app/application/pages/add-page/add-page.component.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,28 @@ export class AddPageComponent extends UnsubscribeComponent implements OnInit {
6868
}
6969

7070
ngOnInit(): void {
71-
this.pageForm.get('type')?.valueChanges.subscribe((type) => {
72-
const contentControl = this.pageForm.controls.content;
73-
if (type === ContentType.form) {
74-
this.formsQuery = this.apollo.watchQuery<FormsQueryResponse>({
75-
query: GET_FORMS,
76-
variables: {
77-
first: ITEMS_PER_PAGE,
78-
sortField: 'name',
79-
},
80-
});
81-
contentControl.setValidators([Validators.required]);
82-
contentControl.updateValueAndValidity();
83-
} else {
84-
contentControl.setValidators(null);
85-
contentControl.setValue(null);
86-
contentControl.updateValueAndValidity();
87-
}
88-
this.onNext();
89-
});
71+
this.pageForm
72+
.get('type')
73+
?.valueChanges.pipe(takeUntil(this.destroy$))
74+
.subscribe((type) => {
75+
const contentControl = this.pageForm.controls.content;
76+
if (type === ContentType.form) {
77+
this.formsQuery = this.apollo.watchQuery<FormsQueryResponse>({
78+
query: GET_FORMS,
79+
variables: {
80+
first: ITEMS_PER_PAGE,
81+
sortField: 'name',
82+
},
83+
});
84+
contentControl.setValidators([Validators.required]);
85+
contentControl.updateValueAndValidity();
86+
} else {
87+
contentControl.setValidators(null);
88+
contentControl.setValue(null);
89+
contentControl.updateValueAndValidity();
90+
}
91+
this.onNext();
92+
});
9093

9194
// Set the available widgets that can directly be added as single widget dashboard
9295
this.availableWidgets = this.availableWidgets.filter((widget: any) => {
Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Component, OnInit } from '@angular/core';
22
import { Router } from '@angular/router';
3-
import { AuthService } from '@oort-front/shared';
3+
import { AuthService, UnsubscribeComponent } from '@oort-front/shared';
4+
import { takeUntil } from 'rxjs';
45

56
/**
67
* Login Page component.
@@ -10,23 +11,27 @@ import { AuthService } from '@oort-front/shared';
1011
templateUrl: './login.component.html',
1112
styleUrls: ['./login.component.scss'],
1213
})
13-
export class LoginComponent implements OnInit {
14+
export class LoginComponent extends UnsubscribeComponent implements OnInit {
1415
/**
1516
* Login Page component.
1617
*
1718
* @param authService Shared authentication service
1819
* @param router Angular router
1920
*/
20-
constructor(private authService: AuthService, private router: Router) {}
21+
constructor(private authService: AuthService, private router: Router) {
22+
super();
23+
}
2124

2225
/**
2326
* Check that user is authenticated, and redirect to main page if true.
2427
*/
2528
ngOnInit(): void {
26-
this.authService.isAuthenticated$.subscribe((res: boolean) => {
27-
if (res === true) {
28-
this.router.navigate(['/applications']);
29-
}
30-
});
29+
this.authService.isAuthenticated$
30+
.pipe(takeUntil(this.destroy$))
31+
.subscribe((res: boolean) => {
32+
if (res === true) {
33+
this.router.navigate(['/applications']);
34+
}
35+
});
3136
}
3237
}

apps/back-office/src/app/components/add-form-modal/add-form-modal.component.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import {
2121
Form,
2222
ResourceQueryResponse,
2323
ResourceSelectComponent,
24+
UnsubscribeComponent,
2425
} from '@oort-front/shared';
26+
import { takeUntil } from 'rxjs';
2527

2628
/**
2729
* Add form component (modal)
@@ -49,7 +51,10 @@ import {
4951
templateUrl: './add-form-modal.component.html',
5052
styleUrls: ['./add-form-modal.component.scss'],
5153
})
52-
export class AddFormModalComponent implements OnInit {
54+
export class AddFormModalComponent
55+
extends UnsubscribeComponent
56+
implements OnInit
57+
{
5358
/** Form group */
5459
public form = this.fb.group({
5560
name: ['', Validators.required],
@@ -83,27 +88,33 @@ export class AddFormModalComponent implements OnInit {
8388
private fb: FormBuilder,
8489
public dialogRef: DialogRef<AddFormModalComponent>,
8590
private apollo: Apollo
86-
) {}
91+
) {
92+
super();
93+
}
8794

8895
/** Load the resources and build the form. */
8996
ngOnInit(): void {
90-
this.form.get('newResource')?.valueChanges.subscribe((value: boolean) => {
91-
if (value) {
92-
this.form.get('resource')?.clearValidators();
93-
this.form.patchValue({
94-
resource: null,
95-
inheritsTemplate: false,
96-
template: null,
97-
});
98-
} else {
99-
this.form.get('resource')?.setValidators([Validators.required]);
100-
}
101-
this.form.get('resource')?.updateValueAndValidity();
102-
});
97+
this.form
98+
.get('newResource')
99+
?.valueChanges.pipe(takeUntil(this.destroy$))
100+
.subscribe((value: boolean) => {
101+
if (value) {
102+
this.form.get('resource')?.clearValidators();
103+
this.form.patchValue({
104+
resource: null,
105+
inheritsTemplate: false,
106+
template: null,
107+
});
108+
} else {
109+
this.form.get('resource')?.setValidators([Validators.required]);
110+
}
111+
this.form.get('resource')?.updateValueAndValidity();
112+
});
103113

104114
this.form
105115
.get('inheritsTemplate')
106-
?.valueChanges.subscribe((value: boolean) => {
116+
?.valueChanges.pipe(takeUntil(this.destroy$))
117+
.subscribe((value: boolean) => {
107118
if (value) {
108119
this.form.get('template')?.setValidators([Validators.required]);
109120
} else {
@@ -117,7 +128,8 @@ export class AddFormModalComponent implements OnInit {
117128

118129
this.form
119130
.get('resource')
120-
?.valueChanges.subscribe((value: string | null) => {
131+
?.valueChanges.pipe(takeUntil(this.destroy$))
132+
.subscribe((value: string | null) => {
121133
if (value) {
122134
this.getResource(value);
123135
} else {

apps/back-office/src/app/dashboard/pages/api-configuration/api-configuration.component.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import { Component, OnDestroy, OnInit } from '@angular/core';
22
import { AbstractControl, FormBuilder, Validators } from '@angular/forms';
33
import { ActivatedRoute, Router } from '@angular/router';
44
import { TranslateService } from '@ngx-translate/core';
@@ -18,6 +18,7 @@ import { apiValidator } from '../../../utils/nameValidation';
1818
import { EDIT_API_CONFIGURATION } from './graphql/mutations';
1919
import { GET_API_CONFIGURATION } from './graphql/queries';
2020
import { SnackbarService } from '@oort-front/ui';
21+
import { Subscription } from 'rxjs';
2122

2223
/**
2324
* Default value shown for private settings fields
@@ -34,7 +35,7 @@ const ENCRYPTED_VALUE = '●●●●●●●●●●●●●';
3435
})
3536
export class ApiConfigurationComponent
3637
extends UnsubscribeComponent
37-
implements OnInit
38+
implements OnInit, OnDestroy
3839
{
3940
/** Loading indicator */
4041
public loading = true;
@@ -52,6 +53,8 @@ export class ApiConfigurationComponent
5253
public authType = authType;
5354
/** Available auth types */
5455
public authTypeChoices = Object.values(authType);
56+
/** Form auth type subscription listener */
57+
private authTypeSubscription!: Subscription;
5558

5659
/** @returns API configuration name */
5760
get name(): AbstractControl | null {
@@ -103,11 +106,16 @@ export class ApiConfigurationComponent
103106
this.apiConfiguration.name as string
104107
);
105108
this.apiForm = this.createApiForm(data.apiConfiguration);
106-
this.apiForm.controls.authType?.valueChanges
107-
.pipe(takeUntil(this.destroy$))
108-
.subscribe((value) => {
109-
this.resetFormSettings(value);
110-
});
109+
// Reset auth type subscription on same component instance to avoid keeping multiple opened subscriptions
110+
if (this.authTypeSubscription) {
111+
this.authTypeSubscription.unsubscribe();
112+
}
113+
this.authTypeSubscription =
114+
this.apiForm.controls.authType?.valueChanges
115+
.pipe(takeUntil(this.destroy$))
116+
.subscribe((value) => {
117+
this.resetFormSettings(value);
118+
});
111119
this.loading = loading;
112120
} else {
113121
this.snackBar.openSnackBar(
@@ -376,4 +384,11 @@ export class ApiConfigurationComponent
376384
control.setValue('');
377385
}
378386
}
387+
388+
override ngOnDestroy(): void {
389+
super.ngOnDestroy();
390+
if (this.authTypeSubscription) {
391+
this.authTypeSubscription.unsubscribe();
392+
}
393+
}
379394
}

apps/back-office/src/app/dashboard/pages/applications/components/chose-role/chose-role.component.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ import { Apollo, QueryRef } from 'apollo-angular';
22
import { Component, Inject, OnInit } from '@angular/core';
33
import { Validators, FormBuilder } from '@angular/forms';
44
import { GET_ROLES } from '../../graphql/queries';
5-
import { Role, RolesQueryResponse } from '@oort-front/shared';
5+
import {
6+
Role,
7+
RolesQueryResponse,
8+
UnsubscribeComponent,
9+
} from '@oort-front/shared';
610
import { DialogRef, DIALOG_DATA } from '@angular/cdk/dialog';
11+
import { takeUntil } from 'rxjs';
712

813
/**
914
* Chose role component, to preview application with selected role.
@@ -13,7 +18,7 @@ import { DialogRef, DIALOG_DATA } from '@angular/cdk/dialog';
1318
templateUrl: './chose-role.component.html',
1419
styleUrls: ['./chose-role.component.scss'],
1520
})
16-
export class ChoseRoleComponent implements OnInit {
21+
export class ChoseRoleComponent extends UnsubscribeComponent implements OnInit {
1722
/** Available roles */
1823
public roles: Role[] = [];
1924
/** Loading indicator */
@@ -42,7 +47,9 @@ export class ChoseRoleComponent implements OnInit {
4247
public data: {
4348
application: string;
4449
}
45-
) {}
50+
) {
51+
super();
52+
}
4653

4754
ngOnInit(): void {
4855
this.rolesQuery = this.apollo.watchQuery<RolesQueryResponse>({
@@ -52,8 +59,10 @@ export class ChoseRoleComponent implements OnInit {
5259
},
5360
});
5461

55-
this.rolesQuery.valueChanges.subscribe(({ loading }) => {
56-
this.loading = loading;
57-
});
62+
this.rolesQuery.valueChanges
63+
.pipe(takeUntil(this.destroy$))
64+
.subscribe(({ loading }) => {
65+
this.loading = loading;
66+
});
5867
}
5968
}

apps/back-office/src/app/dashboard/pages/form-answer/form-answer.component.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import {
66
FormQueryResponse,
77
BreadcrumbService,
88
FormComponent,
9+
UnsubscribeComponent,
910
} from '@oort-front/shared';
1011
import { GET_SHORT_FORM_BY_ID } from './graphql/queries';
12+
import { takeUntil } from 'rxjs';
1113

1214
/**
1315
* Form answer page component.
@@ -17,7 +19,10 @@ import { GET_SHORT_FORM_BY_ID } from './graphql/queries';
1719
templateUrl: './form-answer.component.html',
1820
styleUrls: ['./form-answer.component.scss'],
1921
})
20-
export class FormAnswerComponent implements OnInit {
22+
export class FormAnswerComponent
23+
extends UnsubscribeComponent
24+
implements OnInit
25+
{
2126
/** Reference to shared form component */
2227
@ViewChild(FormComponent)
2328
private formComponent?: FormComponent;
@@ -41,7 +46,9 @@ export class FormAnswerComponent implements OnInit {
4146
private apollo: Apollo,
4247
private route: ActivatedRoute,
4348
private breadcrumbService: BreadcrumbService
44-
) {}
49+
) {
50+
super();
51+
}
4552

4653
ngOnInit(): void {
4754
this.id = this.route.snapshot.paramMap.get('id') || '';
@@ -53,7 +60,8 @@ export class FormAnswerComponent implements OnInit {
5360
id: this.id,
5461
},
5562
})
56-
.valueChanges.subscribe(({ data, loading }) => {
63+
.valueChanges.pipe(takeUntil(this.destroy$))
64+
.subscribe(({ data, loading }) => {
5765
this.loading = loading;
5866
this.form = data.form;
5967
this.breadcrumbService.setBreadcrumb(

0 commit comments

Comments
 (0)