55

I have a problem where my view will not change when I update my variable in my observable subscription. I am trying to show a loading spinner while I wait for a response from my backend and then display the response, but the spinner won't hide. My subscription looks like this:

this.isRequesting = "true";
this.questionService.onSubmit(form, this.questions).subscribe( (value) => {
        this._ngZone.run( () => {
             this.fileLocation = JSON.stringify(value);
             console.log(this.fileLocation);
            this.isRequesting = "false";
            console.log(this.isRequesting);
        });
    });

And my html for this component looks like this:

<spinner *ngIf="isRequesting=='true'"></spinner>

I can see that isRequesting changes to "false" in my console after I get a response back from the backend (Springboot), but the view still does not change. The spinner was from SpinKit and I modified this tutorial for my spinner to work.

I have tried:

  1. The way in the tutorial (with stopRefreshing() in the error and complete parameters of the observable)
  2. ngZone to force my view to update.

Does anyone have any ideas on how to get my view to update or any way to get my spinner to hide after I get a response from my backend?

D. Simon
  • 571
  • 1
  • 5
  • 8

4 Answers4

98

are you seeing any error on console? This could be due to fact that angular is not running change detection after you have made updates to value. I think you don't need ngZone.run as it will run code outside the angular zone.

this.questionService.onSubmit(form, this.questions).subscribe( (value) => {
            this.fileLocation = JSON.stringify(value);
            console.log(this.fileLocation);
            this.isRequesting = "false";
            console.log(this.isRequesting);
    });

If due to some reason you need to run this outside Angular zone, then you should inform angular to run a change detection cycle by either of this method.

  • just wrap update of isRequesting in set timeout

    setTimeout( () => this.isRequesting = "false", 0);
    
  • invoking change detection manually

    import {ChangeDetectorRef} from '@angular/core'
    constructor(private ref: ChangeDetectorRef){}
    
    this.questionService.onSubmit(form, this.questions).subscribe( (value)=> {
        //existing code
        console.log(this.isRequesting);
        this.ref.detectChanges();
    });
    
kairos
  • 103
  • 8
Arpit Agarwal
  • 3,993
  • 24
  • 30
  • 10
    Well, this is embarrassing. I had no errors and had tried the first solution, but your solution using `ChangeDetectionRef` made me realize I had set my `ChangeDetectionStrategy` to `OnPush`. That was the issue! Thank you so much for your help! – D. Simon Jul 19 '16 at 13:06
  • Had the same issue with onPush. Observables were firing in console.log but not in template. Disabled onPush and now tempalte is updated. – Adrian Moisa Feb 03 '17 at 19:39
  • 4
    It's `ChangeDetectorRef`, not `ChangeDetectionRef` – Daniele Mar 17 '17 at 11:38
  • 1
    I had the animations problem. The solution was removing ChangeDetectorRef and doing what OP was trying: wrap it in ngZone.run. – Hugo Passos May 23 '18 at 14:00
  • @ArpitAgarwal ngZone.run() runs Code IN Angular zone, not outside of it -> see https://angular.io/api/core/NgZone#run It is there, as far as I understood it, to have code that is running outside the Angular zone return back to the Angular zone, so to speak. To run code outside, you would call ngZone.runOutsideAngular() – seBaka28 Oct 02 '18 at 15:40
  • Thank you. The Angular docs doesn't properly explain this IMO. But this makes clear sense to me... – BBi7 May 06 '20 at 02:34
21

You can also try this solution: Guide from Thoughtram

Short version:

 import { ChangeDetectorRef } from '@angular/core';
    ...
    constructor(private cd: ChangeDetectorRef) {}
    ...
    ... .subscribe( () => {
           <yourstuff>
           this.cd.markForCheck();
        }

and you are good.

Background: when changing a value via .subscribe() angular does not notify that it should run changedetection. So you need to run it yourself.

Flosut Mözil
  • 395
  • 4
  • 12
  • 6
    Why would a value changed via `subscribe` not trigger change detection? Is this something I could manage with the "scheduler" notion in rxjs? –  Jul 13 '18 at 12:48
  • 3
    Is there a documentation to know why changing a value via .subscribe() angular does not kicks its change detection? – Ashwin Sep 07 '18 at 12:19
  • I'm amazed that there is not an answers to these last 2 comments. Like it has to be documented. Anyone managed to find it in docs? – Jake_3H Nov 01 '22 at 15:25
3

You can switch (on/off) template updating via changeDetection. You have to put your choice of strategy into component definition. You can choose Default or OnPush strategy.

@Component({
    selector: '...............',
    templateUrl: '.......html',
    styleUrls: ['..........scss'],
    changeDetection: ChangeDetectionStrategy.Default
})
MacAndrzej
  • 31
  • 2
0

if you are using it in expression *ngIf remember *ngIf = false will not work as expression you need to add

<ul *ngIf=" somefunction(x) ? false: true">
ElasticCode
  • 7,311
  • 2
  • 34
  • 45