slander
slander

Reputation: 111

Angular ngOnChanges not updating view

TL;DR: ngOnChanges shows that changes are being detected on an input property, but the view is not being updated

I'm working on an Angular (2+) app trying to make a progress bar for an asynchronous task being fulfilled by a service using an Observable. The Observable pushes numbers as data to indicate what percent of the task has been completed. There are three pieces involved: The Service, the ResultsComponent, and the ProgressBarComponent (child of ResultsComponent) The basic data flow is:

  1. Number pushed from the Observable to ResultsComponent
  2. ResultsComponent.percentLoaded is set to equal this number
  3. ProgressBarComponent uses percentLoaded as an Input and view is updated

The problem is that the view isn't actually being updated.

In ResultsComponent I have this block:

this.service.doTheTask()
    .subscribe(
      data => {
        this.percentLoaded = data;
        this.ref.detectChanges();
      },
      e => console.log('error:', e),
      () =>  {
        this.isLoading = false;
        this.percentLoaded = 0;
      }
    );

Adding ref.detectChanges() successfully makes the OnChanges hook fire in ProgressBarComponent. Component and template are shown below:

Component

export class ProgressBarComponent implements OnChanges {
  @Input() percentLoaded = 0;

  constructor() { }

  ngOnChanges(changes) {
    console.log('changes: ', changes);
  }
}

Template

<div class="meter">
  <div class="percent" [style.width.%]="percentLoaded"></div>
</div>
<p>{{percentLoaded}}% Loaded</p>

As you can see, I'm just logging changes to test. I've verified that ngOnChanges is firing, and that the values are correct. Everything I've read says "Once changes are detected, the view automatically updates" so I don't know what else to do.

Any suggestions?

Upvotes: 4

Views: 6517

Answers (2)

user1483908
user1483908

Reputation: 21

I had a similar issue and after spending about an hour trying to figure it out I remembered adding change detection strategy onPush to the parent component.

As soon as I removed that the child component started working again. The ngOnChanges method was firing but my view was not updating in the subscribe closure.

Upvotes: 0

Yevgen
Yevgen

Reputation: 4709

After your async task is done you overwrite data that you received from subscription with following line this.percentLoaded = 0;. Delete it:

() =>  {
   this.isLoading = false;
   //delete this line this.percentLoaded = 0;
}

Also your change detection is not triggered, because change detection on inputs is triggered only when reference of object is changed. Number is a simple, not a reference variable, so in your case change detection is not triggered. My suggestion is to subscribe to service in both components and trigger change detection only from child component.

export class ProgressBarComponent {
  percentLoaded: number = 0;

  constructor(private ref: ChangeDetectorRef, private service: TheService) { }

  this.service.doTheTask()
   .subscribe(
     data => {
     this.percentLoaded = data;
     this.ref.detectChanges();
   },
}

Upvotes: 0

Related Questions