如何从信号和可观测值创建计算信号?

Geo*_*nap 9 angular angular-signals

想象一下 Angular 中的标准情况:您需要从服务器获取某些实体 ID 的数据。实体 ID 是一个信号,您希望获取的数据也成为信号。

像这样的事情感觉很自然:


@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]);
};
Run Code Online (Sandbox Code Playgroud)

但浏览器控制台出现运行时错误:

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
Run Code Online (Sandbox Code Playgroud)

Stackblitz 演示在这里

更新:我还尝试提供注入器toSignal

 constructor(private injector: Injector) {}
  userId: Signal<string> = signal('userA');
  roles = computed(() =>
    toSignal(getRoles(this.userId()), { injector: this.injector })()
  );
Run Code Online (Sandbox Code Playgroud)

但另一个运行时错误:

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.
Run Code Online (Sandbox Code Playgroud)

Dan*_*nez 6

不需要effect,因为你想要的可以通过rxjs互操作函数来完成。

对于角色信号,必须使用toObservable将userId转换为可观察值。然后这个新的可观察值的值通过管道传输到switchMap运算符以从服务获取角色值。最后,通过将内部可观察流传递给toSignal将其转换回信号。

export class App {
  readonly userId = signal('userA');

  readonly roles = toSignal(toObservable(this.userId).pipe(
    switchMap(userId => getRoles(userId)),
  ), { initialValue: [] });
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,提供了一个初始值。您可以省略它,但这将创建一个初始未定义的发射。


Mat*_*ler 0

我想说,你必须将信号作为任何异步值处理:

roles = computed(() =>
  getRoles(this.userId())
);
Run Code Online (Sandbox Code Playgroud)

并调用异步管道:

{{roles() | async }
Run Code Online (Sandbox Code Playgroud)

你怎么看待这件事 ?


编辑:关于第二条错误消息,您通过创建信号来写入信号,toSignal()该信号还订阅并设置新值。(参见源代码