diff --git a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts index 23130318b8..be35672862 100644 --- a/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts +++ b/npm/ng-packs/packages/account/src/lib/components/auth-wrapper/auth-wrapper.component.ts @@ -1,21 +1,18 @@ -import { ConfigState, takeUntilDestroy } from '@abp/ng.core'; -import { Component, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; +import { ConfigState, SubscriptionService } from '@abp/ng.core'; +import { Component, Input, OnInit, TemplateRef } from '@angular/core'; import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; -import { Account } from '../../models/account'; import { eAccountComponents } from '../../enums/components'; +import { Account } from '../../models/account'; @Component({ selector: 'abp-auth-wrapper', templateUrl: './auth-wrapper.component.html', exportAs: 'abpAuthWrapper', + providers: [SubscriptionService], }) export class AuthWrapperComponent - implements - Account.AuthWrapperComponentInputs, - Account.AuthWrapperComponentOutputs, - OnInit, - OnDestroy { + implements Account.AuthWrapperComponentInputs, Account.AuthWrapperComponentOutputs, OnInit { @Input() readonly mainContentRef: TemplateRef; @@ -29,18 +26,16 @@ export class AuthWrapperComponent tenantBoxKey = eAccountComponents.TenantBox; - constructor(private store: Store) {} + constructor(private store: Store, private subscription: SubscriptionService) {} ngOnInit() { - this.store - .select(ConfigState.getSetting('Abp.Account.EnableLocalLogin')) - .pipe(takeUntilDestroy(this)) - .subscribe(value => { + this.subscription.addOne( + this.store.select(ConfigState.getSetting('Abp.Account.EnableLocalLogin')), + value => { if (value) { this.enableLocalLogin = value.toLowerCase() !== 'false'; } - }); + }, + ); } - - ngOnDestroy() {} } diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 7b1395223c..c47b3c02fd 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, OnDestroy, Optional, SkipSelf, Type } from '@angular/core'; +import { Component, Injector, Optional, SkipSelf, Type } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { Store } from '@ngxs/store'; import { eLayoutType } from '../enums/common'; @@ -6,9 +6,9 @@ import { ABP } from '../models'; import { ReplaceableComponents } from '../models/replaceable-components'; import { LocalizationService } from '../services/localization.service'; import { RoutesService } from '../services/routes.service'; +import { SubscriptionService } from '../services/subscription.service'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; import { findRoute, getRoutePath } from '../utils/route-utils'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; import { TreeNode } from '../utils/tree-utils'; @Component({ @@ -20,8 +20,9 @@ import { TreeNode } from '../utils/tree-utils'; > `, + providers: [SubscriptionService], }) -export class DynamicLayoutComponent implements OnDestroy { +export class DynamicLayoutComponent { layout: Type; // TODO: Consider a shared enum (eThemeSharedComponents) for known layouts @@ -37,6 +38,7 @@ export class DynamicLayoutComponent implements OnDestroy { injector: Injector, private localizationService: LocalizationService, private store: Store, + private subscription: SubscriptionService, @Optional() @SkipSelf() dynamicLayoutComponent: DynamicLayoutComponent, ) { if (dynamicLayoutComponent) return; @@ -44,7 +46,7 @@ export class DynamicLayoutComponent implements OnDestroy { const router = injector.get(Router); const routes = injector.get(RoutesService); - router.events.pipe(takeUntilDestroy(this)).subscribe(event => { + this.subscription.addOne(router.events, event => { if (event instanceof NavigationEnd) { let expectedLayout = (route.snapshot.data || {}).layout; @@ -73,7 +75,7 @@ export class DynamicLayoutComponent implements OnDestroy { } private listenToLanguageChange() { - this.localizationService.languageChange.pipe(takeUntilDestroy(this)).subscribe(() => { + this.subscription.addOne(this.localizationService.languageChange, () => { this.isLayoutVisible = false; setTimeout(() => (this.isLayoutVisible = true), 0); }); @@ -82,6 +84,4 @@ export class DynamicLayoutComponent implements OnDestroy { private getComponent(key: string): ReplaceableComponents.ReplaceableComponent { return this.store.selectSnapshot(ReplaceableComponentsState.getComponent(key)); } - - ngOnDestroy() {} } diff --git a/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts b/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts index 32d2dadb76..04c596d398 100644 --- a/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/replaceable-route-container.component.ts @@ -1,39 +1,45 @@ -import { Component, OnDestroy, OnInit, Type } from '@angular/core'; +import { Component, OnInit, Type } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngxs/store'; import { distinctUntilChanged } from 'rxjs/operators'; -import { ABP } from '../models/common'; import { ReplaceableComponents } from '../models/replaceable-components'; +import { SubscriptionService } from '../services/subscription.service'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; @Component({ selector: 'abp-replaceable-route-container', template: ` `, + providers: [SubscriptionService], }) -export class ReplaceableRouteContainerComponent implements OnInit, OnDestroy { +export class ReplaceableRouteContainerComponent implements OnInit { defaultComponent: Type; componentKey: string; externalComponent: Type; - constructor(private route: ActivatedRoute, private store: Store) {} + constructor( + private route: ActivatedRoute, + private store: Store, + private subscription: SubscriptionService, + ) {} ngOnInit() { this.defaultComponent = this.route.snapshot.data.replaceableComponent.defaultComponent; this.componentKey = (this.route.snapshot.data .replaceableComponent as ReplaceableComponents.RouteData).key; - this.store + const component$ = this.store .select(ReplaceableComponentsState.getComponent(this.componentKey)) - .pipe(takeUntilDestroy(this), distinctUntilChanged()) - .subscribe((res = {} as ReplaceableComponents.ReplaceableComponent) => { + .pipe(distinctUntilChanged()); + + this.subscription.addOne( + component$, + (res = {} as ReplaceableComponents.ReplaceableComponent) => { this.externalComponent = res.component; - }); + }, + ); } - - ngOnDestroy() {} } diff --git a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts index add5545d06..f35b0c6768 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts @@ -1,34 +1,25 @@ -import { - Directive, - ElementRef, - EventEmitter, - Input, - OnDestroy, - OnInit, - Output, -} from '@angular/core'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; +import { Directive, ElementRef, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { fromEvent } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; +import { SubscriptionService } from '../services/subscription.service'; @Directive({ // tslint:disable-next-line: directive-selector selector: '[input.debounce]', + providers: [SubscriptionService], }) -export class InputEventDebounceDirective implements OnInit, OnDestroy { +export class InputEventDebounceDirective implements OnInit { @Input() debounce = 300; @Output('input.debounce') readonly debounceEvent = new EventEmitter(); - constructor(private el: ElementRef) {} + constructor(private el: ElementRef, private subscription: SubscriptionService) {} ngOnInit(): void { - fromEvent(this.el.nativeElement, 'input') - .pipe(debounceTime(this.debounce), takeUntilDestroy(this)) - .subscribe((event: Event) => { - this.debounceEvent.emit(event); - }); - } + const input$ = fromEvent(this.el.nativeElement, 'input').pipe(debounceTime(this.debounce)); - ngOnDestroy(): void {} + this.subscription.addOne(input$, (event: Event) => { + this.debounceEvent.emit(event); + }); + } } diff --git a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts index 4d6c1014db..1d5955dca2 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts @@ -4,7 +4,6 @@ import { ElementRef, EventEmitter, Input, - OnDestroy, OnInit, Output, Self, @@ -12,15 +11,16 @@ import { import { FormControl, FormGroup, FormGroupDirective } from '@angular/forms'; import { fromEvent } from 'rxjs'; import { debounceTime, filter } from 'rxjs/operators'; -import { takeUntilDestroy } from '../utils'; +import { SubscriptionService } from '../services/subscription.service'; type Controls = { [key: string]: FormControl } | FormGroup[]; @Directive({ // tslint:disable-next-line: directive-selector selector: 'form[ngSubmit][formGroup]', + providers: [SubscriptionService], }) -export class FormSubmitDirective implements OnInit, OnDestroy { +export class FormSubmitDirective implements OnInit { @Input() debounce = 200; @@ -35,30 +35,30 @@ export class FormSubmitDirective implements OnInit, OnDestroy { @Self() private formGroupDirective: FormGroupDirective, private host: ElementRef, private cdRef: ChangeDetectorRef, + private subscription: SubscriptionService, ) {} ngOnInit() { - this.formGroupDirective.ngSubmit.pipe(takeUntilDestroy(this)).subscribe(() => { + this.subscription.addOne(this.formGroupDirective.ngSubmit, () => { this.markAsDirty(); this.executedNgSubmit = true; }); - fromEvent(this.host.nativeElement as HTMLElement, 'keyup') - .pipe( - debounceTime(this.debounce), - filter((key: KeyboardEvent) => key && key.key === 'Enter'), - takeUntilDestroy(this), - ) - .subscribe(() => { - if (!this.executedNgSubmit) { - this.host.nativeElement.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); - } + const keyup$ = fromEvent(this.host.nativeElement as HTMLElement, 'keyup').pipe( + debounceTime(this.debounce), + filter((key: KeyboardEvent) => key && key.key === 'Enter'), + ); - this.executedNgSubmit = false; - }); - } + this.subscription.addOne(keyup$, () => { + if (!this.executedNgSubmit) { + this.host.nativeElement.dispatchEvent( + new Event('submit', { bubbles: true, cancelable: true }), + ); + } - ngOnDestroy(): void {} + this.executedNgSubmit = false; + }); + } markAsDirty() { const { form } = this.formGroupDirective; diff --git a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts index 870e4ec144..fda8b7d315 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts @@ -2,19 +2,18 @@ import { Directive, ElementRef, Input, + OnChanges, OnDestroy, OnInit, - Renderer2, - ViewContainerRef, - TemplateRef, Optional, + Renderer2, SimpleChanges, - OnChanges, + TemplateRef, + ViewContainerRef, } from '@angular/core'; import { Store } from '@ngxs/store'; -import { ConfigState } from '../states'; -import { takeUntilDestroy } from '../utils'; import { Subscription } from 'rxjs'; +import { ConfigState } from '../states'; @Directive({ selector: '[abpPermission]', @@ -39,7 +38,6 @@ export class PermissionDirective implements OnInit, OnDestroy, OnChanges { this.subscription = this.store .select(ConfigState.getGrantedPolicy(this.condition)) - .pipe(takeUntilDestroy(this)) .subscribe(isGranted => { if (this.templateRef && isGranted) { this.vcRef.clear(); @@ -47,7 +45,10 @@ export class PermissionDirective implements OnInit, OnDestroy, OnChanges { } else if (this.templateRef && !isGranted) { this.vcRef.clear(); } else if (!isGranted && !this.templateRef) { - this.renderer.removeChild((this.elRef.nativeElement as HTMLElement).parentElement, this.elRef.nativeElement); + this.renderer.removeChild( + (this.elRef.nativeElement as HTMLElement).parentElement, + this.elRef.nativeElement, + ); } }); } @@ -58,7 +59,9 @@ export class PermissionDirective implements OnInit, OnDestroy, OnChanges { } } - ngOnDestroy(): void {} + ngOnDestroy(): void { + if (this.subscription) this.subscription.unsubscribe(); + } ngOnChanges({ condition }: SimpleChanges) { if ((condition || { currentValue: null }).currentValue) { diff --git a/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts index 91f666137f..fd9051c64e 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/replaceable-template.directive.ts @@ -4,7 +4,6 @@ import { Injector, Input, OnChanges, - OnDestroy, OnInit, SimpleChanges, TemplateRef, @@ -12,17 +11,17 @@ import { ViewContainerRef, } from '@angular/core'; import { Store } from '@ngxs/store'; +import compare from 'just-compare'; import { Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; +import snq from 'snq'; import { ABP } from '../models/common'; import { ReplaceableComponents } from '../models/replaceable-components'; +import { SubscriptionService } from '../services/subscription.service'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; -import compare from 'just-compare'; -import snq from 'snq'; -@Directive({ selector: '[abpReplaceableTemplate]' }) -export class ReplaceableTemplateDirective implements OnInit, OnDestroy, OnChanges { +@Directive({ selector: '[abpReplaceableTemplate]', providers: [SubscriptionService] }) +export class ReplaceableTemplateDirective implements OnInit, OnChanges { @Input('abpReplaceableTemplate') data: ReplaceableComponents.ReplaceableTemplateDirectiveInput; @@ -47,6 +46,7 @@ export class ReplaceableTemplateDirective implements OnInit, OnDestroy, OnChange private cfRes: ComponentFactoryResolver, private vcRef: ViewContainerRef, private store: Store, + private subscription: SubscriptionService, ) { this.context = { initTemplate: ref => { @@ -58,16 +58,18 @@ export class ReplaceableTemplateDirective implements OnInit, OnDestroy, OnChange } ngOnInit() { - this.store + const component$ = this.store .select(ReplaceableComponentsState.getComponent(this.data.componentKey)) .pipe( filter( (res = {} as ReplaceableComponents.ReplaceableComponent) => !this.initialized || !compare(res.component, this.externalComponent), ), - takeUntilDestroy(this), - ) - .subscribe((res = {} as ReplaceableComponents.ReplaceableComponent) => { + ); + + this.subscription.addOne( + component$, + (res = {} as ReplaceableComponents.ReplaceableComponent) => { this.vcRef.clear(); this.externalComponent = res.component; if (this.defaultComponentRef) { @@ -90,7 +92,8 @@ export class ReplaceableTemplateDirective implements OnInit, OnDestroy, OnChange } this.initialized = true; - }); + }, + ); } ngOnChanges(changes: SimpleChanges) { @@ -99,8 +102,6 @@ export class ReplaceableTemplateDirective implements OnInit, OnDestroy, OnChange } } - ngOnDestroy() {} - setDefaultComponentInputs() { if (!this.defaultComponentRef || (!this.data.inputs && !this.data.outputs)) return; diff --git a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts index ced0ae4159..12c035cc19 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts @@ -1,24 +1,21 @@ -import { Directive, ElementRef, EventEmitter, OnInit, Output, OnDestroy } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, OnInit, Output } from '@angular/core'; import { fromEvent } from 'rxjs'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; +import { SubscriptionService } from '../services/subscription.service'; @Directive({ // tslint:disable-next-line: directive-selector selector: '[click.stop]', + providers: [SubscriptionService], }) -export class StopPropagationDirective implements OnInit, OnDestroy { +export class StopPropagationDirective implements OnInit { @Output('click.stop') readonly stopPropEvent = new EventEmitter(); - constructor(private el: ElementRef) {} + constructor(private el: ElementRef, private subscription: SubscriptionService) {} ngOnInit(): void { - fromEvent(this.el.nativeElement, 'click') - .pipe(takeUntilDestroy(this)) - .subscribe((event: MouseEvent) => { - event.stopPropagation(); - this.stopPropEvent.emit(event); - }); + this.subscription.addOne(fromEvent(this.el.nativeElement, 'click'), (event: MouseEvent) => { + event.stopPropagation(); + this.stopPropEvent.emit(event); + }); } - - ngOnDestroy(): void {} } diff --git a/npm/ng-packs/packages/core/src/lib/services/list.service.ts b/npm/ng-packs/packages/core/src/lib/services/list.service.ts index d3da082eeb..3ebb6dc193 100644 --- a/npm/ng-packs/packages/core/src/lib/services/list.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/list.service.ts @@ -1,10 +1,17 @@ import { Inject, Injectable, OnDestroy, Optional } from '@angular/core'; -import { BehaviorSubject, Observable, of, ReplaySubject } from 'rxjs'; -import { catchError, debounceTime, filter, shareReplay, switchMap, tap } from 'rxjs/operators'; +import { BehaviorSubject, Observable, of, ReplaySubject, Subject } from 'rxjs'; +import { + catchError, + debounceTime, + filter, + shareReplay, + switchMap, + takeUntil, + tap, +} from 'rxjs/operators'; import { ABP } from '../models/common'; import { PagedResultDto } from '../models/dtos'; import { LIST_QUERY_DEBOUNCE_TIME } from '../tokens/list.token'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; @Injectable() export class ListService implements OnDestroy { @@ -65,6 +72,8 @@ export class ListService implements OnDes private _isLoading$ = new BehaviorSubject(false); + private destroy$ = new Subject(); + get isLoading$(): Observable { return this._isLoading$.asObservable(); } @@ -92,11 +101,13 @@ export class ListService implements OnDes filter(Boolean), tap(() => this._isLoading$.next(false)), shareReplay({ bufferSize: 1, refCount: true }), - takeUntilDestroy(this), + takeUntil(this.destroy$), ); } - ngOnDestroy() {} + ngOnDestroy() { + this.destroy$.next(); + } } export type QueryStreamCreatorCallback = ( diff --git a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts index 2f80d6b44d..c2b90a91c7 100644 --- a/npm/ng-packs/packages/core/src/lib/services/routes.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/routes.service.ts @@ -1,10 +1,9 @@ import { Injectable, OnDestroy } from '@angular/core'; import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { GetAppConfiguration } from '../actions/config.actions'; import { ABP } from '../models/common'; import { ConfigState } from '../states/config.state'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; import { pushValueTo } from '../utils/array-utils'; import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; @@ -129,6 +128,7 @@ export abstract class AbstractTreeService { @Injectable() export abstract class AbstractNavTreeService extends AbstractTreeService implements OnDestroy { + private subscription: Subscription; readonly id = 'name'; readonly parentId = 'parentName'; readonly hide = (item: T) => item.invisible || !this.isGranted(item); @@ -142,8 +142,8 @@ export abstract class AbstractNavTreeService extends Abstract constructor(protected actions: Actions, protected store: Store) { super(); - this.actions - .pipe(takeUntilDestroy(this), ofActionSuccessful(GetAppConfiguration)) + this.subscription = this.actions + .pipe(ofActionSuccessful(GetAppConfiguration)) .subscribe(() => this.refresh()); } @@ -162,7 +162,9 @@ export abstract class AbstractNavTreeService extends Abstract } /* istanbul ignore next */ - ngOnDestroy() {} + ngOnDestroy() { + this.subscription.unsubscribe(); + } } @Injectable({ providedIn: 'root' }) diff --git a/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts index ef6a75fe65..7e77bddba5 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts @@ -1,3 +1,4 @@ +// tslint:disable: max-line-length import { Observable, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -5,13 +6,16 @@ function isFunction(value) { return typeof value === 'function'; } +/** + * @deprecated no longer working, please use SubscriptionService (https://docs.abp.io/en/abp/latest/UI/Angular/Subscription-Service) instead. + */ export const takeUntilDestroy = (componentInstance, destroyMethodName = 'ngOnDestroy') => ( - source: Observable + source: Observable, ) => { const originalDestroy = componentInstance[destroyMethodName]; if (isFunction(originalDestroy) === false) { throw new Error( - `${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}` + `${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}`, ); } if (!componentInstance['__takeUntilDestroy']) { diff --git a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts index d3192057bb..b37516600e 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts @@ -1,4 +1,4 @@ -import { eLayoutType, takeUntilDestroy } from '@abp/ng.core'; +import { eLayoutType, SubscriptionService } from '@abp/ng.core'; import { collapseWithMargin, slideFromBottom } from '@abp/ng.theme.shared'; import { AfterViewInit, Component, OnDestroy } from '@angular/core'; import { fromEvent } from 'rxjs'; @@ -9,6 +9,7 @@ import { eThemeBasicComponents } from '../../enums/components'; selector: 'abp-layout-application', templateUrl: './application-layout.component.html', animations: [slideFromBottom, collapseWithMargin], + providers: [SubscriptionService], }) export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { // required for dynamic component @@ -24,6 +25,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { navItemsComponentKey = eThemeBasicComponents.NavItems; + constructor(private subscription: SubscriptionService) {} + private checkWindowWidth() { setTimeout(() => { if (window.innerWidth < 992) { @@ -43,11 +46,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy { ngAfterViewInit() { this.checkWindowWidth(); - fromEvent(window, 'resize') - .pipe(takeUntilDestroy(this), debounceTime(150)) - .subscribe(() => { - this.checkWindowWidth(); - }); + const resize$ = fromEvent(window, 'resize').pipe(debounceTime(150)); + this.subscription.addOne(resize$, () => this.checkWindowWidth()); } ngOnDestroy() {} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts index c556776b63..11270c6dc1 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts @@ -1,11 +1,5 @@ -import { ABP, getRoutePath, RoutesService, takeUntilDestroy, TreeNode } from '@abp/ng.core'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - OnDestroy, - OnInit, -} from '@angular/core'; +import { ABP, getRoutePath, RoutesService, TreeNode, SubscriptionService } from '@abp/ng.core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; import { filter, map, startWith } from 'rxjs/operators'; import { eThemeSharedRouteNames } from '../../enums'; @@ -14,28 +8,27 @@ import { eThemeSharedRouteNames } from '../../enums'; selector: 'abp-breadcrumb', templateUrl: './breadcrumb.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + providers: [SubscriptionService], }) -export class BreadcrumbComponent implements OnDestroy, OnInit { +export class BreadcrumbComponent implements OnInit { segments: Partial[] = []; constructor( public readonly cdRef: ChangeDetectorRef, private router: Router, private routes: RoutesService, + private subscription: SubscriptionService, ) {} - ngOnDestroy() {} - ngOnInit(): void { - this.router.events - .pipe( - takeUntilDestroy(this), + this.subscription.addOne( + this.router.events.pipe( filter(event => event instanceof NavigationEnd), // tslint:disable-next-line:deprecation startWith(null), map(() => this.routes.search({ path: getRoutePath(this.router) })), - ) - .subscribe(route => { + ), + route => { this.segments = []; if (route) { let node = { parent: route } as TreeNode; @@ -48,7 +41,8 @@ export class BreadcrumbComponent implements OnDestroy, OnInit { this.cdRef.detectChanges(); } - }); + }, + ); } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts index 0ada431f4f..fb1b2d4a6c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts @@ -1,4 +1,4 @@ -import { Config, takeUntilDestroy } from '@abp/ng.core'; +import { Config, SubscriptionService } from '@abp/ng.core'; import { AfterViewInit, ApplicationRef, @@ -20,6 +20,7 @@ import snq from 'snq'; selector: 'abp-http-error-wrapper', templateUrl: './http-error-wrapper.component.html', styleUrls: ['http-error-wrapper.component.scss'], + providers: [SubscriptionService], }) export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnInit { appRef: ApplicationRef; @@ -51,6 +52,8 @@ export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnIn return this.status ? `[${this.status}]` : ''; } + constructor(private subscription: SubscriptionService) {} + ngOnInit() { this.backgroundColor = snq(() => window.getComputedStyle(document.body).getPropertyValue('background-color')) || @@ -71,15 +74,11 @@ export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnIn customComponentRef.changeDetectorRef.detectChanges(); } - fromEvent(document, 'keyup') - .pipe( - takeUntilDestroy(this), - debounceTime(150), - filter((key: KeyboardEvent) => key && key.key === 'Escape'), - ) - .subscribe(() => { - this.destroy(); - }); + const keyup$ = fromEvent(document, 'keyup').pipe( + debounceTime(150), + filter((key: KeyboardEvent) => key && key.key === 'Escape'), + ); + this.subscription.addOne(keyup$, () => this.destroy()); } ngOnDestroy() {} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts index 47ff4d057d..f94b6b3656 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts @@ -1,7 +1,6 @@ -import { StartLoader, StopLoader } from '@abp/ng.core'; +import { StartLoader, StopLoader, SubscriptionService } from '@abp/ng.core'; import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router'; -import { takeUntilDestroy } from '@ngx-validate/core'; import { Actions, ofActionSuccessful } from '@ngxs/store'; import { Subscription, timer } from 'rxjs'; import { filter } from 'rxjs/operators'; @@ -22,6 +21,7 @@ import { filter } from 'rxjs/operators'; `, styleUrls: ['./loader-bar.component.scss'], + providers: [SubscriptionService], }) export class LoaderBarComponent implements OnDestroy, OnInit { protected _isLoading: boolean; @@ -77,36 +77,38 @@ export class LoaderBarComponent implements OnDestroy, OnInit { return `0 0 10px rgba(${this.color}, 0.5)`; } - constructor(private actions: Actions, private router: Router, private cdRef: ChangeDetectorRef) {} + constructor( + private actions: Actions, + private router: Router, + private cdRef: ChangeDetectorRef, + private subscription: SubscriptionService, + ) {} private subscribeToLoadActions() { - this.actions - .pipe( - ofActionSuccessful(StartLoader, StopLoader), - filter(this.filter), - takeUntilDestroy(this), - ) - .subscribe(action => { + this.subscription.addOne( + this.actions.pipe(ofActionSuccessful(StartLoader, StopLoader), filter(this.filter)), + action => { if (action instanceof StartLoader) this.startLoading(); else this.stopLoading(); - }); + }, + ); } private subscribeToRouterEvents() { - this.router.events - .pipe( + this.subscription.addOne( + this.router.events.pipe( filter( event => event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError, ), - takeUntilDestroy(this), - ) - .subscribe(event => { + ), + event => { if (event instanceof NavigationStart) this.startLoading(); else this.stopLoading(); - }); + }, + ); } ngOnInit() { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index 83d62f0b5a..b16b2bb14c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -1,4 +1,4 @@ -import { takeUntilDestroy } from '@abp/ng.core'; +import { SubscriptionService } from '@abp/ng.core'; import { Component, ContentChild, @@ -27,7 +27,7 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; templateUrl: './modal.component.html', animations: [fadeAnimation], styleUrls: ['./modal.component.scss'], - providers: [ModalService], + providers: [ModalService, SubscriptionService], }) export class ModalComponent implements OnDestroy { @Input() @@ -60,11 +60,11 @@ export class ModalComponent implements OnDestroy { @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) abpSubmit: ButtonComponent; - @ContentChild('abpHeader', {static: false}) abpHeader: TemplateRef; + @ContentChild('abpHeader', { static: false }) abpHeader: TemplateRef; - @ContentChild('abpBody', {static: false}) abpBody: TemplateRef; + @ContentChild('abpBody', { static: false }) abpBody: TemplateRef; - @ContentChild('abpFooter', {static: false}) abpFooter: TemplateRef; + @ContentChild('abpFooter', { static: false }) abpFooter: TemplateRef; @ContentChild('abpClose', { static: false, read: ElementRef }) abpClose: ElementRef; @@ -103,14 +103,15 @@ export class ModalComponent implements OnDestroy { private renderer: Renderer2, private confirmationService: ConfirmationService, private modalService: ModalService, + private subscription: SubscriptionService, ) { this.initToggleStream(); } private initToggleStream() { - this.toggle$ - .pipe(takeUntilDestroy(this), debounceTime(0), distinctUntilChanged()) - .subscribe(value => this.toggle(value)); + this.subscription.addOne(this.toggle$.pipe(debounceTime(0), distinctUntilChanged()), value => + this.toggle(value), + ); } private toggle(value: boolean) { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts index e363dacfd1..a369c97b37 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/loader-bar.component.spec.ts @@ -1,9 +1,15 @@ -import { Router, RouteReuseStrategy, NavigationStart, NavigationEnd, NavigationError } from '@angular/router'; +import { + Router, + RouteReuseStrategy, + NavigationStart, + NavigationEnd, + NavigationError, +} from '@angular/router'; import { createHostFactory, SpectatorHost, SpyObject } from '@ngneat/spectator/jest'; import { Actions, NgxsModule, Store } from '@ngxs/store'; import { Subject, Subscription, Observable, Subscriber, timer } from 'rxjs'; import { LoaderBarComponent } from '../components/loader-bar/loader-bar.component'; -import { StartLoader, StopLoader } from '@abp/ng.core'; +import { StartLoader, StopLoader, SubscriptionService } from '@abp/ng.core'; import { HttpRequest } from '@angular/common/http'; describe('LoaderBarComponent', () => { @@ -16,6 +22,7 @@ describe('LoaderBarComponent', () => { mocks: [Router], imports: [NgxsModule.forRoot()], detectChanges: false, + providers: [SubscriptionService], }); beforeEach(() => {