feat(core): add isStable Observable property to ApplicationRef to indicate when it's stable and unstable (#14337)

PR Close #14337
This commit is contained in:
Vikram Subramanian
2017-02-03 05:42:22 -08:00
committed by Miško Hevery
parent b64946b5f9
commit c4817988ca
3 changed files with 197 additions and 7 deletions

View File

@ -6,9 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import {Subject} from 'rxjs/Subject';
import {Subscription} from 'rxjs/Subscription';
import {merge} from 'rxjs/observable/merge';
import {share} from 'rxjs/operator/share';
import {ErrorHandler} from '../src/error_handler';
import {ListWrapper} from '../src/facade/collection';
import {stringify} from '../src/facade/lang';
import {scheduleMicroTask, stringify} from '../src/facade/lang';
import {isPromise} from '../src/util/lang';
import {ApplicationInitStatus} from './application_init';
@ -395,6 +402,11 @@ export abstract class ApplicationRef {
* Returns the number of attached views.
*/
abstract get viewCount(): number;
/**
* Returns an Observable that indicates when the application is stable or unstable.
*/
abstract get isStable(): Observable<boolean>;
}
/**
@ -412,6 +424,8 @@ export class ApplicationRef_ extends ApplicationRef {
private _views: AppView<any>[] = [];
private _runningTick: boolean = false;
private _enforceNoNewChanges: boolean = false;
private _isStable: Observable<boolean>;
private _stable = true;
constructor(
private _zone: NgZone, private _console: Console, private _injector: Injector,
@ -425,6 +439,46 @@ export class ApplicationRef_ extends ApplicationRef {
this._zone.onMicrotaskEmpty.subscribe(
{next: () => { this._zone.run(() => { this.tick(); }); }});
const isCurrentlyStable = new Observable<boolean>((observer: Observer<boolean>) => {
this._stable = this._zone.isStable && !this._zone.hasPendingMacrotasks &&
!this._zone.hasPendingMicrotasks;
this._zone.runOutsideAngular(() => {
observer.next(this._stable);
observer.complete();
});
});
const isStable = new Observable<boolean>((observer: Observer<boolean>) => {
const stableSub: Subscription = this._zone.onStable.subscribe(() => {
NgZone.assertNotInAngularZone();
// Check whether there are no pending macro/micro tasks in the next tick
// to allow for NgZone to update the state.
scheduleMicroTask(() => {
if (!this._stable && !this._zone.hasPendingMacrotasks &&
!this._zone.hasPendingMicrotasks) {
this._stable = true;
observer.next(true);
}
});
});
const unstableSub: Subscription = this._zone.onUnstable.subscribe(() => {
NgZone.assertInAngularZone();
if (this._stable) {
this._stable = false;
this._zone.runOutsideAngular(() => { observer.next(false); });
}
});
return () => {
stableSub.unsubscribe();
unstableSub.unsubscribe();
};
});
this._isStable = merge(isCurrentlyStable, share.call(isStable));
}
attachView(viewRef: ViewRef): void {
@ -510,4 +564,6 @@ export class ApplicationRef_ extends ApplicationRef {
get componentTypes(): Type<any>[] { return this._rootComponentTypes; }
get components(): ComponentRef<any>[] { return this._rootComponents; }
get isStable(): Observable<boolean> { return this._isStable; }
}