Imagine standard situation in Angular: You need to fetch data from server for some entity ID. Entity Id is a Signal and you want the fetched data to be signal too.
Something like this feels natural:
@Component({
selector: 'my-app',
standalone: true,
imports: [CommonModule],
template: `
<code>{{roles()}}</code>
`,
})
export class App {
userId: Signal<string> = signal('userA');
roles = computed(() => toSignal(getRoles(this.userId())));
}
//this could be api service sending http request
const getRoles = (userId: string) => {
return userId === 'userA' ? of([1, 2, 3]) : of([1]);
};
but there is a runtime errror in browser console:
Error: NG0203: toSignal() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`. Find more at https://angular.io/errors/NG0203
Stackblitz demo here
UPDATE: I also tried to provide Injector into toSignal:
constructor(private injector: Injector) {}
userId: Signal<string> = signal('userA');
roles = computed(() =>
toSignal(getRoles(this.userId()), { injector: this.injector })()
);
but then another runtime error:
Error: NG0600: Writing to signals is not allowed in a `computed` or an `effect` by default. Use `allowSignalWrites` in the `CreateEffectOptions` to enable this inside effects.
There is no need for effect, as what you want can be accomplished with the rxjs interop functions.
For the roles signal, the userId has to be converted to an observable with toObservable. Then value of this new observable is piped to a switchMap operator to get the roles value from a service. Finally, the inner observable stream is converted back to a signal by passing it to toSignal.
export class App {
readonly userId = signal('userA');
readonly roles = toSignal(toObservable(this.userId).pipe(
switchMap(userId => getRoles(userId)),
), { initialValue: [] });
}
In the example above, an initial value is provided. You can omit that, but that will create an initial undefined emission.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With