2

When I try setting a signal value from within an Observable that gets piped to async, I get the 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.

But I'm not inside a computed or an effect, I'm inside an Observable. I also only get this through the async pipe; if I subscribe to the Observable and set my data in the next handler, it works as expected. Is this by design, or is it a bug?

refreshTrigger = new BehaviorSubject<void>(undefined);
refreshing = signal(false);
data = this.refreshTrigger.pipe(
  tap(() => {
    console.log("Refreshing");
    this.refreshing.set(true);
  }),
  map(() => "Doing real work here"),
  shareReplay(1)
);
{{refreshing() ? "Refreshing" : "Not refreshing"}} {{data | async}}

5
  • I didn't see any signal code in the linked stackblitz? Commented May 25, 2023 at 23:49
  • It seems unnecessary to have a BehaviorSubject and a signal and react to one to set the other. You should be able to react directly to the signal value. So whatever code is setting refreshTrigger.next(x) could just do refreshing.set(x). Commented May 25, 2023 at 23:52
  • @DeborahK: I would think that the names of the objects would make it perfectly clear that the signal is a side effect of the real action. But I'll take a look at the stackblitz, I definitely had code in it. Commented May 26, 2023 at 16:53
  • Okay, I have no idea what happened to the stackblitz. I guess I'll just go ahead and remove it since I seem to have received an answer without it. Commented May 26, 2023 at 16:58
  • Oops, I thought I had the return from a different rxjs function than the tap, let me fix that. Commented May 26, 2023 at 17:00

1 Answer 1

2

Update

 The bug has been fixed in the Angular 16.1.0-next.3.

It works fine when you make the observable asynchronous, as mentioned by @matthieu-riegler on GitHub.

You can workaround this issue by adding rxjs delay operator

data = this.refreshTrigger.pipe(
    delay(1),
    tap(() => {
      console.log('Refreshing');
      this.refreshing.set(true);
      return 'hello';
    }),
    shareReplay(1)
);
Sign up to request clarification or add additional context in comments.

3 Comments

Sounds promising, I'll try it after the weekend.
I got pulled to another project before I could try this, and it looks like it will be a while before I can return, but I just wanted to update that the GitHub issue you created has been marked as Completed, so it should theoretically be fixed once the next release is ready, which isn't something I've had a need to wait for with Angular before, so I don't know what kind of timeframe that might be.
Also, run-on sentences FTW.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.