Search code examples
angularionic2angular2-changedetection

Angular 4: Change Detection between component and service


I am currently working on a small test project with Ionic/Angular, Before I post the snippets: the problem I have is that I want to send value changes from inside a service (@Injectable) to a component to track it's changes. I have tried EventEmitter and OnChanges, but to no avail..

I have a progressbar that needs a certain value to advance in progress. This is the progressbar: TS:

import {Component, Input} from '@angular/core';


@Component({
  selector: 'progressbar',
  templateUrl: 'progressbar.html'
})
export class ProgressBarComponent {

  @Input('progress') progress;

  constructor() {}
}

html

<div class="progress-outer">
    <div class="progress-inner" [style.width]="progress + '%'">
        {‌{progress}}%
    </div>
</div>

(credit to JoshMorony) The bar's width attribute is connected to the progress, thus enabling it to advance e.g. by percentage.

Now comes the problem:

I injected the progressbar into a normal component, but the calculation for the advancement happens in a different service, an Injectable. I am only able to send a single value, but not the advancement of the calculation and as such the bar itself:

home.ts

showProgressBar: boolean;
// this variable must always have a value between 0 - 100
loadProgress;

triggerEvent(){
this.service.showProgressbar = true;
}

home.html

<progressbar [progress]="loadProgress"></progressbar>

What is done here is simply a call to trigger an event, that includes the logic for that progressBar. By setting service'sshowProgressbar to true, I am indirectly setting the pages showprogressbar to true as well.

Note: the boolean is not used yet

Service looks like this:

denominator: number = 0;
counter: number = 0;
showProgressbar = false;

result: number = 0;
calculateProgress() {
  if (this.showProgressbar = true) {
    let percentage = Math.round((this.counter / this.denominator) * 100);
    this.result = percentage;
    if (this.result == 100) {
      setTimeout(this.showProgressbar = false, 500);
    }
  } else {
    this.counter = 0;
    this.denominator = 0;
    this.result = 0;
  }
}

I checked the calls, result here DOES calculate correctly, but it does not transfer to home.ts unfortunately. If I statically change result to a random number like 50 or so, it will indeed change the bar.

How can I make home.ts "watch" the value of result constantly or in another way how do I implement the change detection for that result value here?

THANKS!


Solution

  • You can create an Observable of the service and subscribe in home.ts

    Your service

    //create a Subject
    private percentSource:Subject<any>=new Subject<any>();
    //create a Observable
    percentEvent:Observable<any>=this.percentSource.asObservable();
    ...
    calculateProgress() {
      if (this.showProgressbar = true) {
        ...
        //send a change of observable with next
        this.percentSource.next(percentage);  //return as result the percent
        ...
      } else {
         ...
      }
    }
    

    then, in your home you can subscribe to the observable in the function tiggerEven or in progressBar.component in the ngOnInit function

    triggerEvent(){
        this.service.showProgressbar = true;
        //takeWhile make you unsubscribe if condition is not successfully
        //NOT put if you subscribe in your progressBar.component
        this.service.percentEvent
        .takeWhile(() =>this.progress!=100)
        .subscribe(result=>  
        {
             this.progress=result;
        } 
    }