Say I have a very simple shared service holding a subject:
@Injectable({ providedIn: 'root' })
export class MyService {
private _runTaskSubject = new Subject<string>();
get runTaskSubject$() {
return this._runTaskSubject.asObservable();
}
runTask(name: string) {
this._runTaskSubject.next(string);
}
}
Observers of that runTaskSubject$
will only get notified once runTask
is called.
Now I was thinking to convert this service using signals
, but it seems that not an option as signals
always hold an initial value. So does it makes sense to keep my rxjs Subject
for this use case?
The signals equivalent for the same is as below.
@Injectable({ providedIn: 'root' })
export class MyService {
private _runTaskSignal = signal<string | undefined>(undefined);
get runTaskSignal() {
return this._runTaskSignal.asReadonly();
}
runTask(name: string) {
this._runTaskSignal.set(name);
}
}
-
We can set the initial value of the signal to be undefined to mimic the subject.
-
We can use
asReadonly
method to convert the signal to areadOnly
signal, the advantage being thereadOnly
signal does not have access toset
andupdate
methods, hence if the signal should be updated, it can be updated only through therunTask
method. -
When you read the signal and do not want it to fire on initial load, all you need to do is check that the value is not
undefined
and then emit values.export class SomeComponent { myService: MyService = inject(MyService); constructor() { effect(() => { // simulate event bus const runTaskValue = this.myService.runTaskSignal(); if(typeof runTaskValue !== 'undefined') { // ... some actions happen here! } }); } ...
Full Code:
import { Component, Injectable, signal, inject, effect } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
@Injectable({ providedIn: 'root' })
export class MyService {
private _runTaskSignal = signal<string | undefined>(undefined);
get runTaskSignal() {
return this._runTaskSignal.asReadonly();
}
runTask(name: string) {
this._runTaskSignal.set(name);
}
}
@Component({
selector: 'app-root',
template: `
<h1>Hello from {{ myService.runTaskSignal() }}!</h1>
`,
})
export class App {
myService: MyService = inject(MyService);
constructor() {
effect(() => {
// simulate event bus
const runTaskValue = this.myService.runTaskSignal();
if (typeof runTaskValue !== 'undefined') {
console.log('value changed: ', runTaskValue);
}
});
}
ngOnInit() {
setInterval(() => {
this.myService.runTask(`${Math.random()}`);
}, 2000);
}
}
bootstrapApplication(App);
Stackblitz Demo
3