Skip to content

Conversation

alxhub
Copy link
Member

@alxhub alxhub commented Dec 3, 2024

No description provided.

@angular-robot angular-robot bot added the area: core Issues related to the framework runtime label Dec 3, 2024
@ngbot ngbot bot added this to the Backlog milestone Dec 3, 2024
@alxhub alxhub force-pushed the experimental/resource/fixes branch from 0bfb183 to 8923549 Compare December 3, 2024 14:54
@angular-robot angular-robot bot added area: core Issues related to the framework runtime and removed area: core Issues related to the framework runtime labels Dec 3, 2024
@ngbot ngbot bot modified the milestone: Backlog Dec 3, 2024
request.set(undefined);
expect(echoResource.status()).toBe(ResourceStatus.Idle);
});
it('set() should abort a pending load', async () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mega-nit: new line to add spacing between tests

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unmergeable.

Copy link
Member

@pkozlowski-opensource pkozlowski-opensource left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Reviewed-for: public-api
Reviewed-for: fw-core

@alxhub alxhub force-pushed the experimental/resource/fixes branch from 8923549 to c9d2703 Compare December 3, 2024 15:03
@angular-robot angular-robot bot added area: core Issues related to the framework runtime and removed area: core Issues related to the framework runtime labels Dec 3, 2024
@ngbot ngbot bot modified the milestone: Backlog Dec 3, 2024
@pullapprove pullapprove bot requested a review from kirjs December 3, 2024 20:49
Copy link
Contributor

@thePunderWoman thePunderWoman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed-for: public-api

@thePunderWoman thePunderWoman added the target: patch This PR is targeted for the next patch release label Dec 3, 2024
@flensrocker
Copy link

I really like the defaultValue on resource. Having not an undefined on the value is always a good thing IMHO.

Thanks for that!

@alxhub alxhub force-pushed the experimental/resource/fixes branch from c9d2703 to b4c6e01 Compare December 19, 2024 12:39
@angular-robot angular-robot bot added area: core Issues related to the framework runtime and removed area: core Issues related to the framework runtime labels Dec 19, 2024
@ngbot ngbot bot modified the milestone: Backlog Dec 19, 2024
@alxhub alxhub force-pushed the experimental/resource/fixes branch from b4c6e01 to 62bdd74 Compare January 14, 2025 14:13
Originally the `T` in `Resource<T>` represented the resolved type of the
resource, and `undefined` was explicitly added to this type in the `.value`
signal. This turned out to be problematic, as it wasn't possible to write a
type for a resource which didn't return `undefined` values. Such a type is
useful for 2 reasons:

1. to support narrowing of the resource type when `Resource.hasValue()`
   returns `true`.

2. for resources which use a different value instead of `undefined` to
   represent not having a value (for example, array resources which want to
   use `[]` as their default).

Instead, this commit changes `resource()` and `rxResource()` to return an
explicit `ResourceRef<T|undefined>`, and removes the union with `undefined`
from all types related to the resource's value. This way, it's trivially
possible to write `Resource<T>` to represent resources where `.value` only
returns `T`.

`hasValue()` then actually works to perform narrowing, by narrowing the
resource type to `Exclude<T, undefined>`.
When the reactive `request` of a resource() notifies, it transitions to the
Loading state, fires the loader, and eventually transitions to Resolved.
With the prior implementation, a change of the `request` will queue the
effect, but the state remains unchanged until the effect actually runs. For
a brief period, the resource is in a state where the request has changed,
but the state has yet to update.

This is problematic if we want to use resources in certain contexts where we
care about the state of the resource in a synchronous way. For example, an
async validator backed by a resource might be checked after an update:

```
value.set(123);

if (validator.value()) {
  // value is still valid, even though the resource is dirty and will soon
  // flip to loading state (returning value(): undefined) while revalidating
}
```

To address this timing concern, `linkedSignal()` is used within the
`resource()` to synchronously transition the state in response to the
request changing. This ensures any followup reads see a consistent view of
the resource regardless of whether the effect has run.

This also addresses a race condition where `.set()` behaves differently on a
`resource()` depending on whether or not the effect has run.
@alxhub alxhub force-pushed the experimental/resource/fixes branch from 62bdd74 to f21a303 Compare January 15, 2025 19:11
@alxhub alxhub added the action: merge The PR is ready for merge by the caretaker label Jan 16, 2025
@AndrewKushnir AndrewKushnir added the action: presubmit The PR is in need of a google3 presubmit label Jan 16, 2025
@alxhub alxhub added action: merge The PR is ready for merge by the caretaker merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note and removed action: merge The PR is ready for merge by the caretaker action: presubmit The PR is in need of a google3 presubmit labels Jan 16, 2025
@alxhub
Copy link
Member Author

alxhub commented Jan 16, 2025

Caretaker: this PR is sensitive to backsliding. we need to patch cl/716243172 so far (and potentially more depending on presubmit failures).

@AndrewKushnir
Copy link
Contributor

This PR was merged into the repository by commit 01fffdb.

The changes were merged into the following branches: main, 19.1.x

AndrewKushnir pushed a commit that referenced this pull request Jan 16, 2025
…gs (#59024)

Originally the `T` in `Resource<T>` represented the resolved type of the
resource, and `undefined` was explicitly added to this type in the `.value`
signal. This turned out to be problematic, as it wasn't possible to write a
type for a resource which didn't return `undefined` values. Such a type is
useful for 2 reasons:

1. to support narrowing of the resource type when `Resource.hasValue()`
   returns `true`.

2. for resources which use a different value instead of `undefined` to
   represent not having a value (for example, array resources which want to
   use `[]` as their default).

Instead, this commit changes `resource()` and `rxResource()` to return an
explicit `ResourceRef<T|undefined>`, and removes the union with `undefined`
from all types related to the resource's value. This way, it's trivially
possible to write `Resource<T>` to represent resources where `.value` only
returns `T`.

`hasValue()` then actually works to perform narrowing, by narrowing the
resource type to `Exclude<T, undefined>`.

PR Close #59024
AndrewKushnir pushed a commit that referenced this pull request Jan 16, 2025
When the reactive `request` of a resource() notifies, it transitions to the
Loading state, fires the loader, and eventually transitions to Resolved.
With the prior implementation, a change of the `request` will queue the
effect, but the state remains unchanged until the effect actually runs. For
a brief period, the resource is in a state where the request has changed,
but the state has yet to update.

This is problematic if we want to use resources in certain contexts where we
care about the state of the resource in a synchronous way. For example, an
async validator backed by a resource might be checked after an update:

```
value.set(123);

if (validator.value()) {
  // value is still valid, even though the resource is dirty and will soon
  // flip to loading state (returning value(): undefined) while revalidating
}
```

To address this timing concern, `linkedSignal()` is used within the
`resource()` to synchronously transition the state in response to the
request changing. This ensures any followup reads see a consistent view of
the resource regardless of whether the effect has run.

This also addresses a race condition where `.set()` behaves differently on a
`resource()` depending on whether or not the effect has run.

PR Close #59024
AndrewKushnir pushed a commit that referenced this pull request Jan 16, 2025
When the reactive `request` of a resource() notifies, it transitions to the
Loading state, fires the loader, and eventually transitions to Resolved.
With the prior implementation, a change of the `request` will queue the
effect, but the state remains unchanged until the effect actually runs. For
a brief period, the resource is in a state where the request has changed,
but the state has yet to update.

This is problematic if we want to use resources in certain contexts where we
care about the state of the resource in a synchronous way. For example, an
async validator backed by a resource might be checked after an update:

```
value.set(123);

if (validator.value()) {
  // value is still valid, even though the resource is dirty and will soon
  // flip to loading state (returning value(): undefined) while revalidating
}
```

To address this timing concern, `linkedSignal()` is used within the
`resource()` to synchronously transition the state in response to the
request changing. This ensures any followup reads see a consistent view of
the resource regardless of whether the effect has run.

This also addresses a race condition where `.set()` behaves differently on a
`resource()` depending on whether or not the effect has run.

PR Close #59024
PrajaktaB27 pushed a commit to PrajaktaB27/angular that referenced this pull request Feb 7, 2025
…gs (angular#59024)

Originally the `T` in `Resource<T>` represented the resolved type of the
resource, and `undefined` was explicitly added to this type in the `.value`
signal. This turned out to be problematic, as it wasn't possible to write a
type for a resource which didn't return `undefined` values. Such a type is
useful for 2 reasons:

1. to support narrowing of the resource type when `Resource.hasValue()`
   returns `true`.

2. for resources which use a different value instead of `undefined` to
   represent not having a value (for example, array resources which want to
   use `[]` as their default).

Instead, this commit changes `resource()` and `rxResource()` to return an
explicit `ResourceRef<T|undefined>`, and removes the union with `undefined`
from all types related to the resource's value. This way, it's trivially
possible to write `Resource<T>` to represent resources where `.value` only
returns `T`.

`hasValue()` then actually works to perform narrowing, by narrowing the
resource type to `Exclude<T, undefined>`.

PR Close angular#59024
PrajaktaB27 pushed a commit to PrajaktaB27/angular that referenced this pull request Feb 7, 2025
When the reactive `request` of a resource() notifies, it transitions to the
Loading state, fires the loader, and eventually transitions to Resolved.
With the prior implementation, a change of the `request` will queue the
effect, but the state remains unchanged until the effect actually runs. For
a brief period, the resource is in a state where the request has changed,
but the state has yet to update.

This is problematic if we want to use resources in certain contexts where we
care about the state of the resource in a synchronous way. For example, an
async validator backed by a resource might be checked after an update:

```
value.set(123);

if (validator.value()) {
  // value is still valid, even though the resource is dirty and will soon
  // flip to loading state (returning value(): undefined) while revalidating
}
```

To address this timing concern, `linkedSignal()` is used within the
`resource()` to synchronously transition the state in response to the
request changing. This ensures any followup reads see a consistent view of
the resource regardless of whether the effect has run.

This also addresses a race condition where `.set()` behaves differently on a
`resource()` depending on whether or not the effect has run.

PR Close angular#59024
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Feb 16, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants