You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular中基于Signal与Observable创建计算信号?

Angular中根据Signal类型ID获取Signal类型数据的正确实现

场景描述

在Angular开发中,常遇到这类需求:基于Signal类型的实体ID从服务端获取数据,同时希望返回的数据也为Signal类型。

错误实现及问题

初始错误代码

@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())));
}

// 模拟API请求
const getRoles = (userId: string) => {
  return userId === 'userA' ? of([1, 2, 3]) : of([1]);
};

第一个运行时错误

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.

尝试修复后的代码(仍报错)

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

第二个运行时错误

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.

错误原因分析

  1. NG0203错误:toSignal必须在Angular的注入上下文(如构造函数、字段初始化器、工厂函数等)中调用,而computed的回调函数不属于该上下文,直接调用会触发报错。
  2. NG0600错误:computed是纯计算逻辑,不允许在其内部执行Signal写入操作(toSignal内部会创建并更新Signal),即使传入Injector也违反了这个规则。

正确实现方式

方案一:使用toObservable + switchMap + toSignal

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule],
  template: `
  <code>{{roles()}}</code>
  `,
})
export class App {
  userId: Signal<string> = signal('userA');
  // 将Signal转为Observable,监听ID变化
  private userId$ = toObservable(this.userId);
  
  // 通过switchMap切换请求,最终转为Signal
  roles = toSignal(
    this.userId$.pipe(switchMap(userId => getRoles(userId))),
    { initialValue: [] } // 指定初始值避免视图报错
  );
}

// 模拟API请求
const getRoles = (userId: string) => {
  return userId === 'userA' ? of([1, 2, 3]) : of([1]);
};

方案二:使用effect更新Signal

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule],
  template: `
  <code>{{roles()}}</code>
  `,
})
export class App {
  userId: Signal<string> = signal('userA');
  // 初始化空数组作为默认值
  roles = signal<number[]>([]);

  constructor(private injector: Injector) {
    // 监听userId变化,发起请求并更新roles
    effect(() => {
      const currentUserId = this.userId();
      getRoles(currentUserId).subscribe(fetchedRoles => {
        this.roles.set(fetchedRoles);
      });
    }, { injector }); // 传入Injector确保effect在注入上下文运行
  }
}

// 模拟API请求
const getRoles = (userId: string) => {
  return userId === 'userA' ? of([1, 2, 3]) : of([1]);
};

内容的提问来源于stack exchange,提问作者George Knap

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:42:15