CH4B
CH4B

Reputation: 774

(Angular) Make global variables always change their values according to a service

I have this problem, a button that has a data-binded label, something like:

<button>{{label}}</button>

I need it to change its values depending what is on the screen. And that is mostly easy, i just change the label value in my component.ts.
The problem is, after somes changes on the system, i need this to happen when i use other component. This button is always on screen, on MainComponent, but when acessing FooComponent (click clicking specific things on screen), i need its label to change.
To do that, i created a Main.service.ts, where i create the label variable, and just call its value in mainComponent.
This is the service:

@Injectable({
  providedIn: 'root'
})
export class MainService {

  constructor() { }

  label = "IntialFoo";

  foo01() {
    this.label = 'foo01';
  }

  foo02() {
    this.label= 'foo02';
  }

And the component:

export class MainComponent extends BaseService<any> implements OnInit {

  constructor(
    private mainService: MainService,
    ...
  ) {
    super(...);
  }

  ...
  label = this.mainService.label;

Now let's say i import my service in my fooComponent, i'd call the service's functions to change the label value, it works, in the service. And that is the problem, the button is not changing its values, and that's because the variable is not listing to the service, it just gets its value once, and then it does not change.
So, how can i make my label variable (in mainComponent) change in real-time its value according to the label in the service?

Upvotes: 0

Views: 1361

Answers (2)

SiddAjmera
SiddAjmera

Reputation: 39482

You can implement a private BehaviorSubject that will be exposed as a public Observable. Something like this:

import { Injectable } from '@angular/service';
import { Observable, BehaviorSubject } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class MainService {

    private label: BehaviorSubject<string> = new BehaviorSubject<string>('IntialFoo');
    label$: Observable<string> = this.label.asObservable();

    updateLabel(updatedLabel) {
        this.label.next(updatedLabel);
    }
}

You can then inject this service as a dependency in your Components and then use the label$ with an async pipe in your Template to avoid manually calling unsubscribe. Something like this:

export class MainComponent extends BaseService < any > implements OnInit {

  constructor(private mainService: MainService,...) {
    ...
  }

  ...

  label$ = this.mainService.label$;

}

And in the template:

<button>{{ label$ | async }}</button>

Upvotes: 2

Paul
Paul

Reputation: 478

I would not extend the component with BaseService as the service should be a singleton.  I would have the service injected into any component that needs the label and the service to return as observable.  Please see code below...

    // Service
    @Injectable({
      providedIn: 'root'
    })
    export class MainService {

      constructor() { }                  
      getLabel(component:string): Observable<string> {
        return Observable.of(component);

      }
    }

    // Component:
    import { takeWhile, switchMap } from 'rxjs/operators';            
    import { OnInit, OnDestroy, Input } from '@angular/core'

    export class MainComponent implements OnInit, OnDestroy {

          constructor(
            private mainService: MainService,
            ...
          ) {

          }
      @Input() passedInLbl:string;
      private label: string;
      private isActive: boolean;
      ngOnInit():void {
          this.isActive = true;
          mainService.getLabel(passedInLabel).pipe(
              switchMap(resp => {
                  this.label = resp;
              }),
              takeWhile(() => this.isActive)
          );
      }
      ngOnDestory(): void {
          this.isActive = false;
      }

    }

Upvotes: 0

Related Questions