diff --git a/docs/app/@theme/components/fragment-target/fragment-target.directive.ts b/docs/app/@theme/components/fragment-target/fragment-target.directive.ts index d32b4e2992..7b27ae55c4 100644 --- a/docs/app/@theme/components/fragment-target/fragment-target.directive.ts +++ b/docs/app/@theme/components/fragment-target/fragment-target.directive.ts @@ -1,7 +1,7 @@ import { Directive, ElementRef, Inject, Input, OnDestroy, OnInit, PLATFORM_ID, Renderer2 } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { timer } from 'rxjs'; -import { takeWhile, publish, refCount, filter, tap, debounce } from 'rxjs/operators'; +import { timer, Subject } from 'rxjs'; +import { takeUntil, publish, refCount, filter, tap, debounce } from 'rxjs/operators'; import { NB_WINDOW, NbLayoutScrollService } from '@nebular/theme'; import { NgdVisibilityService } from '../../../@theme/services'; @@ -15,7 +15,7 @@ export class NgdFragmentTargetDirective implements OnInit, OnDestroy { private readonly marginFromTop = 120; private isCurrentlyViewed: boolean = false; private isScrolling: boolean = false; - private alive = true; + private destroy$ = new Subject(); @Input() ngdFragment: string; @Input() ngdFragmentClass: string; @@ -37,7 +37,7 @@ export class NgdFragmentTargetDirective implements OnInit, OnDestroy { .pipe( publish(null), refCount(), - takeWhile(() => this.alive), + takeUntil(this.destroy$), filter(() => this.ngdFragmentSync), ) .subscribe((fragment: string) => { @@ -49,7 +49,7 @@ export class NgdFragmentTargetDirective implements OnInit, OnDestroy { }); this.visibilityService.isTopmostVisible(this.el.nativeElement, OBSERVER_OPTIONS) - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((isTopmost: boolean) => { this.isCurrentlyViewed = isTopmost; if (isTopmost) { @@ -59,9 +59,9 @@ export class NgdFragmentTargetDirective implements OnInit, OnDestroy { this.scrollService.onScroll() .pipe( - takeWhile(() => this.alive), tap(() => this.isScrolling = true), debounce(() => timer(100)), + takeUntil(this.destroy$), ) .subscribe(() => this.isScrolling = false); } @@ -88,7 +88,8 @@ export class NgdFragmentTargetDirective implements OnInit, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); this.visibilityService.unobserve(this.el.nativeElement, OBSERVER_OPTIONS); } } diff --git a/docs/app/@theme/components/page-tabs/page-tabs.component.ts b/docs/app/@theme/components/page-tabs/page-tabs.component.ts index 596678b27e..ff54ddbacb 100644 --- a/docs/app/@theme/components/page-tabs/page-tabs.component.ts +++ b/docs/app/@theme/components/page-tabs/page-tabs.component.ts @@ -5,9 +5,9 @@ */ import { ChangeDetectionStrategy, Component, Input, OnDestroy, HostBinding } from '@angular/core'; -import { takeWhile, map, publishReplay, refCount } from 'rxjs/operators'; +import { takeUntil, map, publishReplay, refCount } from 'rxjs/operators'; import { ActivatedRoute } from '@angular/router'; -import { Observable, of as observableOf, combineLatest } from 'rxjs'; +import { Observable, of as observableOf, combineLatest, Subject } from 'rxjs'; @Component({ selector: 'ngd-page-tabs', @@ -25,6 +25,8 @@ import { Observable, of as observableOf, combineLatest } from 'rxjs'; }) export class NgdPageTabsComponent implements OnDestroy { + private destroy$ = new Subject(); + items$: Observable = observableOf([]); @Input() @@ -36,8 +38,8 @@ export class NgdPageTabsComponent implements OnDestroy { this.activatedRoute.params.pipe(publishReplay(), refCount()), ) .pipe( - takeWhile(() => this.alive), map(([tabs, params]) => (tabs.map((item: any) => ({ ...item, selected: item.tab === params.tab })))), + takeUntil(this.destroy$), ); } @@ -76,12 +78,12 @@ export class NgdPageTabsComponent implements OnDestroy { icon: 'image-outline', }, ]; - private alive = true; constructor(private activatedRoute: ActivatedRoute) { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/docs/app/@theme/components/page-toc/page-toc.component.ts b/docs/app/@theme/components/page-toc/page-toc.component.ts index f94121a6f6..4d356c1fe7 100644 --- a/docs/app/@theme/components/page-toc/page-toc.component.ts +++ b/docs/app/@theme/components/page-toc/page-toc.component.ts @@ -5,9 +5,9 @@ */ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core'; -import { takeWhile, map } from 'rxjs/operators'; +import { takeUntil, map } from 'rxjs/operators'; import { ActivatedRoute } from '@angular/router'; -import { of as observableOf, combineLatest } from 'rxjs'; +import { of as observableOf, combineLatest, Subject } from 'rxjs'; @Component({ selector: 'ngd-page-toc', @@ -26,6 +26,8 @@ import { of as observableOf, combineLatest } from 'rxjs'; }) export class NgdPageTocComponent implements OnDestroy { + private destroy$ = new Subject(); + items: any[]; @Input() @@ -35,7 +37,6 @@ export class NgdPageTocComponent implements OnDestroy { this.activatedRoute.fragment, ) .pipe( - takeWhile(() => this.alive), map(([toc, fragment]) => { toc = toc.map((item: any) => ({ ...item, selected: fragment === item.fragment })); if (toc.length && !toc.find(item => item.selected)) { @@ -43,6 +44,7 @@ export class NgdPageTocComponent implements OnDestroy { } return toc; }), + takeUntil(this.destroy$), ) .subscribe((toc) => { this.items = toc; @@ -50,12 +52,11 @@ export class NgdPageTocComponent implements OnDestroy { }) } - private alive = true; - constructor(private activatedRoute: ActivatedRoute, private cd: ChangeDetectorRef) { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/docs/app/blocks/components/live-example-block/live-example-block.component.ts b/docs/app/blocks/components/live-example-block/live-example-block.component.ts index 7dfded5198..50e7c2b737 100644 --- a/docs/app/blocks/components/live-example-block/live-example-block.component.ts +++ b/docs/app/blocks/components/live-example-block/live-example-block.component.ts @@ -13,7 +13,8 @@ import { AfterViewInit, } from '@angular/core'; import { Location } from '@angular/common'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NgdAnalytics, NgdIframeCommunicatorService } from '../../../@theme/services'; import { NgdExampleView } from '../../enum.example-view'; @@ -51,7 +52,8 @@ export class NgdLiveExampleBlockComponent implements OnInit, AfterViewInit, OnDe } iframeHeight = 0; - alive: boolean = true; + + private destroy$ = new Subject(); themes: {label: string; value: string}[] = [ { label: 'Default', value: 'default' }, @@ -79,7 +81,7 @@ export class NgdLiveExampleBlockComponent implements OnInit, AfterViewInit, OnDe ngOnInit() { this.communicator.receive(this.content.id) - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(it => { this.iframeHeight = it.height; this.loading = false; @@ -97,7 +99,8 @@ export class NgdLiveExampleBlockComponent implements OnInit, AfterViewInit, OnDe } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } switchTheme(theme: string) { diff --git a/docs/app/blocks/components/theme-block/theme-block.component.ts b/docs/app/blocks/components/theme-block/theme-block.component.ts index 04cbd5b989..3e39463c2f 100644 --- a/docs/app/blocks/components/theme-block/theme-block.component.ts +++ b/docs/app/blocks/components/theme-block/theme-block.component.ts @@ -6,8 +6,8 @@ import {Component, Input, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef} from '@angular/core'; import { FormControl } from '@angular/forms'; -import { takeWhile, skip, distinctUntilChanged, debounceTime } from 'rxjs/operators'; - +import { takeUntil, skip, distinctUntilChanged, debounceTime } from 'rxjs/operators'; +import { Subject } from 'rxjs'; @Component({ selector: 'ngd-theme-block', @@ -18,7 +18,7 @@ import { takeWhile, skip, distinctUntilChanged, debounceTime } from 'rxjs/operat export class NgdThemeComponent implements OnInit, OnDestroy { searchControl = new FormControl(); - private alive: boolean = true; + private destroy$ = new Subject(); properties = []; filtered = []; @@ -47,7 +47,7 @@ export class NgdThemeComponent implements OnInit, OnDestroy { ngOnInit() { this.searchControl.valueChanges - .pipe(skip(1), distinctUntilChanged(), debounceTime(300), takeWhile(() => this.alive)) + .pipe(skip(1), distinctUntilChanged(), debounceTime(300), takeUntil(this.destroy$)) .subscribe((value: string) => { this.filtered = this.properties .filter(({ name }) => name.toLowerCase().includes(value.toLowerCase())); @@ -60,6 +60,7 @@ export class NgdThemeComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/docs/app/documentation/documentation.component.ts b/docs/app/documentation/documentation.component.ts index d4bc56eae2..c611ede8fa 100644 --- a/docs/app/documentation/documentation.component.ts +++ b/docs/app/documentation/documentation.component.ts @@ -6,7 +6,8 @@ import { Component, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; -import { takeWhile, withLatestFrom, map } from 'rxjs/operators'; +import { takeUntil, withLatestFrom, map } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NbThemeService, NbMenuItem, NbSidebarService, NbMenuService } from '@nebular/theme'; import { NgdMenuService } from '../@theme/services/menu.service'; @@ -24,7 +25,7 @@ export class NgdDocumentationComponent implements OnDestroy { collapsedBreakpoints = ['xs', 'is', 'sm', 'md', 'lg']; sidebarTag = 'menuSidebar'; - private alive = true; + private destroy$ = new Subject(); constructor( private service: NgdMenuService, @@ -42,7 +43,7 @@ export class NgdDocumentationComponent implements OnDestroy { this.router.events .pipe( withLatestFrom(this.themeService.onMediaQueryChange().pipe(map((changes: any[]) => changes[1]))), - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe(([event, mediaQuery]: [any, NbMediaBreakpoint]) => { if (event.url === '/docs') { @@ -62,6 +63,7 @@ export class NgdDocumentationComponent implements OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/docs/app/documentation/page/page.component.ts b/docs/app/documentation/page/page.component.ts index 8ceca6d14f..2b1e8900bb 100644 --- a/docs/app/documentation/page/page.component.ts +++ b/docs/app/documentation/page/page.component.ts @@ -7,7 +7,8 @@ import { Component, Inject, NgZone, OnDestroy, OnInit, ViewChild, AfterContentChecked } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; -import { filter, map, publishReplay, refCount, tap, takeWhile } from 'rxjs/operators'; +import { filter, map, publishReplay, refCount, tap, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NB_WINDOW } from '@nebular/theme'; import { NgdTabbedBlockComponent } from '../../blocks/components/tabbed-block/tabbed-block.component'; import { NgdStructureService } from '../../@theme/services'; @@ -20,7 +21,7 @@ import { NgdStructureService } from '../../@theme/services'; export class NgdPageComponent implements OnInit, AfterContentChecked, OnDestroy { currentItem; - private alive = true; + private destroy$ = new Subject(); currentTabName: string = ''; @@ -52,13 +53,13 @@ export class NgdPageComponent implements OnInit, AfterContentChecked, OnDestroy } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } handlePageNavigation() { this.activatedRoute.params .pipe( - takeWhile(() => this.alive), filter((params: any) => params.subPage), map((params: any) => { const slag = `${params.page}_${params.subPage}`; @@ -75,6 +76,7 @@ export class NgdPageComponent implements OnInit, AfterContentChecked, OnDestroy }), publishReplay(), refCount(), + takeUntil(this.destroy$), ) .subscribe((item) => { this.currentItem = item; diff --git a/docs/app/example/example.component.ts b/docs/app/example/example.component.ts index 263c47be5f..fa38989bc8 100644 --- a/docs/app/example/example.component.ts +++ b/docs/app/example/example.component.ts @@ -1,7 +1,7 @@ import { AfterViewInit, Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { of as observableOf } from 'rxjs'; -import { takeWhile, delay } from 'rxjs/operators'; +import { of as observableOf, Subject } from 'rxjs'; +import { takeUntil, delay } from 'rxjs/operators'; import { NB_DOCUMENT, NbThemeService } from '@nebular/theme'; import { NgdAnalytics, NgdIframeCommunicatorService } from '../@theme/services'; @@ -12,7 +12,7 @@ import { NgdAnalytics, NgdIframeCommunicatorService } from '../@theme/services'; }) export class NgdExampleComponent implements OnInit, AfterViewInit, OnDestroy { private id: string; - private alive: boolean = true; + private destroy$ = new Subject(); constructor(private communicator: NgdIframeCommunicatorService, private themeService: NbThemeService, @@ -34,7 +34,8 @@ export class NgdExampleComponent implements OnInit, AfterViewInit, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } private setupId() { @@ -43,7 +44,7 @@ export class NgdExampleComponent implements OnInit, AfterViewInit, OnDestroy { private subscribeOnThemeSwitch() { this.communicator.receive(this.id) - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(payload => this.changeTheme(payload)) } diff --git a/docs/articles/auth/oauth2.md b/docs/articles/auth/oauth2.md index 408fa493dd..e9421764b6 100644 --- a/docs/articles/auth/oauth2.md +++ b/docs/articles/auth/oauth2.md @@ -140,17 +140,18 @@ And finally, let's add code to our component to initiate the authentication. Fir }) export class NbOAuth2LoginComponent implements OnDestroy { - alive = true; + private destroy$ = new Subject(); login() { this.authService.authenticate('google') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { }); } ngOnDestroy(): void { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } ``` @@ -170,11 +171,11 @@ Now, we need to configure that "callback" URL to be able to properly handle resp }) export class NbOAuth2CallbackPlaygroundComponent implements OnDestroy { - alive = true; + private destroy$ = new Subject(); constructor(private authService: NbAuthService, private router: Router) { this.authService.authenticate('google') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { if (authResult.isSuccess()) { this.router.navigateByUrl('/pages/dashboard'); @@ -183,7 +184,8 @@ export class NbOAuth2CallbackPlaygroundComponent implements OnDestroy { } ngOnDestroy(): void { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } ``` diff --git a/package-lock.json b/package-lock.json index 41ecba2b10..33f1571424 100644 --- a/package-lock.json +++ b/package-lock.json @@ -523,6 +523,30 @@ "tslib": "^1.9.0" } }, + "@babel/runtime-corejs2": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.7.2.tgz", + "integrity": "sha512-GfVnHchOBvIMsweQ13l4jd9lT4brkevnavnVOej5g2y7PpTRY+R4pcQlCjWMZoUla5rMLFzaS/Ll2s59cB1TqQ==", + "dev": true, + "requires": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "core-js": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } + } + }, "@google-cloud/common": { "version": "0.17.0", "resolved": "http://registry.npmjs.org/@google-cloud/common/-/common-0.17.0.tgz", @@ -1059,6 +1083,15 @@ } } }, + "@phenomnomnominal/tsquery": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-4.0.0.tgz", + "integrity": "sha512-s2Yet/MCj9Jh6nR6GfldrUPT6Y+aM1jIAdiKcOKEzmeKALT0Tc7SFIkYP3KvzjzbkKK5W7BiJ3cWy2UOa4ITbw==", + "dev": true, + "requires": { + "esquery": "^1.0.1" + } + }, "@schematics/angular": { "version": "8.0.0-rc.4", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.0.0-rc.4.tgz", @@ -6340,6 +6373,15 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", @@ -16026,6 +16068,47 @@ "tslib": "^1.9.0" } }, + "rxjs-tslint-rules": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/rxjs-tslint-rules/-/rxjs-tslint-rules-4.26.3.tgz", + "integrity": "sha512-SZM3AjmvHhEJ1tUtGB82i/KruhqxiDohBx7wdepQst5PnG76qp84NU7WSGzXFY0pK54rdS7iNU/oqJMcCu/Ssg==", + "dev": true, + "requires": { + "@phenomnomnominal/tsquery": "^4.0.0", + "decamelize": "^3.0.0", + "resolve": "^1.4.0", + "semver": "^6.0.0", + "tslib": "^1.8.0", + "tsutils": "^3.0.0", + "tsutils-etc": "^1.1.0" + }, + "dependencies": { + "decamelize": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz", + "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==", + "dev": true, + "requires": { + "xregexp": "^4.2.4" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -18712,6 +18795,12 @@ "tslib": "^1.8.1" } }, + "tsutils-etc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tsutils-etc/-/tsutils-etc-1.1.0.tgz", + "integrity": "sha512-pJlLtLmQPUyGHqY/Pq6EGnpGmQCnnTDZetQ7eWkeQ5xaw4GtfcR1Zt7HMKFHGDDp53HzQfbqQ+7ps6iJbfa9Hw==", + "dev": true + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -20242,6 +20331,15 @@ "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", "dev": true }, + "xregexp": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.2.4.tgz", + "integrity": "sha512-sO0bYdYeJAJBcJA8g7MJJX7UrOZIfJPd8U2SC7B2Dd/J24U0aQNoGp33shCaBSWeb0rD5rh6VBUIXOkGal1TZA==", + "dev": true, + "requires": { + "@babel/runtime-corejs2": "^7.2.0" + } + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index 80191788d6..ea405c34bb 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "pump": "1.0.2", "rimraf": "2.6.1", "rollup-plugin-node-resolve": "3.0.0", + "rxjs-tslint-rules": "^4.26.3", "stylelint": "7.13.0", "through2": "^2.0.3", "ts-node": "8.4.1", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index a8d916ad7d..d9783d23b4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -7,8 +7,8 @@ import { AfterViewInit, Component, Inject, OnDestroy } from '@angular/core'; import { NavigationStart, Router } from '@angular/router'; import { NB_DOCUMENT } from '@nebular/theme'; -import { fromEvent } from 'rxjs'; -import { filter, takeWhile } from 'rxjs/operators'; +import { fromEvent, Subject } from 'rxjs'; +import { filter, takeUntil } from 'rxjs/operators'; import { ComponentLink, PLAYGROUND_COMPONENTS } from './playground-components'; @Component({ @@ -34,7 +34,7 @@ import { ComponentLink, PLAYGROUND_COMPONENTS } from './playground-components'; }) export class AppComponent implements AfterViewInit, OnDestroy { - alive: boolean = true; + private destroy$ = new Subject(); document: Document; optionsVisible: boolean = true; componentsListVisible: boolean = false; @@ -54,28 +54,29 @@ export class AppComponent implements AfterViewInit, OnDestroy { fromEvent(this.document, 'keypress') .pipe( - takeWhile(() => this.alive), filter((e: KeyboardEvent) => e.key === 'c'), + takeUntil(this.destroy$), ) .subscribe(this.toggleComponentsOverlay.bind(this)); fromEvent(this.document, 'keyup') .pipe( - takeWhile(() => this.alive), filter((e: KeyboardEvent) => e.key === 'Escape' || e.key === 'Esc'), + takeUntil(this.destroy$), ) .subscribe(() => this.hideComponentsOverlay()); this.router.events .pipe( - takeWhile(() => this.alive), filter(event => event instanceof NavigationStart), + takeUntil(this.destroy$), ) .subscribe(() => this.hideComponentsOverlay()) } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } toggleOptions() { diff --git a/src/framework/auth/components/auth.component.ts b/src/framework/auth/components/auth.component.ts index aca1a7fb40..d976d63c4f 100644 --- a/src/framework/auth/components/auth.component.ts +++ b/src/framework/auth/components/auth.component.ts @@ -7,7 +7,8 @@ import { Component, OnDestroy } from '@angular/core'; import { Location } from '@angular/common'; import { NbAuthService } from '../services/auth.service'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; @Component({ selector: 'nb-auth', @@ -35,7 +36,7 @@ import { takeWhile } from 'rxjs/operators'; }) export class NbAuthComponent implements OnDestroy { - private alive = true; + private destroy$ = new Subject(); subscription: any; @@ -46,7 +47,7 @@ export class NbAuthComponent implements OnDestroy { constructor(protected auth: NbAuthService, protected location: Location) { this.subscription = auth.onAuthenticationChange() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authenticated: boolean) => { this.authenticated = authenticated; }); @@ -58,6 +59,7 @@ export class NbAuthComponent implements OnDestroy { } ngOnDestroy(): void { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/framework/theme/components/accordion/accordion-item-body.component.ts b/src/framework/theme/components/accordion/accordion-item-body.component.ts index 2e136f8e08..ee87b571b0 100644 --- a/src/framework/theme/components/accordion/accordion-item-body.component.ts +++ b/src/framework/theme/components/accordion/accordion-item-body.component.ts @@ -13,7 +13,8 @@ import { OnDestroy, } from '@angular/core'; import { trigger, state, style, animate, transition } from '@angular/animations'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NbAccordionItemComponent } from './accordion-item.component'; @@ -53,7 +54,7 @@ const accordionItemBodyTrigger = trigger('accordionItemBody', [ changeDetection: ChangeDetectionStrategy.OnPush, }) export class NbAccordionItemBodyComponent implements OnInit, OnDestroy { - private alive: boolean = true; + private destroy$ = new Subject(); constructor(@Host() private accordionItem: NbAccordionItemComponent, private cd: ChangeDetectorRef) {} @@ -63,11 +64,12 @@ export class NbAccordionItemBodyComponent implements OnInit, OnDestroy { ngOnInit() { this.accordionItem.accordionItemInvalidate - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => this.cd.markForCheck()); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/framework/theme/components/accordion/accordion-item-header.component.ts b/src/framework/theme/components/accordion/accordion-item-header.component.ts index 7401107933..5e0243dddf 100644 --- a/src/framework/theme/components/accordion/accordion-item-header.component.ts +++ b/src/framework/theme/components/accordion/accordion-item-header.component.ts @@ -15,7 +15,8 @@ import { ChangeDetectorRef, } from '@angular/core'; import { trigger, state, style, animate, transition } from '@angular/animations'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NbAccordionItemComponent } from './accordion-item.component'; @@ -90,17 +91,18 @@ export class NbAccordionItemHeaderComponent implements OnInit, OnDestroy { } } - private alive: boolean = true; + private destroy$ = new Subject(); constructor(@Host() private accordionItem: NbAccordionItemComponent, private cd: ChangeDetectorRef) { } ngOnInit() { this.accordionItem.accordionItemInvalidate - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => this.cd.markForCheck()); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/framework/theme/components/accordion/accordion-item.component.ts b/src/framework/theme/components/accordion/accordion-item.component.ts index 78060610ee..273d85e8ca 100644 --- a/src/framework/theme/components/accordion/accordion-item.component.ts +++ b/src/framework/theme/components/accordion/accordion-item.component.ts @@ -19,7 +19,7 @@ import { OnDestroy, } from '@angular/core'; import { Subject } from 'rxjs'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; import { NbAccordionComponent } from './accordion.component'; import { convertToBoolProperty } from '../helpers'; @@ -90,7 +90,7 @@ export class NbAccordionItemComponent implements OnInit, OnChanges, OnDestroy { private collapsedValue = true; private disabledValue = false; - private alive = true; + private destroy$ = new Subject(); constructor(@Host() private accordion: NbAccordionComponent, private cd: ChangeDetectorRef) { } @@ -126,7 +126,7 @@ export class NbAccordionItemComponent implements OnInit, OnChanges, OnDestroy { ngOnInit() { this.accordion.openCloseItems - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(collapsed => { !this.disabled && (this.collapsed = collapsed); }); @@ -137,7 +137,8 @@ export class NbAccordionItemComponent implements OnInit, OnChanges, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); this.accordionItemInvalidate.complete(); } diff --git a/src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay.ts b/src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay.ts index 769ceeda34..3cf3ace578 100644 --- a/src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay.ts +++ b/src/framework/theme/components/cdk/overlay/dynamic/dynamic-overlay.ts @@ -1,6 +1,6 @@ import { ComponentFactoryResolver, ComponentRef, Injectable, NgZone, Type } from '@angular/core'; -import { filter, takeUntil, takeWhile, distinctUntilChanged } from 'rxjs/operators'; -import { Subject, BehaviorSubject, Observable } from 'rxjs'; +import { filter, takeUntil, distinctUntilChanged } from 'rxjs/operators'; +import { Subject, BehaviorSubject, Observable, merge } from 'rxjs'; import { NbAdjustableConnectedPositionStrategy, @@ -32,7 +32,7 @@ export class NbDynamicOverlay { protected positionStrategyChange$ = new Subject(); protected isShown$ = new BehaviorSubject(false); - protected alive = true; + protected destroy$ = new Subject(); get isAttached(): boolean { return this.ref && this.ref.hasAttached(); @@ -105,9 +105,13 @@ export class NbDynamicOverlay { this.positionStrategy.positionChange .pipe( - takeWhile(() => this.alive), - takeUntil(this.positionStrategyChange$), filter(() => !!this.container), + takeUntil( + merge( + this.positionStrategyChange$, + this.destroy$, + ), + ), ) .subscribe((position: NbPosition) => { this.lastAppliedPosition = position; @@ -165,7 +169,8 @@ export class NbDynamicOverlay { } dispose() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); this.hide(); this.disposeOverlayRef(); this.isShown$.complete(); @@ -215,7 +220,7 @@ export class NbDynamicOverlay { */ protected updatePositionWhenStable() { this.zone.onStable - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => { this.ref && this.ref.updatePosition(); }); diff --git a/src/framework/theme/components/cdk/overlay/overlay-trigger.ts b/src/framework/theme/components/cdk/overlay/overlay-trigger.ts index 523829a295..27450030f7 100644 --- a/src/framework/theme/components/cdk/overlay/overlay-trigger.ts +++ b/src/framework/theme/components/cdk/overlay/overlay-trigger.ts @@ -103,9 +103,13 @@ export class NbHoverTriggerStrategy extends NbTriggerStrategyBase { .pipe( filter(() => !this.container()), delay(100), - takeUntil(observableFromEvent(this.host, 'mouseleave')), repeat(), - takeUntil(this.destroyed$), + takeUntil( + observableMerge( + observableFromEvent(this.host, 'mouseleave'), + this.destroyed$, + ), + ), ); hide$: Observable = observableFromEvent(this.host, 'mouseleave') @@ -130,15 +134,15 @@ export class NbHintTriggerStrategy extends NbTriggerStrategyBase { show$: Observable = observableFromEvent(this.host, 'mouseenter') .pipe( delay(100), + // this `delay & takeUntil & repeat` operators combination is a synonym for `conditional debounce` + // meaning that if one event occurs in some time after the initial one we won't react to it + repeat(), takeUntil( observableMerge( observableFromEvent(this.host, 'mouseleave'), this.destroyed$, ), ), - // this `delay & takeUntil & repeat` operators combination is a synonym for `conditional debounce` - // meaning that if one event occurs in some time after the initial one we won't react to it - repeat(), ); hide$: Observable = observableFromEvent(this.host, 'mouseleave') @@ -188,13 +192,13 @@ export class NbFocusTriggerStrategy extends NbTriggerStrategyBase { .pipe( filter(() => !this.container()), debounceTime(100), + repeat(), takeUntil( observableMerge( observableFromEvent(this.host, 'focusout'), this.destroyed$, ), ), - repeat(), ); hide$ = observableMerge(this.focusOut$, this.tabKeyPress$, this.clickOut$) diff --git a/src/framework/theme/components/context-menu/context-menu.directive.ts b/src/framework/theme/components/context-menu/context-menu.directive.ts index c9e9902d2b..e82e262475 100644 --- a/src/framework/theme/components/context-menu/context-menu.directive.ts +++ b/src/framework/theme/components/context-menu/context-menu.directive.ts @@ -15,7 +15,8 @@ import { OnDestroy, OnInit, } from '@angular/core'; -import { filter, takeWhile } from 'rxjs/operators'; +import { filter, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NbDynamicOverlay, NbDynamicOverlayController } from '../cdk/overlay/dynamic/dynamic-overlay'; import { NbDynamicOverlayHandler } from '../cdk/overlay/dynamic/dynamic-overlay-handler'; @@ -163,7 +164,7 @@ export class NbContextMenuDirective implements NbDynamicOverlayController, OnCha protected ref: NbOverlayRef; protected container: ComponentRef; protected positionStrategy: NbAdjustableConnectedPositionStrategy; - protected alive: boolean = true; + protected destroy$ = new Subject(); private _items: NbMenuItem[] = []; private dynamicOverlay: NbDynamicOverlay; @@ -236,8 +237,8 @@ export class NbContextMenuDirective implements NbDynamicOverlayController, OnCha private subscribeOnItemClick() { this.menuService.onItemClick() .pipe( - takeWhile(() => this.alive), filter(({ tag }) => tag === this.tag), + takeUntil(this.destroy$), ) .subscribe(() => this.hide()); } diff --git a/src/framework/theme/components/datepicker/datepicker.component.ts b/src/framework/theme/components/datepicker/datepicker.component.ts index 3a85dac5a0..58f20dc465 100644 --- a/src/framework/theme/components/datepicker/datepicker.component.ts +++ b/src/framework/theme/components/datepicker/datepicker.component.ts @@ -21,7 +21,7 @@ import { SimpleChanges, Optional, } from '@angular/core'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; import { Observable, ReplaySubject, Subject } from 'rxjs'; import { NbComponentPortal, NbOverlayRef } from '../cdk/overlay/mapping'; @@ -177,7 +177,7 @@ export abstract class NbBasePicker * */ protected pickerRef: ComponentRef; - protected alive: boolean = true; + protected destroy$ = new Subject(); /** * Queue contains the last value that was applied to the picker when it was hidden. @@ -243,7 +243,8 @@ export abstract class NbBasePicker } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); this.hide(); this.init$.complete(); @@ -323,7 +324,7 @@ export abstract class NbBasePicker protected subscribeOnPositionChange() { this.positionStrategy.positionChange - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((position: NbPosition) => patch(this.container, { position })); } diff --git a/src/framework/theme/components/datepicker/datepicker.directive.ts b/src/framework/theme/components/datepicker/datepicker.directive.ts index 3bcf005b94..bb203dd93e 100644 --- a/src/framework/theme/components/datepicker/datepicker.directive.ts +++ b/src/framework/theme/components/datepicker/datepicker.directive.ts @@ -24,8 +24,8 @@ import { ValidatorFn, Validators, } from '@angular/forms'; -import { fromEvent, Observable, merge } from 'rxjs'; -import { map, takeWhile, filter, take, tap } from 'rxjs/operators'; +import { fromEvent, Observable, merge, Subject } from 'rxjs'; +import { map, takeUntil, filter, take, tap } from 'rxjs/operators'; import { NB_DOCUMENT } from '../../theme.options'; import { NbDateService } from '../calendar-kit/services/date.service'; @@ -277,7 +277,7 @@ export class NbDatepickerDirective implements OnDestroy, ControlValueAccessor * Datepicker instance. * */ protected picker: NbDatepicker; - protected alive: boolean = true; + protected destroy$ = new Subject(); protected isDatepickerReady: boolean = false; protected queue: D | undefined; protected onChange: (D) => void = () => {}; @@ -316,7 +316,8 @@ export class NbDatepickerDirective implements OnDestroy, ControlValueAccessor } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } /** @@ -432,10 +433,10 @@ export class NbDatepickerDirective implements OnDestroy, ControlValueAccessor if (!this.isDatepickerReady) { this.picker.init .pipe( - takeWhile(() => this.alive), take(1), tap(() => this.isDatepickerReady = true), filter(() => !!this.queue), + takeUntil(this.destroy$), ) .subscribe(() => { this.writeValue(this.queue); @@ -446,7 +447,7 @@ export class NbDatepickerDirective implements OnDestroy, ControlValueAccessor } this.picker.valueChange - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((value: D) => { this.writePicker(value); this.writeInput(value); @@ -462,7 +463,7 @@ export class NbDatepickerDirective implements OnDestroy, ControlValueAccessor fromEvent(this.input, 'blur').pipe( filter(() => !this.picker.isShown && this.document.activeElement !== this.input), ), - ).pipe(takeWhile(() => this.alive)) + ).pipe(takeUntil(this.destroy$)) .subscribe(() => this.onTouched()); } @@ -486,7 +487,7 @@ export class NbDatepickerDirective implements OnDestroy, ControlValueAccessor fromEvent(this.input, 'input') .pipe( map(() => this.inputValue), - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe((value: string) => this.handleInputChange(value)); } diff --git a/src/framework/theme/components/layout/layout.component.ts b/src/framework/theme/components/layout/layout.component.ts index f7e1aa7d4a..a069513015 100644 --- a/src/framework/theme/components/layout/layout.component.ts +++ b/src/framework/theme/components/layout/layout.component.ts @@ -9,8 +9,8 @@ import { Renderer2, ViewChild, ViewContainerRef, Inject, PLATFORM_ID, } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; -import { BehaviorSubject } from 'rxjs'; -import { filter, takeWhile } from 'rxjs/operators'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { filter, takeUntil } from 'rxjs/operators'; import { convertToBoolProperty } from '../helpers'; import { NbThemeService } from '../../services/theme.service'; @@ -210,7 +210,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { protected afterViewInit$ = new BehaviorSubject(null); - private alive: boolean = true; + private destroy$ = new Subject(); constructor( protected themeService: NbThemeService, @@ -230,7 +230,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { this.themeService.onThemeChange() .pipe( - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe((theme: any) => { const body = this.document.getElementsByTagName('body')[0]; @@ -242,7 +242,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { this.themeService.onAppendLayoutClass() .pipe( - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe((className: string) => { this.renderer.addClass(this.elementRef.nativeElement, className); @@ -250,7 +250,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { this.themeService.onRemoveLayoutClass() .pipe( - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe((className: string) => { this.renderer.removeClass(this.elementRef.nativeElement, className); @@ -259,7 +259,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { this.spinnerService.registerLoader(new Promise((resolve, reject) => { this.afterViewInit$ .pipe( - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe((_) => resolve()); })); @@ -267,7 +267,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { this.rulerService.onGetDimensions() .pipe( - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe(({ listener }) => { listener.next(this.getDimensions()); @@ -276,7 +276,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { this.scrollService.onGetPosition() .pipe( - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe(({ listener }) => { listener.next(this.getScrollPosition()); @@ -287,7 +287,7 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { .shouldRestore() .pipe( filter(() => this.restoreScrollTopValue), - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe(() => { this.scroll(0, 0); @@ -319,18 +319,19 @@ export class NbLayoutComponent implements AfterViewInit, OnDestroy { ngAfterViewInit() { this.layoutDirectionService.onDirectionChange() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(direction => this.document.dir = direction); this.scrollService.onManualScroll() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(({ x, y }: NbScrollPosition) => this.scroll(x, y)); this.afterViewInit$.next(true); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); this.unregisterAsOverlayContainer(); } diff --git a/src/framework/theme/components/list/infinite-list.directive.ts b/src/framework/theme/components/list/infinite-list.directive.ts index 114e3c1cea..cfb7f087b4 100644 --- a/src/framework/theme/components/list/infinite-list.directive.ts +++ b/src/framework/theme/components/list/infinite-list.directive.ts @@ -10,8 +10,8 @@ import { ContentChildren, QueryList, } from '@angular/core'; -import { Observable, forkJoin, of as observableOf, interval, timer } from 'rxjs'; -import { takeWhile, filter, switchMap, map, takeUntil, take } from 'rxjs/operators'; +import { Observable, forkJoin, of as observableOf, interval, timer, Subject } from 'rxjs'; +import { filter, switchMap, map, takeUntil, take } from 'rxjs/operators'; import { convertToBoolProperty } from '../helpers'; import { NbLayoutScrollService } from '../../services/scroll.service'; import { NbLayoutRulerService } from '../../services/ruler.service'; @@ -56,7 +56,7 @@ export class NbScrollableContainerDimentions { }) export class NbInfiniteListDirective implements AfterViewInit, OnDestroy { - private alive = true; + private destroy$ = new Subject(); private lastScrollPosition; windowScroll = false; private get elementScroll() { @@ -109,25 +109,25 @@ export class NbInfiniteListDirective implements AfterViewInit, OnDestroy { ngAfterViewInit() { this.scrollService.onScroll() .pipe( - takeWhile(() => this.alive), filter(() => this.windowScroll), switchMap(() => this.getContainerDimensions()), + takeUntil(this.destroy$), ) .subscribe(dimentions => this.checkPosition(dimentions)); this.listItems.changes .pipe( - takeWhile(() => this.alive), // For some reason, changes are emitted before list item removed from dom, // so dimensions will be incorrect. // Check every 50ms for a second if dom and query are in sync. // Once they synchronized, we can get proper dimensions. switchMap(() => interval(50).pipe( - takeUntil(timer(1000)), filter(() => this.inSyncWithDom()), take(1), + takeUntil(timer(1000)), )), switchMap(() => this.getContainerDimensions()), + takeUntil(this.destroy$), ) .subscribe(dimentions => this.checkPosition(dimentions)); @@ -135,7 +135,8 @@ export class NbInfiniteListDirective implements AfterViewInit, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } checkPosition({ scrollHeight, scrollTop, clientHeight }: NbScrollableContainerDimentions) { diff --git a/src/framework/theme/components/list/list-page-tracker.directive.ts b/src/framework/theme/components/list/list-page-tracker.directive.ts index 95fa4285b9..fee3bec664 100644 --- a/src/framework/theme/components/list/list-page-tracker.directive.ts +++ b/src/framework/theme/components/list/list-page-tracker.directive.ts @@ -9,7 +9,8 @@ import { Output, EventEmitter, } from '@angular/core'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import 'intersection-observer'; import { NbListItemComponent } from './list.component'; @@ -24,7 +25,7 @@ import { NbListItemComponent } from './list.component'; }) export class NbListPageTrackerDirective implements AfterViewInit, OnDestroy { - private alive = true; + private destroy$ = new Subject(); private observer: IntersectionObserver; private currentPage: number; @@ -63,7 +64,7 @@ export class NbListPageTrackerDirective implements AfterViewInit, OnDestroy { } this.listItems.changes - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => this.observeItems()); } diff --git a/src/framework/theme/components/menu/menu.component.ts b/src/framework/theme/components/menu/menu.component.ts index e1a8364df0..072a3d3542 100644 --- a/src/framework/theme/components/menu/menu.component.ts +++ b/src/framework/theme/components/menu/menu.component.ts @@ -18,8 +18,8 @@ import { } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; import { Router, NavigationEnd } from '@angular/router'; -import { BehaviorSubject } from 'rxjs'; -import { takeWhile, filter, map } from 'rxjs/operators'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { takeUntil, filter, map } from 'rxjs/operators'; import { NbMenuInternalService, NbMenuItem, NbMenuBag, NbMenuService } from './menu.service'; import { convertToBoolProperty } from '../helpers'; import { NB_WINDOW } from '../../theme.options'; @@ -50,7 +50,7 @@ export class NbMenuItemComponent implements DoCheck, AfterViewInit, OnDestroy { @Output() selectItem = new EventEmitter(); @Output() itemClick = new EventEmitter(); - protected alive = true; + protected destroy$ = new Subject(); toggleState: NbToggleStates; constructor(protected menuService: NbMenuService, @@ -63,15 +63,16 @@ export class NbMenuItemComponent implements DoCheck, AfterViewInit, OnDestroy { ngAfterViewInit() { this.menuService.onSubmenuToggle() .pipe( - takeWhile(() => this.alive), filter(({ item }) => item === this.menuItem), map(({ item }: NbMenuBag) => item.expanded), + takeUntil(this.destroy$), ) .subscribe(isExpanded => this.toggleState = isExpanded ? NbToggleStates.Expanded : NbToggleStates.Collapsed); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } onToggleSubMenu(item: NbMenuItem) { @@ -251,7 +252,7 @@ export class NbMenuComponent implements OnInit, AfterViewInit, OnDestroy { } protected _autoCollapse: boolean = false; - protected alive: boolean = true; + protected destroy$ = new Subject(); constructor(@Inject(NB_WINDOW) protected window, @Inject(PLATFORM_ID) protected platformId, @@ -265,24 +266,24 @@ export class NbMenuComponent implements OnInit, AfterViewInit, OnDestroy { this.menuInternalService .onAddItem() .pipe( - takeWhile(() => this.alive), filter((data: { tag: string; items: NbMenuItem[] }) => this.compareTag(data.tag)), + takeUntil(this.destroy$), ) .subscribe(data => this.onAddItem(data)); this.menuInternalService .onNavigateHome() .pipe( - takeWhile(() => this.alive), filter((data: { tag: string; items: NbMenuItem[] }) => this.compareTag(data.tag)), + takeUntil(this.destroy$), ) .subscribe(() => this.navigateHome()); this.menuInternalService .onGetSelectedItem() .pipe( - takeWhile(() => this.alive), filter((data: { tag: string; listener: BehaviorSubject }) => this.compareTag(data.tag)), + takeUntil(this.destroy$), ) .subscribe((data: { tag: string; listener: BehaviorSubject }) => { data.listener.next({ tag: this.tag, item: this.getSelectedItem(this.items) }); @@ -291,15 +292,15 @@ export class NbMenuComponent implements OnInit, AfterViewInit, OnDestroy { this.menuInternalService .onCollapseAll() .pipe( - takeWhile(() => this.alive), filter((data: { tag: string }) => this.compareTag(data.tag)), + takeUntil(this.destroy$), ) .subscribe(() => this.collapseAll()); this.router.events .pipe( - takeWhile(() => this.alive), filter(event => event instanceof NavigationEnd), + takeUntil(this.destroy$), ) .subscribe(() => { this.menuInternalService.selectFromUrl(this.items, this.tag, this.autoCollapse); @@ -339,7 +340,8 @@ export class NbMenuComponent implements OnInit, AfterViewInit, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } protected navigateHome() { diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index c6e21cadf9..a941f774df 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -21,8 +21,8 @@ import { } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { fromEvent, merge } from 'rxjs'; -import { filter, switchMap, takeUntil, takeWhile } from 'rxjs/operators'; +import { fromEvent, merge, Subject } from 'rxjs'; +import { filter, switchMap, takeUntil } from 'rxjs/operators'; import { convertToBoolProperty, emptyStatusWarning } from '../helpers'; import { NB_DOCUMENT } from '../../theme.options'; import { NbRadioComponent } from './radio.component'; @@ -80,7 +80,7 @@ import { NbComponentStatus } from '../component-status'; }) export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, ControlValueAccessor { - protected alive: boolean = true; + protected destroy$ = new Subject(); protected onChange = (value: any) => {}; protected onTouched = () => {}; @@ -156,7 +156,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr Promise.resolve().then(() => this.updateAndSubscribeToRadios()); this.radios.changes - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => { // 'changes' emit during change detection run and we can't update // option properties right of since they already was initialized. @@ -167,7 +167,8 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } registerOnChange(fn: any): void { @@ -220,8 +221,12 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr merge(...this.radios.map((radio: NbRadioComponent) => radio.valueChange)) .pipe( - takeWhile(() => this.alive), - takeUntil(this.radios.changes), + takeUntil( + merge( + this.radios.changes, + this.destroy$, + ), + ), ) .subscribe((value: any) => { this.writeValue(value); @@ -243,14 +248,18 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr const hostElement = this.hostElement.nativeElement; fromEvent(hostElement, 'focusin') .pipe( - takeWhile(() => this.alive), filter(event => hostElement.contains(event.target as Node)), switchMap(() => merge( fromEvent(this.document, 'focusin'), fromEvent(this.document, 'click'), )), filter(event => !hostElement.contains(event.target as Node)), - takeUntil(this.radios.changes), + takeUntil( + merge( + this.radios.changes, + this.destroy$, + ), + ), ) .subscribe(() => this.onTouched()); } diff --git a/src/framework/theme/components/search/search.component.ts b/src/framework/theme/components/search/search.component.ts index de0d70734d..83883e1eb8 100644 --- a/src/framework/theme/components/search/search.component.ts +++ b/src/framework/theme/components/search/search.component.ts @@ -22,8 +22,8 @@ import { } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; -import { of as observableOf } from 'rxjs'; -import { filter, delay, takeWhile } from 'rxjs/operators'; +import { of as observableOf, Subject } from 'rxjs'; +import { filter, delay, takeUntil } from 'rxjs/operators'; import { NbSearchService } from './search.service'; import { NbThemeService } from '../../services/theme.service'; @@ -238,7 +238,7 @@ export type NbSearchType = 'modal-zoomin' | 'rotate-layout' | 'modal-move' | }) export class NbSearchComponent implements OnInit, OnDestroy { - private alive = true; + private destroy$ = new Subject(); private overlayRef: NbOverlayRef; showSearchField = false; @@ -284,22 +284,22 @@ export class NbSearchComponent implements OnInit, OnDestroy { ngOnInit() { this.router.events .pipe( - takeWhile(() => this.alive), filter(event => event instanceof NavigationEnd), + takeUntil(this.destroy$), ) .subscribe(() => this.hideSearch()); this.searchService.onSearchActivate() .pipe( - takeWhile(() => this.alive), filter(data => !this.tag || data.tag === this.tag), + takeUntil(this.destroy$), ) .subscribe(() => this.openSearch()); this.searchService.onSearchDeactivate() .pipe( - takeWhile(() => this.alive), filter(data => !this.tag || data.tag === this.tag), + takeUntil(this.destroy$), ) .subscribe(() => this.hideSearch()); } @@ -310,7 +310,8 @@ export class NbSearchComponent implements OnInit, OnDestroy { this.overlayRef.detach(); } - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } openSearch() { diff --git a/src/framework/theme/components/select/option-group.component.ts b/src/framework/theme/components/select/option-group.component.ts index d3248e5a37..338dcba849 100644 --- a/src/framework/theme/components/select/option-group.component.ts +++ b/src/framework/theme/components/select/option-group.component.ts @@ -14,7 +14,8 @@ import { OnDestroy, QueryList, } from '@angular/core'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { convertToBoolProperty } from '../helpers'; import { NbOptionComponent } from './option.component'; @@ -30,7 +31,7 @@ import { NbOptionComponent } from './option.component'; }) export class NbOptionGroupComponent implements AfterContentInit, OnDestroy { - protected alive = true; + protected destroy$ = new Subject(); @Input() title: string; @@ -60,12 +61,13 @@ export class NbOptionGroupComponent implements AfterContentInit, OnDestroy { } this.options.changes - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => this.asyncUpdateOptionsDisabledState()); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } /** diff --git a/src/framework/theme/components/select/select.component.ts b/src/framework/theme/components/select/select.component.ts index 848fbf6b30..380eb2a055 100644 --- a/src/framework/theme/components/select/select.component.ts +++ b/src/framework/theme/components/select/select.component.ts @@ -25,8 +25,8 @@ import { ViewChild, } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { merge } from 'rxjs'; -import { startWith, switchMap, takeWhile, filter } from 'rxjs/operators'; +import { merge, Subject } from 'rxjs'; +import { startWith, switchMap, takeUntil, filter } from 'rxjs/operators'; import { NbAdjustableConnectedPositionStrategy, @@ -729,6 +729,8 @@ export class NbSelectComponent implements AfterViewInit, AfterContentInit, On protected alive: boolean = true; + protected destroy$ = new Subject(); + protected keyManager: NbFocusKeyManager>; /** @@ -812,9 +814,9 @@ export class NbSelectComponent implements AfterViewInit, AfterContentInit, On ngAfterContentInit() { this.options.changes .pipe( - takeWhile(() => this.alive), startWith(this.options), filter(() => this.queue != null && this.canSelectValue()), + takeUntil(this.destroy$), ) .subscribe(() => { // Call 'writeValue' when current change detection run is finished. @@ -837,6 +839,9 @@ export class NbSelectComponent implements AfterViewInit, AfterContentInit, On ngOnDestroy() { this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); + if (this.ref) { this.ref.dispose(); } @@ -1020,7 +1025,7 @@ export class NbSelectComponent implements AfterViewInit, AfterContentInit, On protected subscribeOnPositionChange() { this.positionStrategy.positionChange - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((position: NbPosition) => { this.overlayPosition = position; this.cd.detectChanges(); @@ -1039,7 +1044,7 @@ export class NbSelectComponent implements AfterViewInit, AfterContentInit, On switchMap((options: QueryList>) => { return merge(...options.map(option => option.click)); }), - takeWhile(() => this.alive), + takeUntil(this.destroy$), ) .subscribe((clickedOption: NbOptionComponent) => this.handleOptionClick(clickedOption)); } @@ -1047,8 +1052,8 @@ export class NbSelectComponent implements AfterViewInit, AfterContentInit, On protected subscribeOnOverlayKeys(): void { this.ref.keydownEvents() .pipe( - takeWhile(() => this.alive), filter(() => this.isOpen), + takeUntil(this.destroy$), ) .subscribe((event: KeyboardEvent) => { if (event.keyCode === ESCAPE) { @@ -1060,7 +1065,7 @@ export class NbSelectComponent implements AfterViewInit, AfterContentInit, On }); this.keyManager.tabOut - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => { this.hide(); this.onTouched(); diff --git a/src/framework/theme/components/sidebar/sidebar.component.ts b/src/framework/theme/components/sidebar/sidebar.component.ts index cb0b56f3f9..fef2bd5116 100644 --- a/src/framework/theme/components/sidebar/sidebar.component.ts +++ b/src/framework/theme/components/sidebar/sidebar.component.ts @@ -5,8 +5,8 @@ */ import { Component, HostBinding, Input, OnInit, OnDestroy, ElementRef, OnChanges } from '@angular/core'; -import { Subscription } from 'rxjs'; -import { takeWhile } from 'rxjs/operators'; +import { Subscription, Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { convertToBoolProperty } from '../helpers'; import { NbThemeService } from '../../services/theme.service'; @@ -143,7 +143,7 @@ export class NbSidebarComponent implements OnChanges, OnInit, OnDestroy { protected stateValue: string; protected responsiveValue: boolean = false; - private alive = true; + private destroy$ = new Subject(); containerFixedValue: boolean = true; @@ -302,7 +302,7 @@ export class NbSidebarComponent implements OnChanges, OnInit, OnDestroy { ngOnInit() { this.sidebarService.onToggle() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((data: { compact: boolean, tag: string }) => { if (!this.tag || this.tag === data.tag) { this.toggle(data.compact); @@ -310,7 +310,7 @@ export class NbSidebarComponent implements OnChanges, OnInit, OnDestroy { }); this.sidebarService.onExpand() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((data: { tag: string }) => { if (!this.tag || this.tag === data.tag) { this.expand(); @@ -318,7 +318,7 @@ export class NbSidebarComponent implements OnChanges, OnInit, OnDestroy { }); this.sidebarService.onCollapse() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((data: { tag: string }) => { if (!this.tag || this.tag === data.tag) { this.collapse(); @@ -326,7 +326,7 @@ export class NbSidebarComponent implements OnChanges, OnInit, OnDestroy { }); this.sidebarService.onCompact() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((data: { tag: string }) => { if (!this.tag || this.tag === data.tag) { this.compact(); @@ -335,7 +335,8 @@ export class NbSidebarComponent implements OnChanges, OnInit, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); if (this.mediaQuerySubscription) { this.mediaQuerySubscription.unsubscribe(); } diff --git a/src/framework/theme/components/tree-grid/tree-grid-cell.component.ts b/src/framework/theme/components/tree-grid/tree-grid-cell.component.ts index bd6d10baab..bbd8e665da 100644 --- a/src/framework/theme/components/tree-grid/tree-grid-cell.component.ts +++ b/src/framework/theme/components/tree-grid/tree-grid-cell.component.ts @@ -16,7 +16,8 @@ import { } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; -import { filter, takeWhile } from 'rxjs/operators'; +import { filter, takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NbLayoutDirectionService } from '../../services/direction.service'; import { NB_WINDOW } from '../../theme.options'; @@ -37,7 +38,7 @@ import { NbColumnsService } from './tree-grid-columns.service'; providers: [{ provide: NbCdkCell, useExisting: NbTreeGridCellDirective }], }) export class NbTreeGridCellDirective extends NbCellDirective implements OnInit, OnDestroy { - private alive: boolean = true; + private destroy$ = new Subject(); private readonly tree: NbTreeGridComponent; private readonly columnDef: NbTreeGridColumnDefDirective; private initialLeftPadding: string = ''; @@ -93,14 +94,15 @@ export class NbTreeGridCellDirective extends NbCellDirective implements OnInit, this.columnService.onColumnsChange() .pipe( - takeWhile(() => this.alive), filter(() => this.latestWidth !== this.tree.getColumnWidth()), + takeUntil(this.destroy$), ) .subscribe(() => this.cd.detectChanges()); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } toggleRow(): void { @@ -144,7 +146,7 @@ export class NbTreeGridCellDirective extends NbCellDirective implements OnInit, providers: [{ provide: NbCdkHeaderCell, useExisting: NbTreeGridHeaderCellDirective }], }) export class NbTreeGridHeaderCellDirective extends NbHeaderCellDirective implements OnInit, OnDestroy { - private alive: boolean = true; + private destroy$ = new Subject(); private latestWidth: string; private readonly tree: NbTreeGridComponent; @@ -168,14 +170,15 @@ export class NbTreeGridHeaderCellDirective extends NbHeaderCellDirective impleme ngOnInit() { this.columnService.onColumnsChange() .pipe( - takeWhile(() => this.alive), filter(() => this.latestWidth !== this.tree.getColumnWidth()), + takeUntil(this.destroy$), ) .subscribe(() => this.cd.detectChanges()); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } @@ -188,7 +191,7 @@ export class NbTreeGridHeaderCellDirective extends NbHeaderCellDirective impleme providers: [{ provide: NbCdkFooterCell, useExisting: NbTreeGridFooterCellDirective }], }) export class NbTreeGridFooterCellDirective extends NbFooterCellDirective implements OnInit, OnDestroy { - private alive: boolean = true; + private destroy$ = new Subject(); private latestWidth: string; private readonly tree: NbTreeGridComponent; @@ -212,13 +215,14 @@ export class NbTreeGridFooterCellDirective extends NbFooterCellDirective impleme ngOnInit() { this.columnService.onColumnsChange() .pipe( - takeWhile(() => this.alive), filter(() => this.latestWidth !== this.tree.getColumnWidth()), + takeUntil(this.destroy$), ) .subscribe(() => this.cd.detectChanges()); } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/framework/theme/components/tree-grid/tree-grid-filter.ts b/src/framework/theme/components/tree-grid/tree-grid-filter.ts index 7f290ca9b0..e38d963e2f 100644 --- a/src/framework/theme/components/tree-grid/tree-grid-filter.ts +++ b/src/framework/theme/components/tree-grid/tree-grid-filter.ts @@ -6,7 +6,7 @@ import { Directive, HostListener, Input, OnDestroy, OnInit } from '@angular/core'; import { Subject } from 'rxjs'; -import { debounceTime, takeWhile } from 'rxjs/operators'; +import { debounceTime, takeUntil } from 'rxjs/operators'; import { NbFilterable } from './data-source/tree-grid-data-source'; @@ -28,7 +28,7 @@ export class NbFilterDirective { }) export class NbFilterInputDirective extends NbFilterDirective implements OnInit, OnDestroy { private search$: Subject = new Subject(); - private alive: boolean = true; + private destroy$ = new Subject(); @Input('nbFilterInput') filterable: NbFilterable; @@ -41,8 +41,8 @@ export class NbFilterInputDirective extends NbFilterDirective implements OnInit, ngOnInit() { this.search$ .pipe( - takeWhile(() => this.alive), debounceTime(this.debounceTime), + takeUntil(this.destroy$), ) .subscribe((query: string) => { super.filter(query) @@ -50,7 +50,8 @@ export class NbFilterInputDirective extends NbFilterDirective implements OnInit, } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); this.search$.complete(); } diff --git a/src/framework/theme/components/tree-grid/tree-grid.component.ts b/src/framework/theme/components/tree-grid/tree-grid.component.ts index 170781b93b..6f19bbbda5 100644 --- a/src/framework/theme/components/tree-grid/tree-grid.component.ts +++ b/src/framework/theme/components/tree-grid/tree-grid.component.ts @@ -19,8 +19,8 @@ import { OnDestroy, QueryList, } from '@angular/core'; -import { fromEvent, merge } from 'rxjs'; -import { debounceTime, takeWhile } from 'rxjs/operators'; +import { fromEvent, merge, Subject } from 'rxjs'; +import { debounceTime, takeUntil } from 'rxjs/operators'; import { NB_DOCUMENT, NB_WINDOW } from '../../theme.options'; import { NbPlatform } from '../cdk/platform/platform-service'; @@ -154,7 +154,7 @@ export class NbTreeGridComponent extends NbTable(); private _source: NbTreeGridDataSource; private platform: NbPlatform; @@ -201,7 +201,7 @@ export class NbTreeGridComponent extends NbTable this.alive)) + rowsChange$.pipe(takeUntil(this.destroy$)) .subscribe(() => this.checkDefsCount()); if (this.platform.isBrowser) { @@ -209,14 +209,15 @@ export class NbTreeGridComponent extends NbTable this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe(() => this.updateVisibleColumns()); } } ngOnDestroy() { super.ngOnDestroy(); - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } toggleRow(row: NbTreeGridRowComponent, options?: NbToggleOptions): void { diff --git a/src/playground/with-layout/menu/menu-service.component.ts b/src/playground/with-layout/menu/menu-service.component.ts index 05e8bb149a..64f3a220b8 100644 --- a/src/playground/with-layout/menu/menu-service.component.ts +++ b/src/playground/with-layout/menu/menu-service.component.ts @@ -6,7 +6,8 @@ import { Component, OnDestroy } from '@angular/core'; import { NbMenuService } from '@nebular/theme'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { MENU_ITEMS } from './menu-service-items'; @Component({ @@ -30,13 +31,14 @@ export class MenuServiceComponent implements OnDestroy { menuItems = MENU_ITEMS; - private alive: boolean = true; + private destroy$ = new Subject(); selectedItem: string; constructor(private menuService: NbMenuService) { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } addMenuItem() { @@ -58,7 +60,7 @@ export class MenuServiceComponent implements OnDestroy { getSelectedItem() { this.menuService.getSelectedItem('menu') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe( (menuBag) => { this.selectedItem = menuBag.item.title; }); diff --git a/src/playground/with-layout/oauth2/oauth2-callback.component.ts b/src/playground/with-layout/oauth2/oauth2-callback.component.ts index d03b56e9ae..332fa43c05 100644 --- a/src/playground/with-layout/oauth2/oauth2-callback.component.ts +++ b/src/playground/with-layout/oauth2/oauth2-callback.component.ts @@ -7,7 +7,8 @@ import { Component, OnDestroy } from '@angular/core'; import { NbAuthResult, NbAuthService } from '@nebular/auth'; import { Router } from '@angular/router'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; @Component({ selector: 'nb-playground-oauth2-callback', @@ -19,11 +20,11 @@ import { takeWhile } from 'rxjs/operators'; }) export class OAuth2CallbackComponent implements OnDestroy { - alive = true; + private destroy$ = new Subject(); constructor(private authService: NbAuthService, private router: Router) { this.authService.authenticate('google') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { if (authResult.isSuccess() && authResult.getRedirect()) { this.router.navigateByUrl(authResult.getRedirect()); @@ -32,6 +33,7 @@ export class OAuth2CallbackComponent implements OnDestroy { } ngOnDestroy(): void { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/playground/with-layout/oauth2/oauth2-login.component.ts b/src/playground/with-layout/oauth2/oauth2-login.component.ts index 799234f23a..05e20c0652 100644 --- a/src/playground/with-layout/oauth2/oauth2-login.component.ts +++ b/src/playground/with-layout/oauth2/oauth2-login.component.ts @@ -6,7 +6,8 @@ import { Component, OnDestroy } from '@angular/core'; import { NbAuthOAuth2Token, NbAuthResult, NbAuthService } from '@nebular/auth'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; @Component({ selector: 'nb-playground-auth', @@ -30,11 +31,11 @@ export class OAuth2LoginComponent implements OnDestroy { token: NbAuthOAuth2Token; - alive = true; + private destroy$ = new Subject(); constructor(private authService: NbAuthService) { this.authService.onTokenChange() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((token: NbAuthOAuth2Token) => { this.token = null; if (token && token.isValid()) { @@ -45,19 +46,20 @@ export class OAuth2LoginComponent implements OnDestroy { login() { this.authService.authenticate('google') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { }); } logout() { this.authService.logout('google') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { }); } ngOnDestroy(): void { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/playground/without-layout/azure/azure-callback.component.ts b/src/playground/without-layout/azure/azure-callback.component.ts index 4e64340106..14d7b00ebb 100644 --- a/src/playground/without-layout/azure/azure-callback.component.ts +++ b/src/playground/without-layout/azure/azure-callback.component.ts @@ -7,7 +7,8 @@ import { Component, OnDestroy } from '@angular/core'; import { NbAuthResult, NbAuthService } from '@nebular/auth'; import { Router } from '@angular/router'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; @Component({ selector: 'nb-playground-azure-callback', @@ -19,11 +20,11 @@ import { takeWhile } from 'rxjs/operators'; }) export class AzureCallbackComponent implements OnDestroy { - alive = true; + private destroy$ = new Subject(); constructor(private authService: NbAuthService, private router: Router) { this.authService.authenticate('azure') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { if (authResult.isSuccess() && authResult.getRedirect()) { this.router.navigateByUrl(authResult.getRedirect()); @@ -32,6 +33,7 @@ export class AzureCallbackComponent implements OnDestroy { } ngOnDestroy(): void { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/playground/without-layout/azure/azure-login.component.ts b/src/playground/without-layout/azure/azure-login.component.ts index 9de05ef0f8..690bafede3 100644 --- a/src/playground/without-layout/azure/azure-login.component.ts +++ b/src/playground/without-layout/azure/azure-login.component.ts @@ -6,7 +6,8 @@ import { Component, OnDestroy } from '@angular/core'; import { NbAuthResult, NbAuthService } from '@nebular/auth'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { AuthAzureToken } from './azure-adb2c-auth-strategy'; @Component({ @@ -31,11 +32,11 @@ export class AzureLoginComponent implements OnDestroy { token: AuthAzureToken; - alive = true; + private destroy$ = new Subject(); constructor(private authService: NbAuthService) { this.authService.onTokenChange() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((token: AuthAzureToken) => { this.token = null; if (token && token.isValid()) { @@ -46,19 +47,20 @@ export class AzureLoginComponent implements OnDestroy { login() { this.authService.authenticate('azure') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { }); } logout() { this.authService.logout('azure') - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((authResult: NbAuthResult) => { }); } ngOnDestroy(): void { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/src/playground/without-layout/menu/menu-test.component.ts b/src/playground/without-layout/menu/menu-test.component.ts index 73d6642a3c..51d7fddb39 100644 --- a/src/playground/without-layout/menu/menu-test.component.ts +++ b/src/playground/without-layout/menu/menu-test.component.ts @@ -5,7 +5,8 @@ */ import { Component, OnInit, OnDestroy } from '@angular/core'; -import { takeWhile } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; import { NbMenuService, NbMenuItem } from '@nebular/theme'; @Component({ @@ -160,19 +161,19 @@ export class MenuTestComponent implements OnInit, OnDestroy { }, ]; - private alive: boolean = true; + private destroy$ = new Subject(); constructor(private menuService: NbMenuService) { } ngOnInit() { this.menuService .onItemClick() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((data: { tag: string; item: NbMenuItem }) => console.info(data)); this.menuService .onItemSelect() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((data: { tag: string; item: NbMenuItem }) => console.info(data)); // this.itemHoverSubscription = this.menuService.onItemHover() @@ -180,7 +181,7 @@ export class MenuTestComponent implements OnInit, OnDestroy { this.menuService .onSubmenuToggle() - .pipe(takeWhile(() => this.alive)) + .pipe(takeUntil(this.destroy$)) .subscribe((data: { tag: string; item: NbMenuItem }) => console.info(data)); this.menuService.addItems( @@ -226,7 +227,8 @@ export class MenuTestComponent implements OnInit, OnDestroy { } ngOnDestroy() { - this.alive = false; + this.destroy$.next(); + this.destroy$.complete(); } addMenuItem() { diff --git a/tslint.json b/tslint.json index aba8c58d4a..f0f0f8176f 100644 --- a/tslint.json +++ b/tslint.json @@ -1,4 +1,7 @@ { + "extends": [ + "rxjs-tslint-rules" + ], "rulesDirectory": [ "node_modules/codelyzer" ], @@ -131,7 +134,13 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "no-unused-variable": [true, {"ignore-pattern": "^set"}] + "no-unused-variable": [true, {"ignore-pattern": "^set"}], + "rxjs-no-unsafe-takeuntil": { + "options": [{ + "allow": ["count", "defaultIfEmpty", "endWith", "every", "finalize", "finally", "isEmpty", "last", "max", "min", "publish", "publishBehavior", "publishLast", "publishReplay", "reduce", "share", "shareReplay", "skipLast", "takeLast", "throwIfEmpty", "toArray"] + }], + "severity": "error" + } }, "linterOptions": { "exclude": [