Replies: 22 comments 36 replies
-
I have used the current resource API and I love how it works so much so the explicit I believe is the right choice
the only concern/question I have is how to handle user interactions that needs to reload the data, e.g. in list of things the user want to apply a filter, change sort ... etc how exactly is this going to happen? do we need to send everything through query params so that the router understand that these data are changing? how will we show the user that his request is received and the new data is loading
I think
|
Beta Was this translation helpful? Give feedback.
-
Question 1B: What challenges do you see in your own applications with declaring data dependencies "above" components? I am wondering how we would do this, if the loading would be declared above the components. Here a short example how we do it currently:
|
Beta Was this translation helpful? Give feedback.
-
Overall, I think explicitness async is better than implicit async. A problem that I can think of off top of my mind right now is how the template looks like when we want to kick off multiple resources. Taking the code from the RFC, the Suspense approach might look like this which looks pretty clean imo @suspense {
<user-info [data]="userData()" />
<other-async [data]="otherData()" />
} @fallback {
<loading-spinner />
} Wondering how this would look with Resource approach. Maybe we do need to have a special construct like
Personally, I see this in two folds. SSR and SPA. For SSR applications, this is a godsend; components don't have to deal with the platform to (re)perform fetching for the data that they need. This is also assuming that there is fallback to "fetching on the component" where the users see fit (like initiate a streamed data on user interactions etc...). For SPA though, one challenge I see is handling the loading state; maybe we can provide a UI fallback on the route level (or the level where we declare the data fetching). Overall, I think many apps will still benefit from declaring data fetching above the components; I mean I'm probably not the only one annoyed by the "flashing loading spinner". Coupled with
😅 The "Future research" section kind of answered my questions above.
EDIT: expanding on the "Future Research" section
I think this is desirable for both component-level data fetching as well as non-blocking data. There gotta be a consistent way to read async data on the template on the component level.
Would these 2 be grouped together under one umbrella? Where if an application is SSR, then resource streaming is the default. If an app is CSR, then it's just plain non-blocking (aka resource is only kicked off by the consuming component(s))? But this is definitely a desirable feature. In Remix, we would do something like this export const loader = async () => {
const lightData = await getLightData();
const heavyDataPromise = getHeavyData();
return defer({
lightData, // blocking data
heavyDataPromise, // non-blocking
})
}
export default function MyCmp() {
const { lightData, heavyDataPromise } = useLoaderData<typeof loader>();
// lightData is readily available, heavyDataPromise needs Suspense to start loading in (SSR streams the response in Remix' case)
return (
<div>
<p>{ lightData }</p>
<Suspense fallback={...}>
<Await resolve={heavyDataPromise}>
{(heavyData) => (...)}
</Await>
</Suspense>
</div>
)
}
YES!! Multiple prefetch strategies would be nice
I mentioned this above. This is definitely needed if we lift the data fetching above the components.
I think this is definitely nice to have but if we could have
Considering most Angular apps are still SPA and probably will be SPA (unless more materials about Angular SSR that point people to choose Angular for SSR applications), this is at most a nice to have.
I'm not sure what error recovery means when put together with retry here? Maybe Error Handling in general would be better to explore first. I personally think "retry mechanics" is userland responsibility rather than the framework but I definitely can be convinced otherwise.
This would be nice for type-safety if we can pull this off. I personally like what the ReactRouter team achieves with Route Module or TanStack Start https://tanstack.com/start/latest/docs/framework/react/learn-the-basics |
Beta Was this translation helpful? Give feedback.
-
I wanna add a few short replies; I have used the resource pattern and I love it so in that regard i prefer Explicit I have earlier tried to use SSR/SSG with the current ways of fetching data - and I think it would be amazing to know more how things like resource would work with this It would also be amazing to see how the server Only fetching could look Maybe a example of the route config with resource - i have used resolve for this in the past with mixed success Would it make sense to have a resource with a server and a client config when compiled for the two it strip's the other part i guess it would introduce some type related Challenges |
Beta Was this translation helpful? Give feedback.
-
1. My experience from someone who worked a lot with Observables: PRO: With BehaviorSubject, you always have an initial value, allowing for synchronous actions—such as updating the UI immediately on a button press you can check if its still loading, display an error, etc. Therefore I'm also for: @if (userData.isLoading()) {
<loading-spinner />
} @else {
<user-profile [data]="userData.value()" />
} 2. I would like besides all of this that we refactor the signals to be more like BehaviorSubjects: |
Beta Was this translation helpful? Give feedback.
-
First of all, Let's say I have an http endpoint which returns an object { items: [], total: number }. I'd like to do this:
but I have to do this instead:
otherwise the list and total value will flicker while it loads. and I want to keep the previous value while loading. |
Beta Was this translation helpful? Give feedback.
-
I like the concept of resources. I would only suggest that resources are also suitable for use in route resolvers. I would like to have the opportunity to make sure that a "starting state" is loaded before a component is generated. I do not want to check several places in a template whether the data is already loaded. |
Beta Was this translation helpful? Give feedback.
-
Is there a possibility to have something like queryKey, formed by the query parameters and arbitrary string, to have cached API calls, that will retrigger automatically if some part of query key has changed? Perhaps adding staleTime for resource, that will also retrigger the api call to refresh after it expires? |
Beta Was this translation helpful? Give feedback.
-
Question 1A: that's the right choice :) Question 1B: 1. Lack of reactivity; 2. Many components have no resolvers above - they get their params from inputs and fetch the data using these params. Question 1C: Right now, I don't think so. APIs are not flexible enough - I can't pick between switchMap or concatMap behavior; error handling is "hardcoded." Observables offer much more out of the box, and all you need to make them eager is "Server-only data fetching logic" and "Resource streaming from SSR" - this sounds very interesting! |
Beta Was this translation helpful? Give feedback.
-
In the past I implemented something similar to a "resource" with rxjs, which pushes the state with value/error/busy, which then got subscribed to with the I must confess, that "implicit async" seems more difficult for me, reasoning about how the template really looks like in which state and how to display the previous value while loading the next etc.
In my applications most of the time loading the data is done inside the component, so it can already show its title and a spinner etc. And they almost always depend on some (query)params of the route. Loading that data in a resolver may be ok - I haven't tried yet. And since I treat my route as the source of truth, loading new data is almost always triggered with a navigation - even if only the query params get changed.
It's nice to have a unified way to express an (async) resource. I guess most of us implemented something like this. It will reduce complexity and makes the code more understandable for new developers, because they don't have to learn our own utilities. |
Beta Was this translation helpful? Give feedback.
-
Are we trying to reinvent rxjs? |
Beta Was this translation helpful? Give feedback.
-
Firstly, thank you so much for this RFC. The proposed enhancements will unlock many benefits for Angular developers. While I understand that this is a low-level API and it makes sense in that context, I personally find it challenging to offer comprehensive feedback without knowing how it will integrate with other parts of the framework. Additionally, I believe the main missing point is what role Questions and thoughts, with the honest desire of offering constructive feedback:
In general, I feel excited but can't say I'll start using this right away until the framework provides a fully-fledged solution. And without knowing that, it is hard to provide feedback on a specific design decision or API. I hope you appreciate these comments 😄 |
Beta Was this translation helpful? Give feedback.
-
1ASay: I trust you, but I'm not 100% sure the result is set in stone 😅! Out of the 3 points, I mostly see 2 as the real blocking issue for any concrete suspense / error-boundary approach right now. On the other hand, the ideas around Some of the things I like about suspense in solid v2:
But even for solid, such things are relatively new. So again: I understand the choice! source: https://www.youtube.com/watch?v=xnmvxWEK25I 1BMostly two:
Too early to say... but for such cases, some adjustments might be required. 1CDifficult to say at this point, but I overall like the direction. I'd just keep a close eye on what solid is doing: as ng-devs, we're all used to an explicit (data driven) async approach, but maybe it's not the best deal we can get. Just the most convenient now! |
Beta Was this translation helpful? Give feedback.
-
First, I appreciate the effort the Angular team is putting into making async state management more ergonomic. Resources are a great step forward, providing a much-needed alternative to using Observables for data fetching. However, I’d like to clarify two key points in the RFC:
Suggested Refinements
|
Beta Was this translation helpful? Give feedback.
-
Definitely explicit approach seems to be better. Also I don't understand doubts related with the question how to handle loading data from two different resources before they will be presented in template. Doesn't the rxResource is an answer on that question?
It would be great if it could protect against waterfalling, but what if child component would be displayed only if something specific will come in the response to the query send by parent component? Does the solution would be enough smart to know that data for child component shouldn't be loaded in parallel with data for parent component?
All of that looks very promising, I only wondering, how it will cooperate with state management solution. |
Beta Was this translation helpful? Give feedback.
-
Question 1c: Real world applications. (However, this is of course a complex use case that probably few people will enocunter. Scenario: Currently it is not possible to create resources dynamically. Since the dropdown options depend on another column of the table, we cannot create a resource for each possible value in columnX. Current solution:
Ideal solution (probably to complicated for most users):
Probably to complex of a scenario, but having an easy solution to this issue, would maybe make developers default to cached resources and save a lot of traffic, instead of going for the currently much easier non-cached version even though a cache would be beneficial. |
Beta Was this translation helpful? Give feedback.
-
Fearing I might sound too negative and sound as if I'm against change; but at this moment in time I don't really see how this significantly reduces complexity or addresses real problems in my applications or open-source packages. I think the Angular team will need to explain the difference between Observables and Resource in a clearer way, and why moving from an Observable-as-async-primitive based system to this new system is a benefit. Why we would benefit from rewriting parts of our application to this approach when the current approach seems to work just as well. (Stressing the seems part, as that is at least what's the case to me) Because that's what is holding me back at this point in time, we're adding something new (which by all means, looks good and has some nice features); but at the same time I don't yet see the real big benefits that such a change will accomplish. It doesn't take away from the nice features and the good design approaches the Angular Team is taking in terms of Resource though. Again, I don't want to sound negative and oppose change, but I really need more of a "show don't tell" approach to these things and that's what I'm missing from these kind of RFCs. It's a big change to Angular and a clearer "this is how you do it today, this is how you'd do it in the future, and here's why this is better" approach would make it both easier to understand, give feedback and make it easier to also convince our clients why we need to move to this approach over the old one.
I think it would also make it easier to understand why the Angular team sees it this way, why they define the difference between the two and why a resource is then better than the current Observable based approach. Just to make it clear, I'm not of the opinion that it wouldn't be better or that there isn't a difference between the two at all; but for me this statement alone isn't enough to fully convince me. I just really want to understand why this is the way the Angular team sees it. |
Beta Was this translation helpful? Give feedback.
-
I guess based on what I understand implicit is like freezing the application so it's a sync Async operation, which will block everything even the User interactions, which I will take a quote from @alxhub and say @if(spinnerService.isSpinnerShowen()) {
<my-global-spinner/>
}
<router-outlet</router-outlet> and trigger it with a root-injected service that can handle the appearance of the spinner each time a resolver runs so reaching the point that i will be on the choice of explicit over implicit async =====================================================
i can't say it will reduce complexity, but what i can say it will apply the concept of making the component away from data handling and making the angular framework handle it for the developer which i see it as a good approach
it would be awesome if we have retry automatic mechanism like for example user:Resource<user[]>= httpResource(()=>"/api/users" , {
defaultValue:[],
parser: ...,
retryIfFailes :(number of trails , ()=>{
takes a call back in case of even the number of trails fails let the user handle it
})
}) and still keep the .reload() imperatively Now i want to say I love the Whole RFC and thank you so much #angular team for the hard workThank you @alxhub for such an awesome RFC |
Beta Was this translation helpful? Give feedback.
-
Comment about Error HandlingIn our application, we use Angular's However, after adopting Observed Behavior
Given these behaviors, the question is: Should the |
Beta Was this translation helpful? Give feedback.
-
I just want to chip in on a few things I noticed when discovering resources. The first thing I read is that it shouldn't be used for anything other than GET requests. Which to me sounded weird and when trying it out I noticed that yeah, it isn't really suited to use for anything other than a few use cases that don't even include all the GET requests I do. And sure, there might be logic behind why it is the way it is, but ultimately that means that for me its not a valid use case for most of the API calls I do and I was really looking what the heck it is useful for. I also noticed that it needs to run in an injection context. With the first item in consideration that made sense, but it didn't when I was just trying things out. It seemed odd to me since you don't always know what you need to send to the API. There currently is not really a way to prepare the requests based on information in the component. But also this prevents using shared resources from shared services and whatnot. With RxJS there are ways to prepare the data, to make sure you dont send requests you don't need and to make sure you don't send too many by using debouncing. Something that the current system has no place for. Pre-processing also seems like a fairly common use case. I also noticed that it was already running a request before my signals were getting data from route params. So that was already something I found weird. Being able to get data from ngoninit seems like a logical moment to wait for and right now it just didn't do that. Or making sure that it doesn't send another request with the same data. It also doesn't have a way to post-process. I frequently pipe data to make sure it arrives in the template how I want it and remove any needless nesting I don't want to dive into. Many API's wrap their content (for their own error handing or pagination) and you can't really make sure that the value from the resource is the value that was wrapped. Other times I need to check values for being the data that I actually want to use or to flow into different situations which means that I kinda need to post-process the data. Something that the current situation also doesn't offer. When there was first talk about resources, I thought it was going to replace httpClient. The functionality to do API requests. But since you still need to supply a loader function, it feels like its just a wrapper around API responses and it should probably then just stick to that. Which means I still need an alternative for httpClient to work with signals rather than observables. I don't mind a wrapper for responses and properly handling errors without having to separate that, is something I would definitely use, but overall right now it just doesn't offer anything I need. The usecase is just too limited. I did end up with a working example on a search form (with rxjs debouncing) that would search for data and display results/loading/error but I found it to be tedious to set up, difficult to test (especially mocking it in a sensible way) and thus proceeded to fall back to httpclient. It just makes more sense on how I can pre/postprocess the data and to easily test it as well. I hope httpresource gets changed or different tools get to work together to replace rxjs and use signals instead but right now it just feels like a solution looking for a problem than providing an actual improvement to something we already have. |
Beta Was this translation helpful? Give feedback.
-
I am wondering if there is something planned to combine resources? Let’s assume having a resource named Let’s further assume that we have a view, which should only be shown having both resources successfully loaded. It would be cool having a utility which combines two resources and returns a new one, or even a dedicated type like I would assume that this combined resource would be flagged as loading as long as one of the source resources is loading. I would assume that this combined resource is flagged as erroneous if one of the source resources failed. It should be flagged as loaded and contain the combined data if both resources successfully loaded. Obviously, this could be built in each separate project, but I guess a first class support for use cases like this could be beneficial for the whole community. 😇 |
Beta Was this translation helpful? Give feedback.
-
The Resource Architecture RFC has come to a close! We'd like to extend our thanks to everyone who took the time to read through this fairly high-level proposal and leave us their comments and feedback. As always, we're amazed by the thoughtfulness and care that our community brings to these conversations. Based on your feedback, we're confident moving forward with our plans around an asynchronous architecture for Angular, based on resources and explicit asynchronicity. Most of you expressed strong agreement with our proposed choice of an explicit model for asynchronicity, based around the Resource type to communicate the status of an async dependency or loading operation. You agreed with us that the large-scale drawbacks of an implicit model, where signals look synchronous but might block under the hood, outweigh the small-scale ergonomic benefits. At the same time, many of you described use cases which highlight the complexity of this problem. Some notable examples include:
Going forward, we will take these use cases into consideration both when designing future functionality with resources as well as in example code & communications. We noted some excitement around potential benefits of this architecture, particularly around potential integrations with server-side rendering like server-only data fetching code or data streaming. Another idea which had positive responses was the concept of a template-level mechanism for awaiting a resource, largely because of how it could address some of the cases where resources might not be able to be lifted out of components. In terms of concerns, you highlighted that like with signals, this direction is significantly different than current patterns in Angular. This is true, and we always need to weigh that tradeoff when considering such conceptual changes. We wouldn't propose such a move if we didn't believe the result would be worth it, but such a value proposition will need to be demonstrated. In conclusion, we're moving forward with our ambitions to add first-class support for asynchronous data on top of Resources in Angular, and we look forward to engaging with you in future RFCs as these ideas develop into new designs and APIs of their own. |
Beta Was this translation helpful? Give feedback.
-
Welcome to the Resource RFC! This RFC is split into two parts. The first (this discussion) covers the choice of resource as the async primitive for Angular, and the potential benefits and consequences of that choice for Angular developers and their applications. The second part covers the API design of
resource()
,httpResource()
, and other related symbols.Enjoy!
Resource-based Architecture
Angular is in the process of a transformation. Two years ago, we proposed making Angular a reactive web framework with signals. Since then, we've shipped a number of signal features in the framework: core primitives like
signal
&computed
, reactiveinput
s includingmodel
, signal queries, bidirectional RxJS conversion utilities, and migration tooling.At this point, we have confidence in our story for synchronous reactivity in Angular. Now, we're ready to begin the next phase of our reactivity journey: the world of asynchronous state. In v19 we shipped the experimental
resource()
API for developers to try out in advance of this RFC.How should the framework deal with async?
Signals are designed to handle synchronous state. Every signal has a well defined concept of its current value. However, not all state in applications is synchronous. Data must be fetched from backends, loaded from async APIs in the browser, or polled from user input that doesn't happen instantaneously.
Ultimately, the state shown to the user must be synchronous - the UI must show something at any given moment, even if the requested data is not yet available or if the request fails. Allowing the developer to cleanly express this derivation of sync state from async sources is a major goal for the framework. Often the derived synchronous state will express to the user that the async operation is pending (loading state) or has failed (error state). There are also UI patterns where the previous state is maintained until the next state is ready.
We observe two prominent design patterns which incorporate async sources into synchronous reactive systems with signals: the resource pattern and the suspense pattern.
The Resource pattern
In the resource pattern, asynchronous sources are represented by a reactive type called resource. A resource represents an asynchronous operation that might or might not have completed. It exposes signals that communicate the status of the resource's loading operation as well as its value.
Resources are a form of explicit asynchronicity. A resource always has a value, which may be
undefined
if that resource is in a state where it has not yet loaded its data. Additionally, a resource always has a state which communicates the status of its loading operation. In other words, resources convert an async operation into synchronous information about its status and results.Code:
Template:
Much like how signals allow the framework to both consume and produce synchronous reactive values, through the resource abstraction the framework can consume and produce asynchronous sources of data.
The Suspense pattern
Conversely, the suspense pattern is built around implicit asynchronicity. Another name for this pattern is "async signals". In this approach, values which are produced asynchronously are made to look synchronous within the reactivity system; however, reading them causes execution to pause (logically, to suspend). This is often implemented by having such reads result in a thrown exception. Another mechanism, the suspense boundary, then catches this exception and diverts execution to produce a new synchronous state.
Code:
Template:
For example, in the suspense pattern, a parent component may act as a suspense boundary, and attempt rendering of a child. If the child component accesses some asynchronous data which is not yet available, this will suspend execution of the child via an exception which is then caught in the parent component. The parent then knows to render a loading state instead, and leave the child component suspended until (via the reactive graph) it receives a notification that the data it needs is now available, and it can attempt to finish rendering the child.
Choice of explicit asynchronicity
Both models have their strengths and weaknesses, but taking everything into consideration we believe the explicit model of asynchronicity offered by the resource concept is the best choice for Angular. In particular:
Explicit asynchronicity makes it very clear which parts of the system might be async and how any derived state will handle missing values. This aligns with Angular's general API design philosophy. Conversely, making all signals potentially asynchronous would subvert developers' ability to reason about the behavior of their applications, as the execution model is similar to try-catch and thus subverts normal control flow.
Angular components aren't well suited to the Suspense model. They're not pure functions and were not designed to be left in a partially rendered state. Doing so might break assumptions that components make in ways that are difficult to predict or resolve.
A major goal of the asynchronous integration is to avoid accidental waterfalling behavior, where developers unknowingly write code in a way where one request blocks another, even though logically both could happen at the same time. While waterfalling is possible in both systems, we see more opportunities for guard rails when asynchronous behavior is explicit.
There are certainly drawbacks to the explicit nature of the resource concept. Like
async
/await
, resource creates a "function coloring" problem where everything consuming a resource must account for its asynchronous nature, creating friction. We will need to consider this friction in designs that interact with resources.Question 1A: What thoughts do you have on our choice of explicit over implicit async? What problems/challenges do you see with our approach? Are there other perspectives or factors we should consider?
What is a resource?
A resource is a declarative dependency on an asynchronous data source, expressed through signals. Resources bridge the synchronous world of signals with operations that take time, such as making network requests, reading files, or even waiting for user input. You can think of them as a form of asynchronous
computed
. They do this in two primary ways: by configuring the request reactively, and by exposing its progress as a state that the UI can depend on through signals.Many asynchronous dependencies are parameterized: fetching user data for an ID from the URL, loading the currently selected account, etc. In other words, the request that needs to be made is a function of the application's state. Resources allow this dependency to be expressed through the reactive graph, by configuring the request as a derivation based on other signals.
Resources produce several signals which can be used in further derivations or to drive the UI. The most obvious of these is a signal of the resource's current value, which might be
undefined
if the resource hasn't finished loading. Additionally, resources communicate their current status as well as any error state via signals, allowing the UI to react appropriately as the request progresses.Resource's place in Angular
Signals in Angular are more than an API for developers. They're a shared contract, a "language" that the framework and developer can use to communicate about things that change. Angular understands signals when they're used inside of templates, and it also can produce signals for values (for example, for inputs) as a way of allowing the application to understand when those values change.
We see Angular's resource concept as serving a similar purpose. It's more than an API for making requests, it defines a shared contract for asynchronous dependencies. Angular can both consume resources created by the application, as well as produce resources when the framework needs to communicate about the status of an asynchronous operation. It provides an interoperable primitive that the ecosystem can design around. In this way, the resource concept complements signals and allows for the full data story of an application to be expressed through the reactive graph.
What about
Observable
?The goal of using resources as a common async primitive is in contrast to the intermediate state we're in today, where
Signal
has become a common primitive for synchronous state andObservable
still serves as a common asynchronous primitive.The Angular team sees resources and
Observable
as serving fundamentally different purposes and use cases. Observables work best when used to model events over time, while resource is concerned with asynchronously derived state. For cases where the output of anObservable
flow is a stream of states, we provide therxResource
bridge to convert the stream to a resource.Angular's async future with resource
With features like
@defer
and incremental hydration, Angular is significantly improving its capabilities around managing and optimizing the loading and execution of application code. However, today the framework has very little visibility into data dependencies.With resources, we see the potential for Angular to resolve this problem, and take a more central role in orchestrating and optimizing data fetching operations. Having a shared contract for data dependencies will allow application developers to delegate management of the data fetching lifecycle to Angular. Together with code loading, this will give Angular a holistic view of loading operations within an application, especially around the application lifecycle of server-side rendering or client-side navigations. Our belief is that this will result in improvements to both the developer experience of the framework as well as the end user experience (performance).
Alternatives to data fetching within components
Today, often Angular components are "in charge" of data fetching operations. Requests are frequently made as late as possible, only initiated when the UI (via the
async
pipe) requests data via a subscription to anObservable
or other source. We frequently see components where the template is wrapped in a conditional of the form:Sometimes
data$
is sync, and sometimes it's async. When rendered, this component shows no UI until its data is available. This is a poor user experience, and can result in issues with Core Web Vitals (for example, layout shifts when the data resolves). Angular can't do anything to resolve these problems as it doesn't have any awareness of the data dependency.If this component is used within a parent that also waits to fetch data before rendering, then the two loading operations will be performed sequentially (often called waterfalling). This may or may not be intentional - it's possible that the two fetch operations have nothing to do with each other, and are only forced to waterfall because of the UI structure.
An alternative exists today in Angular in the form of route resolvers, but the ergonomics and semantics of resolvers make them difficult to use for this purpose. With resources, we intend to explore architectural solutions where data fetching can be lifted outside of components and managed by the framework itself, whether at the level of routes or through some other mechanism. We see this as having some attractive benefits:
A higher-level mechanism can understand data needs from an entire component tree and fetch all the data at once / in parallel, avoiding waterfalls.
Reasoning about the state in an application becomes easier when UI components do not concern themselves with data fetching and focus on data derivation instead.
Testing gets easier as UI component tests can focus on the display logic.
A major focus of our reactivity efforts going forward will therefore be the integration of resource-based data fetching and the router. It's too early to know exactly what shape the APIs will take here, but we do want to share some of the research ideas that are enabled by the resource concept.
Question 1B: What challenges do you see in your own applications with declaring data dependencies "above" components?
Future Research
The resource concept opens up a range of potential advancements for Angular's data management, and the above outlines our concrete plans for integration of resources into Angular. In this section, we want to talk about some of the more speculative ideas that have us excited about the potential of resources.
Note that these are included as examples of what might be possible, and some or all of these ideas could turn out to be impossible, impractical, or not actually desirable.
Template API for awaiting data
Not all data fetching can or should be lifted out of components. In cases where components deal with asynchronous dependencies as resources directly, it might make sense to have a template API (
@async
?) which expresses the dependency and allows management of resolved, loading, and error states.Ideally,
@defer
would also understand resource dependencies and manage the loading of data and code dependencies in parallel.Blocking and non-blocking data dependencies
Not all data dependencies have the same priority. A video player component for example may load information about the video to be played, as well as user comments on the video. It may not make sense to render the component until the video information is available, but it's fine if the comments aren't present in the initial rendering.
To better support this, we could consider allowing resource dependencies to be marked as blocking or non-blocking. The router, for example, would wait for blocking dependencies of a route before rendering a component, delivering their value to UI components as signals. Non-blocking resources would not block rendering of a component, and their values would be delivered as resources (where the template constructs discussed above could facilitate rendering based on the resource state).
Prefetching data dependencies
If Angular knows which data needs to be fetched for a given link / UI button, it can intelligently choose to prefetch both code and data in response to user signals, such as mousing over the link, scrolling it into view, etc.
Router loading state
The router could take on a more active role in managing the loading state during navigation and data fetching, expanding its support for view transitions and adding functionality such as the ability to render dedicated loading states in the case where no previous view is present (such as during initial render).
The router could even learn to begin rendering components before their data has arrived, optimizing load performance.
Deadlines for non-blocking data
To prevent content flicker caused by non-blocking data that loads quickly, both the router and any hypothetical template construct could support a "deadline" for non-blocking data, a timeout for which they're willing to wait before proceeding with rendering. This is similar to
@defer
'sminimum
placeholder time (and perhaps these two concepts can be unified).Server-only data fetching logic
If an application is using server-side rendering, it could be possible to define data fetching logic at the route level that lives exclusively on the server. Such logic could rely on server-side secrets and interact directly with backend APIs (similar to how other frameworks support
'use server'
declarations within functions). Angular could manage the retrieval of data from the server during client-side navigations, eliminating the need for defining API endpoints for the client to use.Resource streaming from SSR
When Angular SSR is servicing a request, it will fetch both blocking and non-blocking data in parallel. It could render the page to the client without the non-blocking data, and leave the connection open. Once the non-blocking data fetch completes, SSR could stream down the results, resolving the resource on the client and updating the UI as needed.
Retry mechanics & error recovery
Currently resource failures can be retried only by calling
.reload()
imperatively. We would like to explore adding holistic support for error recovery.Compiler-supported data dependencies
We've definitely seen benefits of "lifting" data dependencies out of components, but also acknowledge that it creates some level of friction compared to fetching data from within components. We could leverage our compiler to extract the data dependencies of a route as a build optimization. A hybrid approach like this theoretically offers a better tradeoff between the DX of defining data dependencies at the component level and the performance optimizations which are possible through lifting data fetching to a higher level.
Final Question
Don't forget to check out Part 2 of the RFC around the
resource()
APIs!Question 1C: Would the resource concept or any of the ideas discussed here significantly reduce complexity or address real problems in your applications? Are there other things we should consider doing with resources that you can think of, that would make a difference in your applications?
Beta Was this translation helpful? Give feedback.
All reactions