diff --git a/src/app/organization/holidays/create-holiday/create-holiday.component.ts b/src/app/organization/holidays/create-holiday/create-holiday.component.ts index 6926740597..1468012dda 100644 --- a/src/app/organization/holidays/create-holiday/create-holiday.component.ts +++ b/src/app/organization/holidays/create-holiday/create-holiday.component.ts @@ -1,6 +1,6 @@ /** Angular Imports. */ import { SelectionModel } from '@angular/cdk/collections'; -import { Component, OnInit, ViewChild, Injectable } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, Injectable } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, @@ -9,6 +9,8 @@ import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { Dates } from 'app/core/utils/dates'; import { MatTreeFlatDataSource, @@ -52,7 +54,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; MatIcon ] }) -export class CreateHolidayComponent implements OnInit { +export class CreateHolidayComponent implements OnInit, OnDestroy { /** Create Holiday form. */ holidayForm: UntypedFormGroup; /** Repayment Scheduling data. */ @@ -63,6 +65,8 @@ export class CreateHolidayComponent implements OnInit { minDate = new Date(2000, 0, 1); /** Maximum Date allowed. */ maxDate = new Date(2100, 0, 1); + /** Subject for managing subscriptions. */ + private destroy$ = new Subject(); // Stores office data in a trie-like structure officesTrie: any; // Stores office data for access from DOM via Office ID @@ -111,7 +115,7 @@ export class CreateHolidayComponent implements OnInit { private _database: ChecklistDatabase, private createHoliday: CreateHoliday ) { - this.route.data.subscribe((data: { offices: any; holidayTemplate: any }) => { + this.route.data.pipe(takeUntil(this.destroy$)).subscribe((data: { offices: any; holidayTemplate: any }) => { this.officesData = data.offices; this.repaymentSchedulingTypes = data.holidayTemplate; // Constructs trie everytime data changes @@ -124,7 +128,7 @@ export class CreateHolidayComponent implements OnInit { this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); // Listens for changes in CheckListDatabase - this._database.dataChange.subscribe((data) => { + this._database.dataChange.pipe(takeUntil(this.destroy$)).subscribe((data) => { this.dataSource.data = data; }); } @@ -314,13 +318,16 @@ export class CreateHolidayComponent implements OnInit { * Sets the conditional controls. */ buildDependencies() { - this.holidayForm.get('reschedulingType').valueChanges.subscribe((option: any) => { - if (option === 2) { - this.holidayForm.addControl('repaymentsRescheduledTo', new UntypedFormControl('', Validators.required)); - } else { - this.holidayForm.removeControl('repaymentsRescheduledTo'); - } - }); + this.holidayForm + .get('reschedulingType') + .valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe((option: any) => { + if (option === 2) { + this.holidayForm.addControl('repaymentsRescheduledTo', new UntypedFormControl('', Validators.required)); + } else { + this.holidayForm.removeControl('repaymentsRescheduledTo'); + } + }); } /** @@ -350,14 +357,25 @@ export class CreateHolidayComponent implements OnInit { locale, offices }; - this.organizationService.createHoliday(data).subscribe((response: any) => { - this.router.navigate( - [ - '../', - response.resourceId - ], - { relativeTo: this.route } - ); - }); + this.organizationService + .createHoliday(data) + .pipe(takeUntil(this.destroy$)) + .subscribe((response: any) => { + this.router.navigate( + [ + '../', + response.resourceId + ], + { relativeTo: this.route } + ); + }); + } + + /** + * Component lifecycle hook to clean up subscriptions. + */ + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/app/organization/holidays/edit-holiday/edit-holiday.component.ts b/src/app/organization/holidays/edit-holiday/edit-holiday.component.ts index 9bb4f1cc7c..a11334a670 100644 --- a/src/app/organization/holidays/edit-holiday/edit-holiday.component.ts +++ b/src/app/organization/holidays/edit-holiday/edit-holiday.component.ts @@ -1,5 +1,5 @@ /** Angular Imports. */ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, @@ -8,6 +8,8 @@ import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { Dates } from 'app/core/utils/dates'; /** Custom Services. */ @@ -26,7 +28,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; ...STANDALONE_SHARED_IMPORTS ] }) -export class EditHolidayComponent implements OnInit { +export class EditHolidayComponent implements OnInit, OnDestroy { /** Edit Holiday form. */ holidayForm: UntypedFormGroup; /** Holiday data. */ @@ -39,6 +41,8 @@ export class EditHolidayComponent implements OnInit { minDate = new Date(2000, 0, 1); /** Maximum Date allowed. */ maxDate = new Date(); + /** Subject for managing subscriptions. */ + private destroy$ = new Subject(); /** * Get holiday and holiday template from `Resolver`. @@ -56,7 +60,7 @@ export class EditHolidayComponent implements OnInit { private settingsService: SettingsService, private router: Router ) { - this.route.data.subscribe((data: { holiday: any; holidayTemplate: any }) => { + this.route.data.pipe(takeUntil(this.destroy$)).subscribe((data: { holiday: any; holidayTemplate: any }) => { this.holidayData = data.holiday; this.holidayData.repaymentSchedulingTypes = data.holidayTemplate; this.reSchedulingType = this.holidayData.reschedulingType; @@ -116,14 +120,20 @@ export class EditHolidayComponent implements OnInit { * Get Rescheduling Type. */ getReschedulingType() { - this.holidayForm.get('reschedulingType').valueChanges.subscribe((option: any) => { - this.reSchedulingType = option; - if (option === 2) { - this.holidayForm.addControl('repaymentsRescheduledTo', new UntypedFormControl(new Date(), Validators.required)); - } else { - this.holidayForm.removeControl('repaymentsRescheduledTo'); - } - }); + this.holidayForm + .get('reschedulingType') + .valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe((option: any) => { + this.reSchedulingType = option; + if (option === 2) { + this.holidayForm.addControl( + 'repaymentsRescheduledTo', + new UntypedFormControl(new Date(), Validators.required) + ); + } else { + this.holidayForm.removeControl('repaymentsRescheduledTo'); + } + }); } /** @@ -155,9 +165,20 @@ export class EditHolidayComponent implements OnInit { dateFormat, locale }; - this.organizatioService.updateHoliday(this.holidayData.id, data).subscribe((response) => { - /** TODO Add Redirects to ViewMakerCheckerTask page. */ - this.router.navigate(['../'], { relativeTo: this.route }); - }); + this.organizatioService + .updateHoliday(this.holidayData.id, data) + .pipe(takeUntil(this.destroy$)) + .subscribe((response) => { + /** TODO Add Redirects to ViewMakerCheckerTask page. */ + this.router.navigate(['../'], { relativeTo: this.route }); + }); + } + + /** + * Component lifecycle hook to clean up subscriptions. + */ + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts index ce1416aae1..8fdb366dc0 100644 --- a/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts +++ b/src/app/products/fixed-deposit-products/fixed-deposit-product-stepper/fixed-deposit-product-charges-step/fixed-deposit-product-charges-step.component.ts @@ -1,7 +1,11 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; +/** RxJS Imports */ +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + import { TranslateService } from '@ngx-translate/core'; import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -47,7 +51,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; FormatNumberPipe ] }) -export class FixedDepositProductChargesStepComponent implements OnInit { +export class FixedDepositProductChargesStepComponent implements OnInit, OnDestroy { @Input() fixedDepositProductsTemplate: any; @Input() currencyCode: UntypedFormControl; @@ -61,6 +65,8 @@ export class FixedDepositProductChargesStepComponent implements OnInit { 'chargeTimeType', 'action' ]; + /** Subject to handle subscription cleanup */ + private destroy$ = new Subject(); constructor( public dialog: MatDialog, @@ -74,7 +80,12 @@ export class FixedDepositProductChargesStepComponent implements OnInit { } else { this.chargesDataSource = []; } - this.currencyCode.valueChanges.subscribe(() => (this.chargesDataSource = [])); + this.currencyCode.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => (this.chargesDataSource = [])); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } addCharge(charge: any) { diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts index d236fb5003..7c2ca1687f 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component.ts @@ -1,7 +1,11 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; +/** RxJS Imports */ +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + import { TranslateService } from '@ngx-translate/core'; import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -51,7 +55,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; FormatNumberPipe ] }) -export class LoanProductChargesStepComponent implements OnInit { +export class LoanProductChargesStepComponent implements OnInit, OnDestroy { @Input() loanProductsTemplate: any; @Input() currencyCode: UntypedFormControl; @Input() multiDisburseLoan: UntypedFormControl; @@ -69,6 +73,8 @@ export class LoanProductChargesStepComponent implements OnInit { ]; pristine = true; + /** Subject to handle subscription cleanup */ + private destroy$ = new Subject(); constructor( public dialog: MatDialog, @@ -86,8 +92,13 @@ export class LoanProductChargesStepComponent implements OnInit { this.chargesDataSource = this.loanProductsTemplate.charges || []; this.pristine = true; - this.currencyCode.valueChanges.subscribe(() => (this.chargesDataSource = [])); - this.multiDisburseLoan.valueChanges.subscribe(() => (this.chargesDataSource = [])); + this.currencyCode.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => (this.chargesDataSource = [])); + this.multiDisburseLoan.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => (this.chargesDataSource = [])); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } addCharge(charge: any) { diff --git a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts index 6fe08177aa..2c8630158c 100644 --- a/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts +++ b/src/app/products/recurring-deposit-products/recurring-deposit-product-stepper/recurring-deposit-product-charges-step/recurring-deposit-product-charges-step.component.ts @@ -1,7 +1,11 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; +/** RxJS Imports */ +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + import { TranslateService } from '@ngx-translate/core'; import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -45,7 +49,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; ChargesFilterPipe ] }) -export class RecurringDepositProductChargesStepComponent implements OnInit { +export class RecurringDepositProductChargesStepComponent implements OnInit, OnDestroy { @Input() recurringDepositProductsTemplate: any; @Input() currencyCode: UntypedFormControl; @@ -59,6 +63,8 @@ export class RecurringDepositProductChargesStepComponent implements OnInit { 'chargeTimeType', 'action' ]; + /** Subject to handle subscription cleanup */ + private destroy$ = new Subject(); constructor( public dialog: MatDialog, @@ -72,7 +78,12 @@ export class RecurringDepositProductChargesStepComponent implements OnInit { } else { this.chargesDataSource = []; } - this.currencyCode.valueChanges.subscribe(() => (this.chargesDataSource = [])); + this.currencyCode.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => (this.chargesDataSource = [])); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } addCharge(charge: any) { diff --git a/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts b/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts index 73bc5ccf13..e98ab2e790 100644 --- a/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts +++ b/src/app/products/share-products/share-product-stepper/share-product-charges-step/share-product-charges-step.component.ts @@ -1,7 +1,11 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; import { UntypedFormControl } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; +/** RxJS Imports */ +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + import { TranslateService } from '@ngx-translate/core'; import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; import { MatButton, MatIconButton } from '@angular/material/button'; @@ -47,7 +51,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; FormatNumberPipe ] }) -export class ShareProductChargesStepComponent implements OnInit { +export class ShareProductChargesStepComponent implements OnInit, OnDestroy { @Input() shareProductsTemplate: any; @Input() currencyCode: UntypedFormControl; @@ -63,6 +67,8 @@ export class ShareProductChargesStepComponent implements OnInit { ]; pristine = true; + /** Subject to handle subscription cleanup */ + private destroy$ = new Subject(); constructor( public dialog: MatDialog, @@ -75,7 +81,12 @@ export class ShareProductChargesStepComponent implements OnInit { this.chargesDataSource = this.shareProductsTemplate.charges || []; this.pristine = true; - this.currencyCode.valueChanges.subscribe(() => (this.chargesDataSource = [])); + this.currencyCode.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => (this.chargesDataSource = [])); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } addCharge(charge: any) { diff --git a/src/app/shared/notifications-tray/notifications-tray.component.ts b/src/app/shared/notifications-tray/notifications-tray.component.ts index 53a175dc90..c1d6fc0a72 100644 --- a/src/app/shared/notifications-tray/notifications-tray.component.ts +++ b/src/app/shared/notifications-tray/notifications-tray.component.ts @@ -2,7 +2,8 @@ import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; /** RxJS Imports */ -import { forkJoin } from 'rxjs'; +import { forkJoin, Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; /** Custom Services */ import { NotificationsService } from 'app/notifications/notifications.service'; @@ -46,6 +47,8 @@ export class NotificationsTrayComponent implements OnInit, OnDestroy { unreadNotifications: any[] = []; /** Timer to refetch notifications every 60 seconds */ timer: any; + /** Subject to handle subscription cleanup */ + private destroy$ = new Subject(); /** * Gets router link prefix from notification's objectType attribute @@ -70,11 +73,13 @@ export class NotificationsTrayComponent implements OnInit, OnDestroy { constructor(public notificationsService: NotificationsService) { forkJoin([ this.notificationsService.getNotifications(true, 9), - this.notificationsService.getNotifications(false, 9)]).subscribe((response: any[]) => { - this.readNotifications = response[0].pageItems; - this.unreadNotifications = response[1].pageItems; - this.setNotifications(); - }); + this.notificationsService.getNotifications(false, 9)]) + .pipe(takeUntil(this.destroy$)) + .subscribe((response: any[]) => { + this.readNotifications = response[0].pageItems; + this.unreadNotifications = response[1].pageItems; + this.setNotifications(); + }); } ngOnInit() { @@ -83,6 +88,8 @@ export class NotificationsTrayComponent implements OnInit, OnDestroy { ngOnDestroy() { this.destroy(); + this.destroy$.next(); + this.destroy$.complete(); } public destroy() { @@ -101,10 +108,13 @@ export class NotificationsTrayComponent implements OnInit, OnDestroy { * Recursively fetch unread notifications. */ fetchUnreadNotifications() { - this.notificationsService.getNotifications(false, 9).subscribe((response: any) => { - this.unreadNotifications = this.unreadNotifications.concat(response.pageItems); - this.setNotifications(); - }); + this.notificationsService + .getNotifications(false, 9) + .pipe(takeUntil(this.destroy$)) + .subscribe((response: any) => { + this.unreadNotifications = this.unreadNotifications.concat(response.pageItems); + this.setNotifications(); + }); // this.mockNotifications(); // Uncomment for Testing. this.timer = setTimeout(() => { this.fetchUnreadNotifications(); @@ -116,7 +126,10 @@ export class NotificationsTrayComponent implements OnInit, OnDestroy { */ menuClosed() { // Update the server for read notifications. - this.notificationsService.updateNotifications().subscribe(() => {}); + this.notificationsService + .updateNotifications() + .pipe(takeUntil(this.destroy$)) + .subscribe(() => {}); // Update locally for read notifications. this.readNotifications = this.unreadNotifications.concat(this.readNotifications); this.unreadNotifications = []; @@ -127,9 +140,12 @@ export class NotificationsTrayComponent implements OnInit, OnDestroy { * Function to test notifications in case of faulty backend. */ mockNotifications() { - this.notificationsService.getMockUnreadNotification().subscribe((response: any) => { - this.unreadNotifications = this.unreadNotifications.concat(response.pageItems); - this.setNotifications(); - }); + this.notificationsService + .getMockUnreadNotification() + .pipe(takeUntil(this.destroy$)) + .subscribe((response: any) => { + this.unreadNotifications = this.unreadNotifications.concat(response.pageItems); + this.setNotifications(); + }); } } diff --git a/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts index 1bbd294942..443fdc9366 100644 --- a/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts +++ b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts @@ -1,8 +1,12 @@ /** Angular Imports */ -import { Component, OnInit, OnChanges, Input } from '@angular/core'; +import { Component, OnInit, OnChanges, Input, OnDestroy } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { UntypedFormControl } from '@angular/forms'; +/** RxJS Imports */ +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + /** Custom Dialogs */ import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component'; @@ -55,7 +59,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; ChargesFilterPipe ] }) -export class SharesAccountChargesStepComponent implements OnInit, OnChanges { +export class SharesAccountChargesStepComponent implements OnInit, OnChanges, OnDestroy { /** Shares Account Product Template */ @Input() sharesAccountProductTemplate: any; /** Shares Account Template */ @@ -79,6 +83,8 @@ export class SharesAccountChargesStepComponent implements OnInit, OnChanges { 'chargeTimeType', 'action' ]; + /** Subject to handle subscription cleanup */ + private destroy$ = new Subject(); /** * @param {MatDialog} dialog Mat Dialog @@ -89,7 +95,7 @@ export class SharesAccountChargesStepComponent implements OnInit, OnChanges { ) {} ngOnInit() { - this.currencyCode.valueChanges.subscribe(() => { + this.currencyCode.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => { if (!this.isChargesPatched && this.sharesAccountTemplate.charges) { this.chargesDataSource = this.sharesAccountTemplate.charges; this.isChargesPatched = true; @@ -106,6 +112,11 @@ export class SharesAccountChargesStepComponent implements OnInit, OnChanges { } } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + /** * Adds the charge to charges table * @param {any} charge Charge