Reputation: 774
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
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
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