2

I'm using Angular 4+, and I noticed Angular is not updating the binded value before I call Observable.subscribe() when the Observable resolves immediately (synchronously?). In other places this works fine, because a network call delays the Observable completion long enough for the initial binding change to take effect in Angular.

https://plnkr.co/edit/LV0On7?p=preview

//our root app component
import {Component, VERSION} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/delay';

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>{{name}}</h2>
      <button class="btn btn-primary" (click)="onAlertClickedNoDelay()">Call Observable Without Delay</button>
      <button class="btn btn-primary" (click)="onAlertClickedDelay()">Call Observable With Delay</button>

      <alert *ngIf="isAlertVisible()" type="success" dismissible="true" (onClose)="onAlertClose()">
          Async action successful!
      </alert>
    </div>
  `,
})
export class AppComponent {
  name:string;
  private isCallComplete = false;
  isAlertVisible(): boolean {
    console.log("isAlertVisible() binding updating: ", this.isCallComplete);
    return this.isCallComplete;
  }
  constructor() {
    this.name = `Angular! v${VERSION.full}`
  }

   onAlertClickedNoDelay() {
    this.isCallComplete = false;
    console.log("set isCallComplete = false");
    Observable.of("some data").subscribe(data => {
      this.isCallComplete = true;
      console.log("set isCallComplete = true");
    });
  }

   onAlertClickedDelay() {
    this.isCallComplete = false;
    console.log("set isCallComplete = false");
    Observable.of("some data").delay(0).subscribe(data => {
      this.isCallComplete = true;
      console.log("set isCallComplete = true");
    });
  }

  onAlertClose() {
    this.isCallComplete = false;
    console.log("set isCallComplete = false");
  }
}

Without Observable.delay(), console prints:

set isCallComplete = false
set isCallComplete = true
isAlertVisible() binding updating: true
isAlertVisible() binding updating: true

When using Observable.delay(), console correctly prints:

set isCallComplete = false
isAlertVisible() binding updating: false
isAlertVisible() binding updating: false
set isCallComplete = true
isAlertVisible() binding updating: true
isAlertVisible() binding updating: true

1) Why does Angular detect the initial property binding (isCallComplete = false) when using Observable.delay(0).subscribe() and not Observable.subscribe()?

2) What is the best solution to bind to a busy property value before a Observable.subscribe() call?

Chris Moore
  • 475
  • 1
  • 5
  • 12
  • 1
    You can use one the methods suggested in [this answer](https://stackoverflow.com/a/34829089/1009922) to trigger change detection before subscribing to the observable. – ConnorsFan Apr 18 '18 at 18:16
  • Thanks, ChangeDetectorRef.detectChanges() seems the best workaround. Though I hate doing that everywhere I use an isBusy property. – Chris Moore Apr 18 '18 at 18:30

1 Answers1

3

Because Angular never gets the chance to check the value of isCallComplete.

Your Observable's subscribe callback gets executed right after it is created.

Using delay(0) would be the equivalent of using setTimeout(()=>/*...*/,0), thus delaying the execution of the subscribe callback after the current synchronous code has executed (in this case Angular checking your component's values)

Gobius Dolhain
  • 326
  • 3
  • 6
  • Thanks as this confirms what I was guessing was happening with change detection. To be safe, I'll let Angular know manually about the property change before the observable call. – Chris Moore Apr 18 '18 at 18:32