-
Notifications
You must be signed in to change notification settings - Fork 24.8k
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
Batch of signal-related updates #49529
Conversation
* | ||
* @publicApi | ||
*/ | ||
export function assertInInjectionContext(debugFn: Function): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as an idea: we can consider exposing a function like isInInjectionContext
, which would return true/false. Using this function developer may be able to implement the necessary assertions and/or other logic as needed (and it's a bit easier to work with vs a function that throws).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should.
- APIs shouldn't sneakily change their behavior depending on whether they're in an injection context. The "right" thing to do is throw if called from the wrong context.
- Having one public API function to check the injection context and throw allows us to give a very standard error message with good guidance on how to solve the problem across all of the APIs that rely on
inject
.
440bedd
to
c010ed1
Compare
// Casting here is required for g3. | ||
const signalFn = createSignalFromFunction(signalNode.signal.bind(signalNode), { | ||
set: signalNode.set.bind(signalNode), | ||
update: signalNode.update.bind(signalNode), | ||
mutate: signalNode.mutate.bind(signalNode), | ||
}) as unknown as SettableSignal<T>; | ||
}) as unknown as WritableSignal<T>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super-nit: we've got the // Casting here is required for g3
which is pretty enigmatic - could we expand it to say "why" it is needed in g3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
@@ -96,8 +96,8 @@ class WritableSignalImpl<T> implements Producer { | |||
* @developerPreview | |||
*/ | |||
export function signal<T>( | |||
initialValue: T, equal: ValueEqualityFn<T> = defaultEquals): WritableSignal<T> { | |||
const signalNode = new WritableSignalImpl(initialValue, equal); | |||
initialValue: T, options?: {equal?: ValueEqualityFn<T>}): WritableSignal<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit / thinking out loud: should we introduce a specific type for options? Or is it just too much hassle / too many symbols in the public API surface?
Asking since I can see the CreateEffectOptions
in the later commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alxhub I went through this PR and most changes LGTM. Is there anything that you think is still needed to get this PR over the finish line?
One thing I'm seeing as still needing work is the G3 failures that seem legit (hence adding cleanup label). Also there are open comments from @AndrewKushnir .
import {bootstrapApplication} from '@angular/platform-browser'; | ||
import {withBody} from '@angular/private/testing'; | ||
|
||
describe('effects', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
existing effect tests should be moved here as well, right? Otherwise we start to have 2 implementations in 2 separate places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have two sets of tests:
- tests for the signal lib itself, independent of FW
- tests that validate FW integration with signals
c010ed1
to
0824e60
Compare
4e50882
to
f6bae6d
Compare
f6bae6d
to
aa1ce5c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reviewed-for: fw-core, public-api
f87363b
to
379e5e5
Compare
This commit adds an assertion function to the public API, which allows authors of functions which rely on `inject` to validate that they're being called with the right context. This mostly produces a nicer error message than calling `inject()` and relying on Angular's default error message for that.
This commit renames the `SettableSignal` interface to align with recent naming changes. `WritableSignal` is considered a more suitable name since it supports more mutation operations than just directly setting.
This commit switches the `signal` and `computed` APIs to accept an optional options argument as their second argument, instead of an equality function directly. The equality function has moved to an option in the options argument.
…tion The `effect` implemented in the signal library is useful for testing but does not integrate with Angular. This commit moves that code to the actual framework package and integrates it with automatic cleanup via `DestroyRef`. A simpler effect implementation is used in the signal tests to test the `Watch` primitive. Further commits will update the scheduling to tie effects together with change detection.
This commit adds a `DeepReadonly` type to the signals API, and makes signal getters return an immutable version of their value type. This doesn't prevent all mutation but adds some friction against modifying the values within signals outside of the proper mutation APIs.
Previously, effects were queued in the global microtask queue and executed directly. This had an undesired consequence: whichever effect scheduled first determined the zone in which all other effects would run, *and* that zone depended on where the signal happened to be set which triggered that first effect. This behavior would be extremely unpredictable. This commit adds zone awareness to the effect API. effects now capture the current zone when they're created, and this zone is used to run the effect callback regardless of which zone set the signal.
379e5e5
to
11fb5e4
Compare
Previously the signals implementation maintained separate interfaces for `Producer` and `Consumer` nodes, with implementers choosing to implement one or both interfaces. Operations defined against those interfaces were exposed as importable functions to be called with the object implementing the relevant contract as the first argument. This commit refactors the implementation to merge both abstractions into a single `ReactiveNode` base class, which represents both producers and consumers in the graph. Implementers choose to interact with a subset of the `ReactiveNode` API depending on their role in the graph. Operations are now available as protected methods on the base class, instead of separate functions.
Signal functions are marked with a `[SIGNAL]` symbol property, which was previously set to `true`. This commit changes the `[SIGNAL]` property to have the `ReactiveNode` as its value instead. This doesn't change the behavior of the signal library itself, but exposes the signal internals in a way where developer tooling can look at any signal and interact with its dependency graph.
11fb5e4
to
1e8f12a
Compare
This PR was merged into the repository by commit ef149de. |
This commit renames the `SettableSignal` interface to align with recent naming changes. `WritableSignal` is considered a more suitable name since it supports more mutation operations than just directly setting. PR Close #49529
…tion (#49529) The `effect` implemented in the signal library is useful for testing but does not integrate with Angular. This commit moves that code to the actual framework package and integrates it with automatic cleanup via `DestroyRef`. A simpler effect implementation is used in the signal tests to test the `Watch` primitive. Further commits will update the scheduling to tie effects together with change detection. PR Close #49529
) Previously, effects were queued in the global microtask queue and executed directly. This had an undesired consequence: whichever effect scheduled first determined the zone in which all other effects would run, *and* that zone depended on where the signal happened to be set which triggered that first effect. This behavior would be extremely unpredictable. This commit adds zone awareness to the effect API. effects now capture the current zone when they're created, and this zone is used to run the effect callback regardless of which zone set the signal. PR Close #49529
Previously the signals implementation maintained separate interfaces for `Producer` and `Consumer` nodes, with implementers choosing to implement one or both interfaces. Operations defined against those interfaces were exposed as importable functions to be called with the object implementing the relevant contract as the first argument. This commit refactors the implementation to merge both abstractions into a single `ReactiveNode` base class, which represents both producers and consumers in the graph. Implementers choose to interact with a subset of the `ReactiveNode` API depending on their role in the graph. Operations are now available as protected methods on the base class, instead of separate functions. PR Close #49529
…49529) Signal functions are marked with a `[SIGNAL]` symbol property, which was previously set to `true`. This commit changes the `[SIGNAL]` property to have the `ReactiveNode` as its value instead. This doesn't change the behavior of the signal library itself, but exposes the signal internals in a way where developer tooling can look at any signal and interact with its dependency graph. PR Close #49529
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. |
(more coming)