New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
toSignal()
can not be called inside computed()
#50106
Comments
This is by design - the Having said this I would be curious to hear more about your use-case, specific tasks that you are trying to cover. |
Obviously you can always do https://stackblitz.com/edit/angular-hvbbhq?devToolsHeight=33&file=src%2Fmain.ts |
I like the design aspect where I use My use-caseI have a library, ngx-collection. It has a lot of observables (17), users are supposed to pick just a few of them to use what is needed. I want to provide signal-based alternatives for every observable. It is better to do this from my side, to don't make users think about things like initial value and injection context. If I will use To avoid that, I use getters: export class CollectionService<T> {
// ...
public get updatingItems(): Signal<T[]> {
return toSignal(this.updatingItems$, {initialValue: [] as T[], injector: this.injector});
}
public get deletingItems(): Signal<T[]> {
return toSignal(this.deletingItems$, {initialValue: [] as T[], injector: this.injector});
}
public get mutatingItems(): Signal<T[]> {
return computed(() => ([
...this.updatingItems(),
...this.deletingItems()
]));
}
// ...
} As you can see, I do not set any Signals inside the If I go this way: public get mutatingItems(): Signal<T[]> {
return toSignal(this.mutatingItems$, {initialValue: [] as T[], injector: this.injector});
} then by the signature, I'm providing a Signal, and everything is fine... until the user will try to use this Signal inside the If I go this way: public readonly updatingItems = toSignal(this.updatingItems$, {initialValue: [] as T[], injector: this.injector}); then I'll have 17 subscriptions "just in case". My main idea is: calling |
The |
This is one of the fundamental differences between signals and Observables - a signal always needs a value so in this sense it can not be "lazy". I see 3 options:
I'm not familiar with the library in question to say which approach is the best |
I'm pretty much aware, it's one of the tradeoffs.
I don't want them to be "lazy", I just want to create them only when it's needed.
None of them, unfortunately, otherwise I wouldn't have created this issue. But still, thank you for your time and ideas. I'll keep looking for a workaround. |
I found a workaround for this use case! I still hope you'll reconsider export class CollectionService<T> {
// ...
private updatingItemsSignal?: Signal<T[]>;
public get updatingItems(): Signal<T[]> {
if (this.updatingItemsSignal) {
return this.updatingItemsSignal;
}
return this.updatingItemsSignal = untracked( // 🙈
() => toSignal(this.updatingItems$, {initialValue: [] as T[], injector: this.injector})
);
}
private mutatingItemsSignal?: Signal<T[]>;
public get mutatingItems(): Signal<T[]> {
if (this.mutatingItemsSignal) {
return this.mutatingItemsSignal;
}
// 👉 here we are getting Signals outside of the `computed()` call
const updatingItems = this.updatingItems;
const deletingItems = this.deletingItems;
return this.mutatingItemsSignal = computed(() => ([
...updatingItems(),
...deletingItems()
]));
}
// ...
} Please close the issue if you think that |
Just in case, all you would need to change is this: next: value => () => state.set({kind: StateKind.Value, value}),
error: error => () => state.set({kind: StateKind.Error, error}), to this: next: value => untracked(() => state.set({kind: StateKind.Value, value})),
error: error => untracked(() => state.set({kind: StateKind.Error, error})), 😉 |
You can certainly cache signals returned from the Thnx for sharing the use-case though! |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
toSignal()
can not set internal state (state.set()
when called fromcomputed()
, because of NG0600 ("Writing to signals is not allowed in acomputed
or aneffect
by default").Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/angular-hjphb7?devToolsHeight=33&file=src/main.ts
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run
ng version
)Anything else?
It is reproducible without Component's template, so the issue is not caused by the template: https://stackblitz.com/edit/angular-rbp4uq?devToolsHeight=33&file=src/main.ts
The text was updated successfully, but these errors were encountered: