{"id":26974,"date":"2024-10-22T12:28:13","date_gmt":"2024-10-22T10:28:13","guid":{"rendered":"https:\/\/www.angulararchitects.io\/blog\/asynchronous-resources-with-angulars-new-resource-api\/"},"modified":"2025-06-04T13:48:28","modified_gmt":"2025-06-04T11:48:28","slug":"asynchronous-resources-with-angulars-new-resource-api","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/","title":{"rendered":"Asynchronous Data Flow with Angular&#8217;s new Resource API"},"content":{"rendered":"<div class=\"wp-post-series-box series-signals wp-post-series-box--expandable\">\n\t\t\t<input id=\"collapsible-series-signals69fe40c84f392\" class=\"wp-post-series-box__toggle_checkbox\" type=\"checkbox\">\n\t\n\t<label\n\t\tclass=\"wp-post-series-box__label\"\n\t\t\t\t\tfor=\"collapsible-series-signals69fe40c84f392\"\n\t\t\ttabindex=\"0\"\n\t\t\t\t>\n\t\t<p class=\"wp-post-series-box__name wp-post-series-name\">\n\t\t\tThis is post 6 of 9 in the series <em>&ldquo;Signals&rdquo;<\/em>\t\t<\/p>\n\t\t\t<\/label>\n\n\t\t\t<div class=\"wp-post-series-box__posts\">\n\t\t\t<ol>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/angular-signals\/\">Signals in Angular: Building Blocks<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/component-communication-with-signals-inputs-two-way-bindings-and-content-view-queries\/\">Component Communication with Signals: Inputs, Two-Way Bindings, and Content\/ View Queries<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/successful-with-signals-in-angular-3-effective-rules-for-your-architecture\/\">Successful with Signals in Angular &#8211; 3 Effective Rules for Your Architecture<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/skillfully-using-signals-in-angular-selected-hints-for-professional-use\/\">Skillfully Using Signals in Angular &#8211; Selected Hints for Professional Use<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/when-not-to-use-effects-in-angular-and-what-to-do-instead\/\">When (Not) to use Effects in Angular &#8212; and what to do instead<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Asynchronous Data Flow with Angular&#8217;s new Resource API<\/span><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/streaming-resources-in-angular-19-2-details-and-semantics\/\">Streaming Resources in Angular &#8211; Details and Semantics<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/streaming-resources-for-a-chat-with-web-sockets-messages-in-a-glitch-free-world\/\">Streaming Resources for a Chat with Web Sockets: Messages in a Glitch-Free World<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/learning-httpresource-with-super-mario\/\">Angular&#8217;s new httpResource<\/a><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n<p>The experimental Resource API introduced with Angular 19 und overhauled with Angular 20 allows for asynchronously loading resources using Signals. A typical use case for <code>resource<\/code> is loading data. In this article, I show how to build a typical CRUD scenario using the new <code>resource<\/code> API.<\/p>\n<p>\ud83d\udcc2 <a href=\"https:\/\/github.com\/manfredsteyer\/desserts.git\">Source Code<\/a> (see <strong>Branch<\/strong> <code>07c-final<\/code>)<\/p>\n<h2>Example Application<\/h2>\n<p>To demonstrate the individual features, I'm using the desserts application already known from former articles:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/demo-app.png\" alt=\"Example Application: Austrian Desserts\" \/><\/p>\n<p>It basically allows searching for Austrian desserts using the original Austrian German name or its English translation. You can rate the individual desserts or load the ratings provided by a recognized expert in this field (who happens to be my lesser self). <\/p>\n<p>Also, there is a details view for editing a dessert:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/detail-view.png\" style=\"max-width: 500px\"><\/p>\n<h2>First Steps with the Resource API<\/h2>\n<p>Each <code>resource<\/code> has a <code>loader<\/code> function that returns a <code>Promise<\/code> with the loaded data. This <code>loader<\/code> is triggered when the resource has been initialized. Optionally, the resource can have a <code>params<\/code> Signal providing parameters (search criteria) for the <code>loader<\/code>. Every time the <code>params<\/code> Signal changes, the loader is triggered again:<\/p>\n<pre><code class=\"language-typescript\">@Component([...])\nexport class DessertsComponent {\n  #dessertService = inject(DessertService);\n\n  [...]\n\n  \/\/ Criteria for search\n  originalName = signal(&#039;&#039;);\n  englishName = signal(&#039;&#039;);\n\n  \/\/ Combine criteria to computed Signal\n  dessertsCriteria = computed(() =&gt; ({\n    originalName: this.originalName(),\n    englishName: this.englishName(),\n  }));\n\n  \/\/ Define resource with params (=search criteria) and loader\n  \/\/ Every time, the params are changing, the loader is triggered\n  dessertsResource = resource({\n    params: this.dessertsCriteria,\n    loader: (loaderParams) =&gt; {\n      return this.#dessertService.findPromise(loaderParams.params);\n    },\n    defaultValue: []\n  });\n\n  \/\/ initially, resources are undefined\n  desserts = this.dessertsResource.value;\n\n  loading = this.dessertsResource.isLoading;\n  error = this.dessertsResource.error;\n\n  \/\/ The reactive flow goes on ...\n  ratings = signal&lt;DessertIdToRatingMap&gt;({});\n  ratedDesserts = computed(() =&gt; this.toRated(this.desserts(), this.ratings()));\n\n  [...]\n}<\/code><\/pre>\n<p>In this example, <code>dessertsResource<\/code> uses the values in the <code>originalName<\/code> and <code>englishName<\/code> Signals as parameters. The loader is triggered initially and when these Signals change. The <code>param<\/code> object passed to the <code>loader<\/code> contains the current search criteria from the <code>params<\/code> Signal.<\/p>\n<p>The <code>resource<\/code>'s result is found in its <code>value<\/code> Signal. By default, a resource's value Signal is <code>undefined<\/code> until the loader asynchronously returns the first value. To prevent this, the shown example set the <code>defaultValue<\/code> to an empty array. The boolean <code>isLoaded<\/code> informs about the loading state, and <code>error<\/code> is a Signal with a possible error that occurred during loading.<\/p>\n<p>The computed <code>ratedDesserts<\/code> Signal combines the received desserts with possibly loaded ratings. This shows that we have a clear reactive flow that extends from the user input through loading the resource to projecting the resource to a model bound in the view. One action reactively leads to another.<\/p>\n<h2>Important: Loaders are Untracked!<\/h2>\n<p>It's important to note that while the <code>params<\/code> are tracked, the <code>loader<\/code> isn't. That means that a change in the <code>params<\/code> Signal triggers a new <code>loader<\/code> execution, but a change in a <code>Signal<\/code> used in the <code>loader<\/code> <strong>does not<\/strong>. <\/p>\n<p>This is because auto-tracking would only work in the first part of the <code>loader<\/code> that is executed synchronously. Everything that is executed after the first asynchronous operation, e.g., code after <code>await<\/code> or in a <code>.then<\/code> handler, cannot be subject to Angular's auto-tracking. To avoid such confusing situations where a part of the loader is handled differently, the whole loader is untracked.<\/p>\n<h2>Race Conditions<\/h2>\n<p>In many web applications, the user can easily trigger overlapping requests. This is especially the case in an reactive UI where just changing a filter can trigger a further loading operation:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/overlapping.png\" alt=\"Overlapping Requests\" \/><\/p>\n<p>In this case, the user would expect to only get results for 'Ice Cream Pancakes' although they shortly had different filters before. It would be quite confusing if results for ordinary 'Pancakes', and 'Sacher Cake' briefly flashed. It would be even more confusing if the first search took a bit longer:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/race-condition.png\" alt=\"Overlapping Requests\" \/><\/p>\n<p>In this case, the unwanted intermediate results would flash in an order that does not match the order of requests. You call this a race condition.<\/p>\n<h2>The Resource API has switchMap Semantics<\/h2>\n<p>In Angular, we usually leverage RxJS and its <code>switchMap<\/code> Operator that cancels overlapping requests but the last one and hence prevents such situations. The good news is that <code>resource<\/code> uses the same behavior. However, by default it cannot cancel the former request. Instead, it just ignores its result.<\/p>\n<p>If you really want to cancel the former request when another one is scheduled, you need to respect the <code>AbortSignal<\/code> the <code>resource<\/code> API passes into the <code>loader<\/code>:<\/p>\n<pre><code class=\"language-typescript\">[...]\ndessertsResource = resource({\n  params: this.dessertsCriteria,\n  loader: (loaderParams) =&gt; {\n    return this.#dessertService.findPromise(\n      loaderParams.params,\n      loaderParams.abortSignal,\n    );\n  },\n  defaultValue: []\n});\n[...]<\/code><\/pre>\n<p>Although it's called <code>AbortSignal,<\/code> it's not a Signal in the sense of Angular's reactivity primitives but a JavaScript API provided by all modern browsers and supported by several APIs such as the Fetch API. While Angular's <code>HttpClient<\/code> is meanwhile capable of using <code>fetch<\/code> under the hoods, its API does not allow passing in an <code>AbortSignal<\/code>. To compensate for this, I've written a simple helper function <code>toPromise<\/code> that converts an <code>Observable<\/code> to a <code>Promise<\/code> respecting an <code>AbortSignal<\/code>. You find this implementation that is inspired by the <code>rxResource<\/code> function mentioned below, in the provided demo repository.<\/p>\n<p>While <code>switchMap<\/code> semantics might be the most common one used for loading data, RxJS provides further so-called flattening operators dealing with overlapping requests in different ways. However, for the discussed operations, <code>resource<\/code> always uses <code>switchMap<\/code> semantics. Besides this, the <code>reload<\/code> method discussed below always uses <code>exhaustMap<\/code> sematics.<\/p>\n<p>This shows that the Angular team does not want to compete with existing libraries. The goal is to provide building blocks with reasonable default logic covering the majority of use cases. For everything that goes beyond that, we can implement our own solutions or add proven libraries like RxJS.<\/p>\n<h2>Debouncing<\/h2>\n<p>Usually, when triggering requests directly after the user changes a filter, we want to have some debouncing: Chances are, the user is changing further filters, and hence, we want to wait some moments not to trigger several unnecessary requests in a row. <\/p>\n<p>An easy way to accomplish debouning is adding a delay to the loader. As a consequence, the Resource is already in loading state during debouncing. However, in a good discussion with some of my Angular GDE fellows and Angular team members (<a href=\"https:\/\/bsky.app\/profile\/rainerhahnekamp.bsky.social\">Rainer Hahnekamp<\/a>, <a href=\"https:\/\/bsky.app\/profile\/deborahkurata.bsky.social\">Deborah Kurata<\/a>, <a href=\"https:\/\/bsky.app\/profile\/sanderelias.nl\">Sander Elias<\/a>, <a href=\"https:\/\/bsky.app\/profile\/jeanmeche.com\">Matthieu Riegler<\/a> and <a href=\"https:\/\/bsky.app\/profile\/synalx.bsky.social\">Alex Rickabaugh<\/a> to name a few) we came to the conclusion that debouncing the event that leads to a new request might be better suited. There are several arguments for this. One argument is that debouncing the underlying event prevents the Resource from entering loading state before debouncing is complete. Another one is that it allows us to move the Resource in a store that is used regardless of whether the input is debounced or not. <\/p>\n<p>Debouncing the underlying event is straightforward when using Reactive Forms. In this case, we can debounce the <code>valueChanges<\/code> Observable. In cases where we use Template-driven Forms with Two Way Bindings, we need a helper debouncing the bound Signal. For this, I've written a simple <code>debounceSignal<\/code> helper:<\/p>\n<pre><code class=\"language-typescript\">#debouncedCriteria = debounceSignal(this.#dessertsCriteria, 300);\n\n#dessertsResource = resource({\n  params: this.#debouncedCriteria,\n  loader: (loaderParams) =&gt; {\n    return this.#dessertService.findPromise(loaderParams.params, param.abortSignal);\n  },\n  defaultValue: []\n});<\/code><\/pre>\n<p>For a simple implementation of debounceSignal, we can leverage Angular's RxJS interop:<\/p>\n<pre><code class=\"language-ts\">import { Signal } from &quot;@angular\/core&quot;;\nimport { toObservable, toSignal } from &quot;@angular\/core\/rxjs-interop&quot;;\nimport { debounceTime } from &quot;rxjs&quot;;\n\nexport function debounceSignal&lt;T&gt;(source: Signal&lt;T&gt;, timeMsec: number): Signal&lt;T | undefined&gt; {\n    return toSignal(toObservable(source).pipe(debounceTime(timeMsec)));\n}<\/code><\/pre>\n<p>Please note that the resulting Signal will represent <code>undefined<\/code> before the first debouncing is done. A <code>params<\/code> Signal returning <code>undefined<\/code> prevents the Resource from triggering the loader which is what might be the intended behavior in a lot of cases. In cases where you want to skip the initial deboucning, the Signals initial value can be used as the default value:<\/p>\n<pre><code class=\"language-ts\">export function debounceSignal&lt;T&gt;(source: Signal&lt;T&gt;, timeMsec: number): Signal&lt;T&gt; {\n    return toSignal(toObservable(source).pipe(debounceTime(timeMsec)), {\n        initialValue: source()\n    });\n}<\/code><\/pre>\n<p>Of course, as always, we can also provide an implementation that does not use RxJS. This might be interesting in the mid and long term when RxJS becomes optional in Angular. Anyway, I would see such functionality as infrastructure code that does not belong in application code but in the framework itself, a helper library, or at least in a utility module, the application treads as a black box. We will see whether the upcoming Signal-based Forms provide a nice solution for such use cases.<\/p>\n<h2>More on this: Angular Architecture Workshop (online, interactive, advanced)<\/h2>\n<p>Become an expert for enterprise-scale and maintainable Angular applications with our <a href=\"https:\/\/www.angulararchitects.io\/en\/angular-workshops\/advanced-angular-enterprise-architecture-incl-ivy\/\">Angular Architecture workshop!<\/a><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/sujet-en.jpg\" alt=\"\" \/><\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/en\/angular-workshops\/advanced-angular-enterprise-architecture-incl-ivy\/\">All Details (English Workshop)<\/a> | <a href=\"https:\/\/www.angulararchitects.io\/schulungen\/advanced-angular-enterprise-anwendungen-und-architektur\/\">All Details (German Workshop)<\/a><br \/>\n<\/p>\n<h2>Reload and Manual Loading<\/h2>\n<p>The <code>resource<\/code> used so far automatically triggered the <code>loader<\/code> after its initialization and after each change of the <code>params<\/code> Signal. This was convenient in the example at hand. However, it's not a behavior you want all the time. <\/p>\n<p>If you want to disable the initial loader execution, just make sure your <code>params<\/code> Signal returns <code>undefined<\/code>:<\/p>\n<pre><code class=\"language-typescript\">type Requested = undefined | true;\n\n[...]\n\n#ratingsRequested = signal&lt;Requested&gt;(undefined);\n\n[...]\n\n#ratingsResource = resource({\n  params: this.#ratingsRequested,\n  loader: () =&gt; {\n    return this.#ratingService.loadExpertRatingsPromise();\n  },\n  defaultValue: {}\n});\n\nreadonly ratings = this.#ratingsResource.value;\n\n[...]<\/code><\/pre>\n<p>As mentioned above, a <code>params<\/code> Signal returning <code>undefined<\/code> does not trigger the loader by design. In the current case, we don't have parameters influecing which ratings to load, hence I'm just going with the <code>value<\/code> true or <code>undefined<\/code>.<\/p>\n<p>To trigger the loader the first time, we can now set the <code>params<\/code> Signal to <code>true<\/code>. For triggering the loader afterwards again, the application calls the Resource's <code>reload<\/code> method:<\/p>\n<pre><code class=\"language-typescript\">loadRatings(): void {\n  this.#ratingsRequested.set(true);\n  this.#ratingsResource.reload();\n}<\/code><\/pre>\n<p>The <code>reload<\/code> method prevents overlapping requests by immediately returning if there is already a running one. Using terms from RxJS, this can be described as <code>exhaustMap<\/code> semantics. For this reason, we don't need a case distinction whether it's the first or a subsequent request. This, in turn, streamlines the implementation of <code>loadRatings<\/code>.<\/p>\n<p>I admit that using the type <code>undefined | true<\/code> might feel a bit off. However, this allows to use the Resource as it is indended to be used: It's all about reactively loading data. This example would feel more natural if we had a real parameter for the resource, like an <code>expertId<\/code>.<\/p>\n<h2>Interlude 1: linkedSignal for Updates<\/h2>\n<p>The user can change the ratings. Normally, this is fine because a resource can be manually updated with its <code>set<\/code> and <code>update<\/code> methods used in a below section. However, a <code>resource<\/code>'s <code>value<\/code> is <code>undefined<\/code> by default. At least since Angular applications use TypeScript's strict mode by default, I try to avoid <code>null<\/code> and <code>undefined<\/code> in favor of a default object, also known as Null Object. So far, I used <code>computed<\/code> to project <code>null<\/code> and <code>undefined<\/code> to the respective Null Object:<\/p>\n<pre><code class=\"language-typescript\">ratings = computed(() =&gt; this.ratingsResource.value() ?? {});<\/code><\/pre>\n<p>Unfortunately, a computed Signal is read only. The rescue is the <code>linkedSignal<\/code> function. It provides a computed Signal that can be changed: <\/p>\n<pre><code class=\"language-typescript\">ratings = linkedSignal(() =&gt; this.ratingsResource.value() ?? {});<\/code><\/pre>\n<p>If Signals used in the computation change, the computation is retriggered and the directly assigned value is overwritten. For the sake of completeness, I want to mention that the computation happens lazily: If no one is interested in the value, the recomputation does not happen.<\/p>\n<p>This characteristic makes <code>linkedSignal<\/code> a perfect fit for forms: We can change a local copy and, on-demand, send it back for saving.<\/p>\n<h2>Error Handling<\/h2>\n<p>Also Error-Handling is baked into the <code>resource<\/code> API. When the loader throws or rejects the returned <code>Promise<\/code>, the <code>resource<\/code> switches into the state <code>error<\/code>. In this state, the <code>error<\/code> Signal provides the thrown value or the value passed to the <code>Promise<\/code>'s reject function.<\/p>\n<p>However, the <code>resource<\/code> will still proceed to work: If someone calls the <code>reload<\/code> method or if the <code>params<\/code> Signal is changing, the <code>loader<\/code> is executed again. If the <code>loader<\/code> succeeds, the resource clears the <code>error<\/code> Signal and moves to the <code>resolved<\/code> state.<\/p>\n<p>This is comparable to today's default behavior of Effects in NgRx, where some additional logic under the covers prevents the RxJS pipe used from stopping in the case of an error.<\/p>\n<p>However, since Angular 20 you are not allowed to access the resource's <code>value<\/code> if there is an error. Hence, in the template, you need to check for an <code>error<\/code> state first:<\/p>\n<pre><code class=\"language-html\">@if(!error()) {\n  @for (dessert of ratedDesserts(); track dessert.id) {\n    &lt;app-dessert-card\n      [dessert]=&quot;dessert&quot;\n      (ratingChange)=&quot;updateRating(dessert.id, $event)&quot;\n    &gt;&lt;\/app-dessert-card&gt;\n  }\n\n}\n@else {\n  &lt;b&gt;Error loading desserts!&lt;\/b&gt;\n}<\/code><\/pre>\n<p>In this case, <code>ratedDesserts<\/code> is derived from the resource's value using <code>computed<\/code>. For this reason, also here, we have to check for an error state.<\/p>\n<h2>rxResource for RxJS-Interop<\/h2>\n<p>If you already have an Observable, you don't need to convert it into a <code>Promise<\/code>. Instead, you can use the <code>rxResource<\/code> API. It works like the <code>resource<\/code> but the loader is expected to return an <code>Observable<\/code>. Also, the loader is defined via the property streaming because an <code>rxResource<\/code> is a so called streaming resource emitting that can emit several values over time.<\/p>\n<pre><code class=\"language-typescript\">[...]\n\ndessertsResource = rxResource({\n  params: this.dessertsCriteria,\n  stream: (loaderParams) =&gt; {\n    return timer(300).pipe(switchMap(() =&gt; this.#dessertService.find(loaderParams.params)));\n  },\n  defaultValue: []\n});\n\n[...]\n\nratingsResource = rxResource({\n  params: this.#ratingsRequested,\n  stream: () =&gt; {\n    return this.#ratingService.loadExpertRatings()\n  },\n  defaultValue: {}\n});\n\n[...]<\/code><\/pre>\n<p>Also, the loader does not need the <code>AbortSignal<\/code> as Observables can be aborted (by unsubscribing implicitly or explicitly) in general.<\/p>\n<h2>Resource in Services and Stores<\/h2>\n<p>The shown examples directly used the <code>resource<\/code> in a component. This was primarily for the sake of simplicity. Often, you will use resources in services or stores. One reason for this is state management: The loaded data will survive when Angular destroys the component at hand (e.g., because of switching to another route) so that it can be used later by an instance of the same or another component. <\/p>\n<p>The second reason is that stores help to streamline your reactive dataflow:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/store.png\"><\/p>\n<p>This result in a so-called unidirectional data flow: The components send intentions to the store. I'm using the term 'intension' in an abstract way because it depends on the store, how it is expressed. When using a Redux store, it will be a triggered action; in a more lightweight store like the NGRX Signal Store, it might just be a called method.<\/p>\n<p>The expressed intention makes the store perform some tasks that result in new values put into Signals. These Signals can be projected via <code>computed<\/code> and transport the data down to the component's view.<\/p>\n<p>A very simple store is a service providing Signals. That means, we can move the code we've discussed so far into a service:<\/p>\n<pre><code class=\"language-typescript\">type Requested = undefined | true;\n\n@Injectable({ providedIn: &#039;root&#039; })\nexport class DessertStore {\n  #dessertService = inject(DessertService);\n  #ratingService = inject(RatingService);\n\n  private #ratingsRequested = signal&lt;Requested&gt;(undefined);\n\n  readonly originalName = signal(&#039;&#039;);\n  readonly englishName = signal(&#039;&#039;);\n\n  #dessertsCriteria = computed(() =&gt; ({\n    originalName: this.originalName(),\n    englishName: this.englishName(),\n  }));\n\n  #dessertsResource = resource({\n    params: this.#dessertsCriteria,\n    loader: debounce((loaderParams) =&gt; {\n      return this.#dessertService.findPromise(loaderParams.params, loaderParams.abortSignal);\n    }),\n    defaultValue: []\n  });\n\n  #ratingsResource = resource({\n    params: this.#ratingsRequested,\n    loader: () =&gt; {\n      return this.#ratingService.loadExpertRatingsPromise();\n    },\n    defaultValue: {}\n  });\n\n  readonly loading = computed(() =&gt; this.#ratingsResource.isLoading() || this.#dessertsResource.isLoading());\n\n  readonly desserts = this.#dessertsResource.value;\n  readonly ratings = this.#ratingsResource.value;\n  readonly ratedDesserts = computed(() =&gt; toRated(this.desserts(), this.ratings()));\n  readonly error = computed(() =&gt; getErrorMessage(this.dessertsResource.error() || this.ratingsResource.error()));\n\n  loadRatings(): void {\n    this.#ratingsRequested.set(true);\n    this.#ratingsResource.reload();\n  }\n\n  updateRating(id: number, rating: number): void {\n    this.#ratingsResource.update((ratings) =&gt; ({\n      ...ratings,\n      [id]: rating,\n    }));\n  }\n}<\/code><\/pre>\n<p>Here, we see the component consuming the store:<\/p>\n<pre><code class=\"language-typescript\">@Component([...])\nexport class DessertsComponent {\n  #store = inject(DessertStore);\n\n  originalName = this.#store.originalName;\n  englishName = this.#store.englishName;\n  loading = this.#store.loading;\n  error = this.#store.error;\n\n  ratedDesserts = this.#store.ratedDesserts;\n\n  loadRatings(): void {\n    this.#store.loadRatings();\n  }\n\n  updateRating(id: number, rating: number): void {\n    this.#store.updateRating(id, rating);\n  }\n}<\/code><\/pre>\n<p>In this example, the unidirectional data flow can be seen quite clearly: The component sends up its intentions by calling methods and receives (updated) data via Signals bound in the template.<\/p>\n<h2>Updating Resources<\/h2>\n<p>Resources are not just read-only. We can also update them with a new value. While writing back changed values needs to be done manually, locally updating the resource ensures that the new value becomes part of our reactive flow. That means it will be displayed and projected, and it will be the foundation for further updates. <\/p>\n<p>The following example shows a simple Service acting as a store for a details view. Please pay particular attention to the <code>save<\/code> method:<\/p>\n<pre><code class=\"language-typescript\">@Injectable({ providedIn: &#039;root&#039; })\nexport class DessertDetailStore {\n  #dessertService = inject(DessertService);\n  #id = signal&lt;number | undefined&gt;(undefined);\n\n  #dessertResource = resource({\n    params: computed(() =&gt; ({ id: this.#id() })),\n    loader: async (loaderParams) =&gt; {\n      const id = loaderParams.params.id;\n      if (id) {\n        const result = await this.#dessertService.findPromiseById(id);\n        return result ?? initDessert;\n      }\n      else {\n        return Promise.resolve(initDessert);\n      }\n    },\n    defaultValue: initDessert\n  });\n\n  readonly loading = this.#dessertResource.isLoading;\n  readonly error = this.#dessertResource.error;\n\n  #saving = signal(false);\n\n  load(id: number): void {\n    this.#id.set(id);\n  }\n\n  save(dessert: Dessert): void {\n    try {\n      this.#saving.set(true);\n      console.log(&#039;saving&#039;, dessert);\n      \/\/ Here would be your HTTP Call\n      [...]\n      this.#dessertResource.set(dessert);\n    }\n    finally {\n      this.#saving.set(false);\n    }    \n  }\n}<\/code><\/pre>\n<p>The <code>save<\/code> method gets an updated dessert and writes it back to the server. The respective HTTP call is only indicated with a comment here. After performing this call, the <code>dessertResource<\/code> is updated with the new value using <code>set<\/code>. As an alternative, there is also an <code>update<\/code> method that allows to project the current value to a new one. If a loading process takes place while updating the resource with such a local value, this loading process is aborted.<\/p>\n<p>In the example above, the <code>resource<\/code> was private to the service. Hence, the service can make sure the resource is updated only properly. If you want to expose the whole <code>resource<\/code>, you can use its <code>asReadonly<\/code> method to prevent consumers from changing the managed data:<\/p>\n<pre><code class=\"language-typescript\">readonly dessert = dessertResource.asReadonly();<\/code><\/pre>\n<h2>Interlude 2: linkedSignal for the Details Form<\/h2>\n<p>When binding the loaded <code>dessert<\/code> to a template-driven form, we need writable Signals. However, stores usually provide read-only Signals. Also, the <code>value<\/code> Signal provided by <code>resource<\/code> is read-only, although it can be changed directly via the <code>resource<\/code>'s <code>set<\/code> and <code>update<\/code> methods.<\/p>\n<p>As already mentioned above, this is a good case for <code>linkedSignal<\/code>:<\/p>\n<pre><code class=\"language-typescript\">@Component({\n  selector: &#039;app-dessert-detail&#039;,\n  standalone: true,\n  imports: [JsonPipe, RouterLink, FormsModule],\n  templateUrl: &#039;.\/dessert-detail.component.html&#039;,\n  styleUrl: &#039;.\/dessert-detail.component.css&#039;\n})\nexport class DessertDetailComponent implements OnChanges {\n  store = inject(DessertDetailStore);\n\n  id = input.required({\n    transform: numberAttribute\n  });\n\n  loadedDessert = this.store.dessert;\n  loading = this.store.loading;\n  error = this.store.error;\n\n  dessert = {\n    originalName: linkedSignal(() =&gt; this.loadedDessert().originalName),\n    englishName: linkedSignal(() =&gt; this.loadedDessert().englishName),\n    kcal: linkedSignal(() =&gt; this.loadedDessert().kcal)\n  };\n\n  ngOnChanges(): void {\n    const id = this.id();\n    this.store.load(id);\n  }\n\n  save(): void {\n    const dessert = {\n      ...this.loadedDessert(),\n      originalName: this.dessert.originalName(),\n      englishName: this.dessert.englishName(),\n      kcal: this.dessert.kcal(),    \n    };\n\n    this.store.save(dessert);\n  }\n}<\/code><\/pre>\n<p>Here, each property we want to bind to a form field is represented by a linked Signal. They are updated whenever their source is changing. Nevertheless, they can be updated with a local value. The <code>save<\/code> method takes this local value and writes it back to the store which delegates to the <code>resource<\/code>.<\/p>\n<p>As a result, we can directly bind ngModel to our Signals:<\/p>\n<pre><code class=\"language-html\">&lt;form&gt;\n  &lt;div&gt;\n    &lt;label for=&quot;englishName&quot;&gt; English Name &lt;\/label&gt;\n    &lt;input name=&quot;englishName&quot; [(ngModel)]=&quot;dessert.englishName&quot; \/&gt;\n  &lt;\/div&gt;\n  &lt;div&gt;\n    &lt;label for=&quot;originalName&quot;&gt; OriginalName &lt;\/label&gt;\n    &lt;input name=&quot;originalName&quot; [(ngModel)]=&quot;dessert.originalName&quot; \/&gt;\n  &lt;\/div&gt;\n  &lt;div&gt;\n    &lt;label for=&quot;kcal&quot;&gt; kcal &lt;\/label&gt;\n    &lt;input name=&quot;kcal&quot; [(ngModel)]=&quot;dessert.kcal&quot; \/&gt;\n  &lt;\/div&gt;\n&lt;\/form&gt;<\/code><\/pre>\n<p>You can see these linked Signals as the counter part of the <code>FormControl<\/code> objects in reactive forms. Of course, <code>FormControl<\/code> objects provide more features like registering validators. But, and this is my personal impression, this shows that with Signals both approaches are not that far from each other. <\/p>\n<p>While I decided to use template-driven forms in this example, you can totally leverage reactive forms, too. In this case, you might want to use an <code>effect<\/code> for connecting the Signals obtained from the store to your <code>FormControl<\/code>s.<\/p>\n<h2>Bonus: Helper Functions for Streamlining<\/h2>\n<p>When implementing the demo application shown here, I also wrote some reactive helpers streamlining the work with Signals and resources. You find them in the provided demo repository in the branch <code>07c-final<\/code>. One of them is <code>deepLink<\/code> takes a Signal with an Object and converts all its properties to linked Signals:<\/p>\n<pre><code class=\"language-typescript\">dessert = deepLink(this.loadedDessert);<\/code><\/pre>\n<p>This prevents us from calling <code>linkedSignal<\/code> once per property, as in the example found in the previous section.<\/p>\n<p>Also, I wanted to prevent the loading indicator from showing up for quick loading operations. Hence, my <code>debounceTrue<\/code> helper uses a resource to debounce a transition from <code>false<\/code> to <code>true<\/code>: <\/p>\n<pre><code class=\"language-typescript\">loading = debounceTrue(() =&gt; this.ratingsResource.isLoading() || this.dessertsResource.isLoading(), 500);<\/code><\/pre>\n<p>It does not debounce a transition from <code>true<\/code> to <code>false<\/code> because we usually want the loading indicator to disappear immediately when the loaded resource becomes available.<\/p>\n<h2>Conclusion<\/h2>\n<p>The new Resource API is indeed a missing link in Angular's Signal story: It gives us an official way for loading asynchronous resources directly within the reactive flow. Also, it takes care of race conditions and contains basic error handling. <\/p>\n<p>With this new API, we have an easy-to-use built-in feature for very common use cases. For more advanced scenarios, such as working with several parallel data streams, we can switch to something more powerful like RxJS. Thanks to the RxJS interop and <code>rxResource<\/code>, both worlds can be bridged.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is post 6 of 9 in the series &ldquo;Signals&rdquo; Signals in Angular: Building Blocks Component Communication with Signals: Inputs, Two-Way Bindings, and Content\/ View Queries Successful with Signals in Angular &#8211; 3 Effective Rules for Your Architecture Skillfully Using Signals in Angular &#8211; Selected Hints for Professional Use When (Not) to use Effects in [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":26964,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-26974","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","post_series-signals"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Asynchronous Data Flow with Angular&#039;s new Resource API - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Asynchronous Data Flow with Angular&#039;s new Resource API - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"This is post 6 of 9 in the series &ldquo;Signals&rdquo; Signals in Angular: Building Blocks Component Communication with Signals: Inputs, Two-Way Bindings, and Content\/ View Queries Successful with Signals in Angular &#8211; 3 Effective Rules for Your Architecture Skillfully Using Signals in Angular &#8211; Selected Hints for Professional Use When (Not) to use Effects in [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2024-10-22T10:28:13+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-04T11:48:28+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/sujet.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manfred Steyer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/sujet.jpg\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/\"},\"author\":{\"name\":\"Manfred Steyer\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\"},\"headline\":\"Asynchronous Data Flow with Angular&#8217;s new Resource API\",\"datePublished\":\"2024-10-22T10:28:13+00:00\",\"dateModified\":\"2025-06-04T11:48:28+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/\"},\"wordCount\":2649,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/\",\"name\":\"Asynchronous Data Flow with Angular's new Resource API - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg\",\"datePublished\":\"2024-10-22T10:28:13+00:00\",\"dateModified\":\"2025-06-04T11:48:28+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg\",\"width\":1000,\"height\":667},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Asynchronous Data Flow with Angular&#8217;s new Resource API\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\",\"name\":\"Manfred Steyer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Asynchronous Data Flow with Angular's new Resource API - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/","og_locale":"en_US","og_type":"article","og_title":"Asynchronous Data Flow with Angular's new Resource API - ANGULARarchitects","og_description":"This is post 6 of 9 in the series &ldquo;Signals&rdquo; Signals in Angular: Building Blocks Component Communication with Signals: Inputs, Two-Way Bindings, and Content\/ View Queries Successful with Signals in Angular &#8211; 3 Effective Rules for Your Architecture Skillfully Using Signals in Angular &#8211; Selected Hints for Professional Use When (Not) to use Effects in [&hellip;]","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/","og_site_name":"ANGULARarchitects","article_published_time":"2024-10-22T10:28:13+00:00","article_modified_time":"2025-06-04T11:48:28+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/sujet.jpg","type":"image\/jpeg"}],"author":"Manfred Steyer","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/sujet.jpg","twitter_misc":{"Written by":"Manfred Steyer","Est. reading time":"18 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/"},"author":{"name":"Manfred Steyer","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a"},"headline":"Asynchronous Data Flow with Angular&#8217;s new Resource API","datePublished":"2024-10-22T10:28:13+00:00","dateModified":"2025-06-04T11:48:28+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/"},"wordCount":2649,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/","name":"Asynchronous Data Flow with Angular's new Resource API - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg","datePublished":"2024-10-22T10:28:13+00:00","dateModified":"2025-06-04T11:48:28+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/10\/shutterstock_2488531761.jpg","width":1000,"height":667},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/asynchronous-resources-with-angulars-new-resource-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Asynchronous Data Flow with Angular&#8217;s new Resource API"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a","name":"Manfred Steyer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","caption":"Manfred Steyer"}}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/26974","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=26974"}],"version-history":[{"count":10,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/26974\/revisions"}],"predecessor-version":[{"id":30480,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/26974\/revisions\/30480"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/26964"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=26974"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=26974"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=26974"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}