为什么在 Angular 16 中将 Observable 转换为 WritableSignal 会抛出缺少属性的错误

2 angular angular-signals angular16

我的组件上有以下简单代码:

import {Component, effect, signal, WritableSignal} from '@angular/core';
import {AppService} from "../app.service";
import {toSignal} from "@angular/core/rxjs-interop";

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent {

  translations: WritableSignal<{data: {}}> = signal({data: []});

  constructor( private appService: AppService) {
    this.translations = toSignal(this.appService.getTranslations());
    effect(() => {
      console.log('translation API:', this.translations());
    });
  }

  changeValue(): void {
    this.translations.set({data: {hello: 'hallo'}})

  }
}
Run Code Online (Sandbox Code Playgroud)

仅供参考:this.appService.getTranslations()返回一个可观察的

我正在尝试 Angular v16 发布的新功能,以及如何将 Observables 转换为 Signals。

我想要对上面的代码做的是,更改 WritableSignal 对象的值并记录更改时的值。

我收到以下错误:

TS2739: Type 'Signal ' is missing the following properties from type 'WritableSignal{ data: {}; }>': set, update, mutate, asReadonly
Run Code Online (Sandbox Code Playgroud)

请帮助。

kag*_*ole 5

可观察量是“只读”的,因此不适合WritableSignal. 您可以订阅它们,但不能调用next推送新数据之类的方法。
主题可能会转换为 a WritableSignal,但据我所知,没有实用方法。

您收到的错误意味着Signal由 返回的对象toSignal缺少来自WritableSignal所有“写入”属性/方法;Signal是 的“只读”版本WritableSignal

对于这种情况,您应该订阅您的可观察对象,并在回调中设置信号。
如果在幕后AppService使用,则无需取消订阅,因为它是在第一次成功回调时自动完成的。HttpClient

constructor(private appService: AppService) {
  this.appService.getTranslations().subscribe(data => {
    this.translations.set(data)
  });
  effect(() => {
    console.log('translation API:', this.translations());
  });
}
Run Code Online (Sandbox Code Playgroud)

如果它不使用HttpClient,并且您只想在构造时检索一次数据,请使用first()RxJS 运算符。

constructor(private appService: AppService) {
  this.appService.getTranslations().pipe(first()).subscribe(data => {
    this.translations.set(data)
  });
  effect(() => {
    console.log('translation API:', this.translations());
  });
}
Run Code Online (Sandbox Code Playgroud)

如果它没有使用HttpClient,并且您需要定期更新,则必须在销毁时取消订阅。
由于您正在测试 Angular 16 开发人员预览版,因此您可以使用新版本DestroyRef而不是挂钩OnDestroy.ngOnDestroy

constructor(
  private appService: AppService,
  destroyRef: DestroyRef
) {
  const sub = this.appService.getTranslations().subscribe(data => {
    this.translations.set(data)
  });
  destroyRef.onDestroy(() => {
    sub.unsubscribe();
  });
  effect(() => {
    console.log('translation API:', this.translations());
  });
}
Run Code Online (Sandbox Code Playgroud)

最后,如果您仍然想使用toSignal,您的translations字段必须为 类型Signal。但是,您的changeValue方法将不再有效,因为Signal没有方法set