I have two unrelated components that use a service to communicate data. I am now trying to get the value when the value in the service has changed.
Here is my code
Service
public keyboardValue: boolean = false;
public setKeyboardValue() {
this.keyboardValue = !this.keyboardValue;
}
public getKeyboardValue() {
return this.keyboardValue;
}
Component A
isKeyboardActive() {
this.registrationService.setKeyboardValue();
}
<div (click)="isKeyboardActive()">...</div>
Component B - trying to get the changed value here
public keyboardValue: boolean = false;
ngOnInit(): void {
this.keyboardValue = this.registrationService.getKeyboardValue();
console.log(this.keyboardValue);
}
How can I get the updated value in component B everytime the user clicks?
Edit:
I now tried the following in my code but still have the issue
Service
public keyboardValue: boolean = false;
public keyboardValue$ = new BehaviorSubject<boolean>(false);
public setKeyboardValue() {
this.keyboardValue = !this.keyboardValue;
this.keyboardValue$.next(this.keyboardValue);
console.log(this.keyboardValue);
}
public getKeyboardValue(): Observable<boolean> {
return this.keyboardValue$;
}
Component B
//This is not seeing the changes
this.registrationService.keyboardValue$.subscribe(data=> {
console.log(data);
});
CodePudding user response:
Observables solve the exact problem you are describing:
"get the value when the value in the service has changed"
Here's how to achieve this:
- In your service:
- expose public observables that emit current value
- provide public methods that update state which is then emitted through exposed observables
- In your components:
- subscribe to observables (will always receive newest values)
- can call public service methods to cause service state to be updated
When you do this, it doesn't matter what component causes a state change. The public observables will always emit the most recent value to all consumers.
Here's a basic service with one public observable and one public method to toggle the value:
@Injectable({ providedIn: 'root' })
export class SharedService {
private toggleKeyboardValue$ = new BehaviorSubject<void>(null);
public keyboardValue$ = this.toggleKeyboardValue$.pipe(
scan(previous => !previous, false)
);
public toggleKeyboardValue() {
this.toggleKeyboardValue$.next();
}
}
The exact implementation isn't as pertinent to your question, but the main thing is that any component can listen to keyboardValue$
and always receive updates when that value changes. And, any component could call toggleKeyboardValue()
which will cause keyboardValue$
to emit the updated value.
In the components, we can just define the data as an observable and leverage the Async Pipe in the template. This way, we don't need to worry about unsubscribing, since the Async Pipe will handle that for us:
ts:
keyboardValue$ = this.service.keyboardValue$;
constructor(private service: SharedService) { }
toggle() {
this.service.toggleKeyboardValue();
}
html:
<p> Keyboard Value: {{ keyboardValue$ | async }} </p>
<button (click)="toggle()"> Toggle Keyboard Value </button>
Here's a little StackBlitz demo