2

I'm encountering an issue where an async observable in my view doesn't show the observable's updated values.

As I understood it, you should be able to expose the latest observed value in your view by simply using an async pipe in the template (e.g. <p>Hello, {{ name | async }}</p>). In this case, though, the new value is being emitted outside Angular's world view which seems to cause Angular to ignore the updated value until something else triggers a view update.

I've also tried subscribing to the observable in my component's constructor and manually exposing the updated values as properties on the component, but that didn't work either.

Here's my minimum repo case.

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

import { Component } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
  selector: 'my-app',
  // Replace with your markup
  template: `
    <pre>{{ pumpObs | async }}</pre>
    <button (click)="noop">click me</button>
  `,
  styles: [ `h2 { font-weight: normal; }`]
})

export class AppComponent  {
  pumpObs = new Observable(observer => {
    observer.next("New observable");

    // Expose a function to modify the observable's value
    (<any>window).pump = val => {
      console.log(`pumpObs: ${val}`);
      observer.next(val);
    };

    // Noop unsubscribe handler
    return this.noop;
  });

  noop = () => {};
}

You can view a live demo at https://stackblitz.com/edit/angular-issue-repro2-dmkbhg

Simeon Vincent
  • 373
  • 1
  • 11

1 Answers1

0

As is the way of these things, I got an answer shortly after posting this. Via @fmalcher01:

The AsyncPipe does markForCheck(), thus makes the component to be checked in the next CD run. However, pump() runs outside the NgZone, so it won't make notice of the data changed and no CD runs. If you trigger CD manually by calling detectChanges() it will work. (tweet)

Same works when you execute the pump() code within the NgZone with NgZone.run() (tweet)

After I got that info, I found the following SO question: Triggering change detection manually in Angular

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

import { Component, ChangeDetectorRef } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
  selector: 'my-app',
  // Replace with your markup
  template: `
    <pre>{{ pumpObs | async }}</pre>
    <button (click)="noop">click me</button>
  `,
  styles: [ `h2 { font-weight: normal; }`]
})

export class AppComponent  {
  pumpObs = new Observable(observer => {
    observer.next("New observable");

    // Expose a function to modify the observable's value
    (<any>window).pump = val => {
      console.log(`pumpObs: ${val}`);
      observer.next(val);
      this.cd.detectChanges();
    };

    // Noop unsubscribe handler
    return this.noop;
  });

  noop = () => {};

  constructor(public cd: ChangeDetectorRef) {}

You can see the working demo at https://stackblitz.com/edit/angular-issue-repro2-2trexs

Simeon Vincent
  • 373
  • 1
  • 11