Skip to content

Commit

Permalink
docs(core): document isStable traps (#28102)
Browse files Browse the repository at this point in the history
PR Close #28102
  • Loading branch information
cexbrayat authored and matsko committed Feb 6, 2019
1 parent 0e6f799 commit 353362f
Showing 1 changed file with 90 additions and 0 deletions.
90 changes: 90 additions & 0 deletions packages/core/src/application_ref.ts
Expand Up @@ -359,6 +359,94 @@ function optionsReducer<T extends Object>(dst: any, objs: T | T[]): T {
/**
* A reference to an Angular application running on a page.
*
* @usageNotes
*
* {@a is-stable-examples}
* ### isStable examples and caveats
*
* Note two important points about `isStable`, demonstrated in the examples below:
* - the application will never be stable if you start any kind
* of recurrent asynchronous task when the application starts
* (for example for a polling process, started with a `setInterval`, a `setTimeout`
* or using RxJS operators like `interval`);
* - the `isStable` Observable runs outside of the Angular zone.
*
* Let's imagine that you start a recurrent task
* (here incrementing a counter, using RxJS `interval`),
* and at the same time subscribe to `isStable`.
*
* ```
* constructor(appRef: ApplicationRef) {
* appRef.isStable.pipe(
* filter(stable => stable)
* ).subscribe(() => console.log('App is stable now');
* interval(1000).subscribe(counter => console.log(counter));
* }
* ```
* In this example, `isStable` will never emit `true`,
* and the trace "App is stable now" will never get logged.
*
* If you want to execute something when the app is stable,
* you have to wait for the application to be stable
* before starting your polling process.
*
* ```
* constructor(appRef: ApplicationRef) {
* appRef.isStable.pipe(
* first(stable => stable),
* tap(stable => console.log('App is stable now')),
* switchMap(() => interval(1000))
* ).subscribe(counter => console.log(counter));
* }
* ```
* In this example, the trace "App is stable now" will be logged
* and then the counter starts incrementing every second.
*
* Note also that this Observable runs outside of the Angular zone,
* which means that the code in the subscription
* to this Observable will not trigger the change detection.
*
* Let's imagine that instead of logging the counter value,
* you update a field of your component
* and display it in its template.
*
* ```
* constructor(appRef: ApplicationRef) {
* appRef.isStable.pipe(
* first(stable => stable),
* switchMap(() => interval(1000))
* ).subscribe(counter => this.value = counter);
* }
* ```
* As the `isStable` Observable runs outside the zone,
* the `value` field will be updated properly,
* but the template will not be refreshed!
*
* You'll have to manually trigger the change detection to update the template.
*
* ```
* constructor(appRef: ApplicationRef, cd: ChangeDetectorRef) {
* appRef.isStable.pipe(
* first(stable => stable),
* switchMap(() => interval(1000))
* ).subscribe(counter => {
* this.value = counter;
* cd.detectChanges();
* });
* }
* ```
*
* Or make the subscription callback run inside the zone.
*
* ```
* constructor(appRef: ApplicationRef, zone: NgZone) {
* appRef.isStable.pipe(
* first(stable => stable),
* switchMap(() => interval(1000))
* ).subscribe(counter => zone.run(() => this.value = counter));
* }
* ```
*
* @publicApi
*/
@Injectable()
Expand All @@ -384,6 +472,8 @@ export class ApplicationRef {

/**
* Returns an Observable that indicates when the application is stable or unstable.
*
* @see [Usage notes](#is-stable-examples) for examples and caveats when using this API.
*/
// TODO(issue/24571): remove '!'.
public readonly isStable !: Observable<boolean>;
Expand Down

1 comment on commit 353362f

@IgorMinar
Copy link
Contributor

@IgorMinar IgorMinar commented on 353362f Feb 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an awesome contribution! Thank you, @cexbrayat!

Please sign in to comment.