<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Giorgio Galassi</title>
    <description>The latest articles on DEV Community by Giorgio Galassi (@ggalassi).</description>
    <link>https://dev.to/ggalassi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1717803%2F0624b9d4-7bae-4e4e-a1f1-ae7a5eb7f02b.png</url>
      <title>DEV Community: Giorgio Galassi</title>
      <link>https://dev.to/ggalassi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ggalassi"/>
    <language>en</language>
    <item>
      <title>validateHttp() Has No Async Machinery: A Trace From Signal Forms Down to fetch() 🔍🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Thu, 27 Aug 2026 09:45:47 +0000</pubDate>
      <link>https://dev.to/ggalassi/validatehttp-has-no-async-machinery-a-trace-from-signal-forms-down-to-fetch-54n6</link>
      <guid>https://dev.to/ggalassi/validatehttp-has-no-async-machinery-a-trace-from-signal-forms-down-to-fetch-54n6</guid>
      <description>&lt;p&gt;Let's be honest: async validation is the part of any forms library where you brace yourself. Debouncing, cancelling the request the user just invalidated by typing another character, keeping a "checking..." spinner honest, not letting a slow response overwrite a fast one. Every library that has ever done this has grown a pile of bespoke machinery for it.&lt;/p&gt;

&lt;p&gt;So when &lt;a href="https://angular.dev/guide/forms/signals/overview" rel="noopener noreferrer"&gt;Signal Forms&lt;/a&gt; shipped &lt;a href="https://angular.dev/api/forms/signals/validateHttp" rel="noopener noreferrer"&gt;&lt;code&gt;validateHttp()&lt;/code&gt;&lt;/a&gt; and it just worked, I wanted to see the pile. I opened the source expecting a few hundred lines of async bookkeeping, and instead found a function whose entire body is a single call to something else.&lt;/p&gt;

&lt;p&gt;That turned into a trace all the way down, from a form field to the line where bytes actually leave the browser. Six layers, and only two of them add anything you could call new async machinery.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Availability:&lt;/strong&gt; &lt;code&gt;validateHttp()&lt;/code&gt; is &lt;code&gt;@publicApi 22.0&lt;/code&gt;, stable. Every source reference in this article is pinned to the &lt;a href="https://github.com/angular/angular/tree/v22.1.1" rel="noopener noreferrer"&gt;&lt;code&gt;v22.1.1&lt;/code&gt; tag&lt;/a&gt;, so the line numbers stay valid even as &lt;code&gt;main&lt;/code&gt; moves.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧩 The View From Outside
&lt;/h2&gt;

&lt;p&gt;The usage is unremarkable, which is the point. You declare that a field validates against an endpoint, and you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;validateHttp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`/api/username-available?u=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server-unreachable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;available&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username-taken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sync validators run first, the request waits until they pass, &lt;code&gt;field().pending()&lt;/code&gt; is true while it's in flight, and typing again cancels the previous call. If you've read &lt;a href="https://medium.com/@giorgio.galassi/angular-forms-validation-and-form-state-part-3-0f2db5b4961e" rel="noopener noreferrer"&gt;Part 3 of my Signal Forms series&lt;/a&gt;, that's the behaviour contract you already know. The question here is who implements it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Layer 1: validateHttp() Is a Delegation
&lt;/h2&gt;

&lt;p&gt;Here is the whole function, from &lt;a href="https://github.com/angular/angular/blob/v22.1.1/packages/forms/signals/src/api/rules/validation/validate_http.ts#L96-L110" rel="noopener noreferrer"&gt;&lt;code&gt;validate_http.ts&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateHttp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;validateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;httpResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No await, no subscription, no cancellation logic. The URL function you wrote becomes the &lt;code&gt;params&lt;/code&gt; of something, and &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-httpresource-api-23h"&gt;&lt;code&gt;httpResource()&lt;/code&gt;&lt;/a&gt; gets handed over as a &lt;code&gt;factory&lt;/code&gt;. Out of the whole file, that &lt;code&gt;factory&lt;/code&gt; is the only line that does anything HTTP-related; everything else naming HTTP is an import, a type or a doc comment.&lt;/p&gt;

&lt;p&gt;So the async machinery is in &lt;code&gt;validateAsync&lt;/code&gt;. Let's go there.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Layer 2: Three Behaviours, Zero New Mechanisms
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/angular/angular/blob/v22.1.1/packages/forms/signals/src/api/rules/validation/validate_async.ts#L128-L180" rel="noopener noreferrer"&gt;&lt;code&gt;validate_async.ts&lt;/code&gt;&lt;/a&gt; is where I expected the pile to be. It isn't there either. Three behaviours, each one built from something that already existed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sync-before-async gating is just returning &lt;code&gt;undefined&lt;/code&gt;.&lt;/strong&gt; The computation that feeds the resource's params does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validationState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldSkipValidation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;validationState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;syncValid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;a href="https://angular.dev/guide/signals/resource" rel="noopener noreferrer"&gt;&lt;code&gt;resource()&lt;/code&gt;&lt;/a&gt; whose params computation returns &lt;code&gt;undefined&lt;/code&gt; sits in &lt;code&gt;idle&lt;/code&gt; and never calls its loader — &lt;code&gt;loadEffect()&lt;/code&gt; returns early as soon as it sees &lt;code&gt;extRequest.request === undefined&lt;/code&gt;. That's documented public behaviour, not a forms detail. The docs promise that async validation runs only after all synchronous validation passes. Nobody implemented that promise here. It falls out of a rule that was already there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;pending()&lt;/code&gt; is a resource status, read through a validator.&lt;/strong&gt; The async error rule is a &lt;code&gt;switch&lt;/code&gt; (lightly condensed here — the real one wraps each result in &lt;code&gt;addDefaultField()&lt;/code&gt; to attach it to the field):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reloading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resolved&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six resource statuses collapse into four form outcomes. &lt;code&gt;field().pending()&lt;/code&gt; holds no state of its own, it's a projection of &lt;code&gt;res.status()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debounce composes two existing primitives instead of writing a timer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;debounced&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrappedParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ɵchain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;debouncedResource&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrappedParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://angular.dev/guide/signals/debounced" rel="noopener noreferrer"&gt;&lt;code&gt;debounced()&lt;/code&gt;&lt;/a&gt; is a public (still experimental) &lt;code&gt;@angular/core&lt;/code&gt; primitive, and &lt;code&gt;ɵchain&lt;/code&gt; is the internal side of &lt;code&gt;ctx.chain()&lt;/code&gt;, the resource-composition helper you reach through a params context. The file does declare &lt;code&gt;export function chain()&lt;/code&gt;, but it carries no &lt;code&gt;@publicApi&lt;/code&gt; marker and is not surfaced on &lt;code&gt;@angular/core&lt;/code&gt;'s public entry point, which is why the forms code reaches it through the &lt;code&gt;ɵ&lt;/code&gt;-prefixed alias rather than importing it by name. The forms team is chaining a debounced resource into a loader's params exactly the way you would chain any two resources in your own code. Worth noticing that they're dogfooding an experimental API in a stable feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Layer 3: httpResource() Swaps the Loader
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/angular/angular/blob/v22.1.1/packages/common/http/src/resource.ts#L336-L463" rel="noopener noreferrer"&gt;&lt;code&gt;httpResource()&lt;/code&gt;&lt;/a&gt; extends &lt;code&gt;ResourceImpl&lt;/code&gt; and doesn't override any of the loading logic. It passes in a loader that subscribes to &lt;code&gt;HttpClient&lt;/code&gt;, and it adds three signals:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Resets on request change?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;progress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;linkedSignal&lt;/code&gt; sourced from &lt;code&gt;extRequest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes, by the graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;statusCode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;linkedSignal&lt;/code&gt; sourced from &lt;code&gt;extRequest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes, by the graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;headers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;computed&lt;/code&gt; over a &lt;code&gt;linkedSignal&lt;/code&gt;, gated on status&lt;/td&gt;
&lt;td&gt;Yes, plus stays &lt;code&gt;undefined&lt;/code&gt; until &lt;code&gt;resolved&lt;/code&gt;/&lt;code&gt;error&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So changing the request resets all three with no reset code involved — and the gate on &lt;code&gt;headers&lt;/code&gt; means you never read half-populated headers mid-flight.&lt;/p&gt;

&lt;p&gt;The one place a reset &lt;em&gt;is&lt;/em&gt; written by hand is &lt;code&gt;override set()&lt;/code&gt;, which clears all three when you write a value locally. The signal graph covers the request path; the local-write path doesn't go through it.&lt;/p&gt;

&lt;p&gt;One line inside that loader is the bridge between the signal world and the HTTP world:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔧 Layer 4: The Pipeline Everything Shares
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/angular/angular/blob/v22.1.1/packages/core/src/resource/resource.ts#L203-L345" rel="noopener noreferrer"&gt;&lt;code&gt;ResourceImpl&lt;/code&gt;&lt;/a&gt; is the actual engine, and it's four reactive nodes. The one I'd single out is the state machine, because it explains a UI behaviour you've probably relied on without asking why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the branch taken when the resource already has previous state&lt;/span&gt;
&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two of these, one per branch of an &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; inside that &lt;code&gt;linkedSignal&lt;/code&gt;. The first covers initialisation and can also resolve straight from an initial stream; the second is the one above, taken when params change on a resource that already exists. Both send &lt;code&gt;undefined&lt;/code&gt; to &lt;code&gt;'idle'&lt;/code&gt;. That expression runs inside a &lt;code&gt;linkedSignal&lt;/code&gt;, which means it's &lt;strong&gt;not behind an await&lt;/strong&gt;. &lt;code&gt;linkedSignal&lt;/code&gt; is lazy, it recomputes on read rather than on write, but that recomputation is pure and synchronous: the first read of &lt;code&gt;status()&lt;/code&gt; after your params change already returns &lt;code&gt;'loading'&lt;/code&gt;, and &lt;code&gt;pending()&lt;/code&gt; is already true, in the same tick, before any promise exists. Only the data is async. If you've ever wondered why Signal Forms' pending state doesn't flicker, that's the reason: the state transition doesn't live inside the async function.&lt;/p&gt;

&lt;p&gt;The loader itself is called by exactly one &lt;a href="https://github.com/angular/angular/blob/v22.1.1/packages/core/src/resource/resource.ts#L411-L516" rel="noopener noreferrer"&gt;&lt;code&gt;effect&lt;/code&gt;&lt;/a&gt; whose only tracked dependency is the request. Nobody calls the loader imperatively. The graph decides that work should happen, and a single effect does it. It's the same lazy, pull-based model I traced in &lt;a href="https://dev.to/gdg/linkedsignal-writes-back-a-trace-through-the-reactive-graph-37o7"&gt;Internals #1&lt;/a&gt;, which is a good sign: you learn this machine once and it keeps showing up.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔌 Layer 5: Where the Trace Goes Dark
&lt;/h2&gt;

&lt;p&gt;Follow &lt;code&gt;client.request(...)&lt;/code&gt; downward and you hit a wall. &lt;a href="https://angular.dev/guide/http" rel="noopener noreferrer"&gt;&lt;code&gt;HttpClient&lt;/code&gt;&lt;/a&gt; hands off to &lt;code&gt;HttpHandler&lt;/code&gt;, &lt;code&gt;HttpHandler&lt;/code&gt; hands off to &lt;code&gt;HttpBackend&lt;/code&gt;, and both of those are abstract classes with a single &lt;code&gt;handle()&lt;/code&gt; method and no implementation. Grep as hard as you like, no import leads to the code that touches the network.&lt;/p&gt;

&lt;p&gt;That's because the binding isn't an import, it's a provider. From &lt;a href="https://github.com/angular/angular/blob/v22.1.1/packages/common/http/src/provider.ts#L122-L138" rel="noopener noreferrer"&gt;&lt;code&gt;provider.ts&lt;/code&gt;&lt;/a&gt;, reformatted here to fit the width (the real &lt;code&gt;HttpBackend&lt;/code&gt; entry uses a block-bodied factory, and each object sits on its own lines):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FetchBackend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;HttpInterceptorHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useExisting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpInterceptorHandler&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpBackend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FetchBackend&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTTP_INTERCEPTOR_FNS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;xsrfInterceptorFn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;provideHttpClient()&lt;/code&gt; is the only place in the framework where those two abstract tokens get a concrete answer. Two details are easy to get wrong here. First, &lt;code&gt;HttpClient&lt;/code&gt; itself is &lt;code&gt;@Injectable({providedIn: 'root'})&lt;/code&gt; and &lt;code&gt;FetchBackend&lt;/code&gt; is auto-provided too, so both classes are reachable from a bare injector. Forgetting &lt;code&gt;provideHttpClient()&lt;/code&gt; doesn't fail on &lt;code&gt;HttpClient&lt;/code&gt;, it fails with &lt;code&gt;No provider for HttpHandler&lt;/code&gt;, because the class resolves and its dependency doesn't. Second, re-listing &lt;code&gt;HttpClient&lt;/code&gt; and the backends in that array isn't redundant. It re-provides them at the environment injector where you called the function. That's what lets a lazy route have its own interceptors, instead of everything collapsing onto one root instance.&lt;/p&gt;

&lt;p&gt;This is also why swapping the transport is a one-line change:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What it adds&lt;/th&gt;
&lt;th&gt;Status at v22.1.1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;withFetch()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{provide: HttpBackend, useExisting: FetchBackend}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Deprecated&lt;/strong&gt; — "&lt;code&gt;FetchBackend&lt;/code&gt; is the default &lt;code&gt;HttpBackend&lt;/code&gt;"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;withXhr()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{provide: HttpBackend, useExisting: HttpXhrBackend}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported — still needed for upload progress and JSONP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🌐 Layer 6: The Floor
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/angular/angular/blob/v22.1.1/packages/common/http/src/fetch.ts#L78-L146" rel="noopener noreferrer"&gt;&lt;code&gt;FetchBackend&lt;/code&gt;&lt;/a&gt; is where Angular stops and the platform starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;fetchImpl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FetchFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})?.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;((...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ngZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runOutsideAngular&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlWithParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even the last hop is injectable. And notice &lt;code&gt;handle()&lt;/code&gt; returns &lt;code&gt;new Observable(...)&lt;/code&gt;, so nothing was sent until layer 3 called &lt;code&gt;.subscribe()&lt;/code&gt;. The cold Observable is the hinge the whole chain turns on: the resource's effect decides when, and the subscription is the trigger.&lt;/p&gt;

&lt;p&gt;Cancellation runs back up the same path in reverse. Typing another character changes the params signal, so the effect aborts its &lt;code&gt;AbortController&lt;/code&gt;, the httpResource loader unsubscribes, the RxJS teardown aborts a &lt;strong&gt;second&lt;/strong&gt; &lt;code&gt;AbortController&lt;/code&gt; inside &lt;code&gt;FetchBackend&lt;/code&gt;, and that one is the signal handed to &lt;code&gt;fetch&lt;/code&gt;. Four different cancellation idioms relayed in sequence, with a staleness check at the top that rejects a late response by object identity in case anything slips through.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Lined up, the whole stack is one pipeline with one part swapped at each level:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it swaps&lt;/th&gt;
&lt;th&gt;What it adds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validateHttp()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The params&lt;/td&gt;
&lt;td&gt;Nothing at all — the body is one delegating call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validateAsync()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The params again, now gated and debounced&lt;/td&gt;
&lt;td&gt;The translation from resource status into form errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;httpResource()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The loader&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;headers()&lt;/code&gt;, &lt;code&gt;statusCode()&lt;/code&gt;, &lt;code&gt;progress()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ResourceImpl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nothing — it &lt;em&gt;is&lt;/em&gt; the pipeline&lt;/td&gt;
&lt;td&gt;Status, cancellation, staleness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HttpClient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The transport, through DI&lt;/td&gt;
&lt;td&gt;Interceptors, XSRF, the transfer cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FetchBackend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nothing either — it's the floor&lt;/td&gt;
&lt;td&gt;Chunked body reading, progress, timeouts, and finally &lt;code&gt;globalThis.fetch&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;FetchBackend&lt;/code&gt; is the one row worth a caveat: there is real code down there, it just isn't &lt;em&gt;async validation&lt;/em&gt; code. None of the debouncing, cancelling or pending-state logic we came looking for lives below &lt;code&gt;ResourceImpl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason this matters beyond trivia: there's no forms-specific async system to learn, work around, or wait on the team to extend. If you understand &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-3393"&gt;&lt;code&gt;resource()&lt;/code&gt;&lt;/a&gt;, you already understand &lt;code&gt;validateHttp()&lt;/code&gt;, and anything it does you can reproduce with public APIs. Gate a loader by returning &lt;code&gt;undefined&lt;/code&gt;. Render pending state off &lt;code&gt;status()&lt;/code&gt;. Debounce by chaining a &lt;code&gt;debounced()&lt;/code&gt; resource into your params. That's the entire feature.&lt;/p&gt;

&lt;p&gt;It's also a fair signal about the abstraction's honesty. A thin layer over primitives you can reach yourself is one you can debug at 6pm on a Friday, and one that inherits every improvement made underneath it for free.&lt;/p&gt;

&lt;p&gt;If you want to walk it yourself, clone the repo at &lt;code&gt;v22.1.1&lt;/code&gt; and read in this order: &lt;code&gt;validate_http.ts&lt;/code&gt;, &lt;code&gt;validate_async.ts&lt;/code&gt;, &lt;code&gt;common/http/src/resource.ts&lt;/code&gt;, then &lt;code&gt;core/src/resource/resource.ts&lt;/code&gt; (the constructor, then &lt;code&gt;loadEffect&lt;/code&gt;). A nice runtime check: provide a custom &lt;code&gt;FetchFactory&lt;/code&gt; that logs, then type into a &lt;code&gt;validateHttp&lt;/code&gt; field and watch one line appear at the bottom of a five-layer stack.&lt;/p&gt;

&lt;p&gt;For the usage side of Signal Forms, &lt;a href="https://medium.com/@giorgio.galassi/angular-forms-model-setup-and-ui-binding-part-1-8143b8b58fdf" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; through &lt;a href="https://medium.com/@giorgio.galassi/angular-forms-advanced-patterns-part-4-1de423db0671" rel="noopener noreferrer"&gt;Part 4&lt;/a&gt; of the series cover the API from the outside. This one was the floor beneath it.&lt;/p&gt;




&lt;p&gt;If you found this helpful, follow me here and on &lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for more deep dives into Angular, web performance, and modern frontend development.&lt;/p&gt;

&lt;p&gt;I also send the longer, more opinionated version of this kind of thing to a small list — reading the source, and what it changes about how you build. &lt;a href="https://ggalassi.substack.com/" rel="noopener noreferrer"&gt;Subscribe here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See you in the next one! 🤙🏻&lt;br&gt;
— G.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>angularsignals</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Angular WebMCP — Your App is Now an AI Tool 🔥🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Sat, 08 Aug 2026 10:09:17 +0000</pubDate>
      <link>https://dev.to/gdg/angular-webmcp-your-app-is-now-an-ai-tool-3bp4</link>
      <guid>https://dev.to/gdg/angular-webmcp-your-app-is-now-an-ai-tool-3bp4</guid>
      <description>&lt;p&gt;There's a pattern in Angular releases that I've come to appreciate: every major version picks one bet that's slightly ahead of its time. Signals were that bet in v16. Zoneless was that bet in v21. In v22, that bet is &lt;strong&gt;&lt;a href="https://angular.dev/ai/webmcp" rel="noopener noreferrer"&gt;WebMCP&lt;/a&gt;&lt;/strong&gt;, and it's a fundamentally different kind of feature — not a framework improvement, but an architectural shift in what an Angular app &lt;em&gt;is&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let me explain what I mean.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧭 The Idea: Outside → In
&lt;/h2&gt;

&lt;p&gt;Every Angular feature so far has been about what happens &lt;em&gt;inside&lt;/em&gt; the app — how components detect changes, how services are injected, how forms manage state. WebMCP is different: it's about exposing your app's capabilities to the &lt;em&gt;outside&lt;/em&gt;, specifically to AI agents running in the browser.&lt;/p&gt;

&lt;p&gt;The mental model is simple. Today, an AI assistant browsing your app sees a DOM — pixels and HTML. It can read text and click buttons, but it has no understanding of what your app &lt;em&gt;can do&lt;/em&gt;. WebMCP changes that. You declare a set of tools backed by your real Angular services, your real signals, your real DI graph, and any WebMCP-capable agent can discover and call them directly through a typed, described interface you control.&lt;/p&gt;

&lt;p&gt;This is different from &lt;strong&gt;&lt;a href="https://angular.dev/ai/agent-skills" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt;&lt;/strong&gt;, which is your app calling &lt;em&gt;out&lt;/em&gt; to AI. Two separate features, two directions: WebMCP is agents driving your app; Agent Skills is your app calling AI. Today we're only talking about WebMCP.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔧 Browser Support
&lt;/h2&gt;

&lt;p&gt;WebMCP is built on the &lt;strong&gt;&lt;a href="https://github.com/webmachinelearning/webmcp/" rel="noopener noreferrer"&gt;W3C ModelContext API&lt;/a&gt;&lt;/strong&gt;, a draft browser standard. Your tools register on &lt;code&gt;navigator.modelContext&lt;/code&gt; and any agent that speaks this protocol can query that object and call them.&lt;/p&gt;

&lt;p&gt;As of June 2026, Edge 147 ships it natively, Chrome 149 has an open Origin Trial, Firefox is committed for Q3 2026, and Safari for Q4. Mass adoption is realistically mid-2027, and Angular ships this as experimental — that flag is honest, but the integration is already surprisingly clean.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Experimental:&lt;/strong&gt; &lt;a href="https://angular.dev/api/core/provideExperimentalWebMcpTools" rel="noopener noreferrer"&gt;&lt;code&gt;provideExperimentalWebMcpTools()&lt;/code&gt;&lt;/a&gt; is available in Angular 22 but carries no stability guarantees yet. The W3C spec is still evolving; expect API changes before general availability.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🛠️ Building Your First WebMCP Tool
&lt;/h2&gt;

&lt;p&gt;Let's build something concrete. A dashboard shows a list of expenses and we want an AI assistant to ask "what expenses are currently visible?" and get real, live data back — not a DOM scrape or a static API response, but the actual signal state the component is rendering from.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: The service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// expense.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Expense&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Accommodation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accommodation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Meals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CATEGORY_VALUES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExpenseService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;expenses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Expense&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hotel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Accommodation&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Taxi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Transport&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dinner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Meals&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nf"&gt;getVisible&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Expense&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expenses&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@Service()&lt;/code&gt; is Angular 22's new shorthand for &lt;code&gt;@Injectable({ providedIn: 'root' })&lt;/code&gt; — if you want the full picture on that, I covered it in &lt;a href="https://medium.com/@giorgio.galassi/angular-22-service-and-injectasync-dependency-injection-finally-grows-up-499e508fa47c" rel="noopener noreferrer"&gt;Angular 22 — @Service and injectAsync: Dependency Injection Finally Grows Up&lt;/a&gt;. Notice that &lt;code&gt;CATEGORY_VALUES&lt;/code&gt; lives right here next to the &lt;code&gt;ExpenseCategory&lt;/code&gt; object — we'll use it to keep the tool description in sync automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Declaring the tool
&lt;/h3&gt;

&lt;p&gt;Parameters are described using &lt;a href="https://json-schema.org/" rel="noopener noreferrer"&gt;JSON Schema&lt;/a&gt; syntax — the same format Angular uses internally and the same format the agent receives.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// expense-mcp.tool.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;declareExperimentalWebMcpTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ExpenseService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CATEGORY_VALUES&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./expense.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expenseListTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;declareExperimentalWebMcpTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getVisibleExpenses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Returns the list of expenses currently visible in the dashboard.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Filter by expense category. Allowed values: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CATEGORY_VALUES&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ExpenseCategory&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ExpenseService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;svc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four things to notice here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The return value follows the MCP tool result format.&lt;/strong&gt; &lt;code&gt;{ content: [{ type: 'text', text: '...' }] }&lt;/code&gt; is the wire format the agent expects, defined by the Model Context Protocol spec. You don't return a plain object or array directly — you serialise your data into that &lt;code&gt;text&lt;/code&gt; field with &lt;code&gt;JSON.stringify&lt;/code&gt;, and the agent parses it on its end. This is not Angular-specific; it's the protocol contract every MCP tool must honour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; are instructions to the LLM&lt;/strong&gt;, not documentation for humans. The agent reads them at runtime to decide whether and how to call the tool, so write them like an OpenAPI spec for an AI: precise, specific, unambiguous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;execute&lt;/code&gt; runs inside Angular's injection context&lt;/strong&gt;, which means &lt;code&gt;inject()&lt;/code&gt; works normally and your tool has full access to your DI graph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CATEGORY_VALUES&lt;/code&gt; is derived from the object, not hardcoded.&lt;/strong&gt; Add a value to &lt;code&gt;ExpenseCategory&lt;/code&gt; and the agent description updates automatically, with no second place to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Registering in the app
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideExperimentalWebMcpTools&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expenseListTool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./expense-mcp.tool&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideExperimentalWebMcpTools&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;expenseListTool&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="c1"&gt;// ... rest of your providers&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line in your app config and you're done. From here, any WebMCP-capable agent visiting your app will find &lt;code&gt;getVisibleExpenses&lt;/code&gt; in &lt;code&gt;document.modelContext.tools&lt;/code&gt; and can call it against the live session.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 What the Agent Actually Sees
&lt;/h2&gt;

&lt;p&gt;When a WebMCP-capable agent inspects the page, this is what it finds on &lt;code&gt;document.modelContext&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getVisibleExpenses"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Returns the list of expenses currently visible in the dashboard."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Filter by expense category. Allowed values: accommodation, transport, meals."&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user says &lt;em&gt;"show me only transport expenses."&lt;/em&gt; The agent reads the schema, resolves &lt;code&gt;category: "transport"&lt;/code&gt;, calls the tool. Angular runs the handler against the live signal state. The agent gets back exactly the filtered rows the dashboard is currently showing — no DOM scraping, no brittle selectors, no static mock data.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚛️ Signal Forms as Agent Tools
&lt;/h2&gt;

&lt;p&gt;If your app is form-heavy, Angular 22 also ships &lt;a href="https://angular.dev/api/forms/signals/provideExperimentalWebMcpForms" rel="noopener noreferrer"&gt;&lt;code&gt;provideExperimentalWebMcpForms()&lt;/code&gt;&lt;/a&gt;, which automatically surfaces all your &lt;a href="https://angular.dev/essentials/signal-forms" rel="noopener noreferrer"&gt;Signal Forms&lt;/a&gt; as agent-callable tools without you having to declare each one manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideExperimentalWebMcpForms&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/forms/signals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideExperimentalWebMcpForms&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Experimental:&lt;/strong&gt; &lt;code&gt;provideExperimentalWebMcpForms()&lt;/code&gt; is double-experimental — both WebMCP itself and the forms integration are in preview. Treat it as a proof of concept for now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each Signal Form becomes a tool the agent can fill and submit. For internal tooling or admin dashboards this is already genuinely useful, and it shows where the Angular team is heading: if Signal Forms are the way to model user intent, they should also be the way to model agent intent.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Beyond the Happy Path
&lt;/h2&gt;

&lt;p&gt;The docs highlight forms as the easy entry point, but the more interesting territory is everything else — read queries, write actions, navigation triggers.&lt;/p&gt;

&lt;p&gt;Let's be honest: a &lt;strong&gt;read + write pair&lt;/strong&gt; is where agents become genuinely powerful. Expose &lt;code&gt;getExpenseReports()&lt;/code&gt; alongside &lt;code&gt;flagExpense(id, reason)&lt;/code&gt; and an agent can query, reason over the data, and act in a single turn. Add permission-awareness by injecting your &lt;code&gt;AuthService&lt;/code&gt; inside the &lt;code&gt;execute&lt;/code&gt; handler and the agent automatically gets a properly scoped view, seeing only what the current user is authorised to see. You could also expose derived data tools like &lt;code&gt;getSummaryByCategory()&lt;/code&gt; — pre-computed aggregates cost fewer tokens and produce faster responses than handing the agent a raw list to process itself.&lt;/p&gt;

&lt;p&gt;The general principle worth internalising: anywhere you'd write a hardcoded string describing your domain model, ask whether you can derive it from TypeScript instead. Your types are the single source of truth and the agent's schema should follow from them, not diverge from them.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 One Thing Worth Getting Right Early: Trust
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;execute&lt;/code&gt; handlers run inside Angular's injection context with full access to your DI graph, a WebMCP tool is as powerful as the service method it wraps — which means a write tool is a real mutation, not a preview. Before shipping any tool that modifies state, there are three things worth understanding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt injection in your descriptions.&lt;/strong&gt; The &lt;code&gt;description&lt;/code&gt; and parameter description fields are read by the LLM as trusted context. A malicious site can embed instructions in its own tool descriptions that manipulate the agent's behaviour on &lt;em&gt;other&lt;/em&gt; sites — and if your own tool returns user-generated content, that content is another vector. The &lt;a href="https://github.com/webmachinelearning/webmcp/blob/main/docs/security-privacy-considerations.md" rel="noopener noreferrer"&gt;W3C security considerations doc&lt;/a&gt; calls this out explicitly under "Metadata / Description Attacks" and "Output Injection Attacks": never echo unsanitised user content directly from an &lt;code&gt;execute&lt;/code&gt; handler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular does not validate inputs for you.&lt;/strong&gt; The agent is supposed to match the &lt;code&gt;inputSchema&lt;/code&gt; you define, but Angular makes no guarantee it does — the &lt;code&gt;execute&lt;/code&gt; callback receives whatever the agent sends. The &lt;a href="https://angular.dev/ai/webmcp#validate-tool-inputs" rel="noopener noreferrer"&gt;Angular docs are explicit on this&lt;/a&gt;: &lt;em&gt;"Consider explicitly validating arguments to the execute function before using them."&lt;/em&gt; Treat every input as untrusted, the same way you would a form submission or a query parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The session is the attack surface.&lt;/strong&gt; The &lt;a href="https://github.com/webmachinelearning/webmcp/blob/main/docs/security-privacy-considerations.md" rel="noopener noreferrer"&gt;W3C spec's threat model&lt;/a&gt; notes that agents inherit the user's authentication context — session cookies, logged-in state, everything. The real risk isn't an anonymous external caller; same-origin constraints handle that. The risk is a legitimate, trusted agent that has been manipulated through prompt injection and then calls your write tools with the user's full permissions. Design accordingly: scope write tools tightly, and never expose an action you wouldn't want triggered automatically on behalf of a logged-in user.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ The Honest Caveat
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/webmachinelearning/webmcp/" rel="noopener noreferrer"&gt;W3C spec&lt;/a&gt; is still early and has been changing frequently. Mid-2027 is the realistic mass-adoption target, when both browser default support and enough publisher adoption exist to make it meaningful at scale. For now it's great for internal tools and early experimentation, but it's not ready to ship to anonymous production users.&lt;/p&gt;

&lt;p&gt;That said, the direction is set. Your app has always had one interface — the DOM, for humans. WebMCP adds a second one, for agents. How you design that second surface is a new skill worth starting to develop now, before the spec lands and everyone is catching up at once.&lt;/p&gt;




&lt;p&gt;If you found this helpful, follow me here and on &lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for more deep dives into Angular, signals, AI, and modern frontend development.&lt;/p&gt;

&lt;p&gt;See you in the next one! 🤙🏻&lt;br&gt;
— G.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>linkedSignal() Writes Back: A Trace Through the Reactive Graph 🔍🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Fri, 07 Aug 2026 08:43:39 +0000</pubDate>
      <link>https://dev.to/ggalassi/linkedsignal-writes-back-a-trace-through-the-reactive-graph-37o7</link>
      <guid>https://dev.to/ggalassi/linkedsignal-writes-back-a-trace-through-the-reactive-graph-37o7</guid>
      <description>&lt;p&gt;Let's be honest: &lt;a href="https://angular.dev/guide/signals/linked-signal" rel="noopener noreferrer"&gt;&lt;code&gt;linkedSignal()&lt;/code&gt;&lt;/a&gt; always had a bit of a split personality. It read beautifully from another signal, deriving a value that stayed in sync automatically. But the moment you needed to write back to that source, you were on your own, writing a separate method, converting the value by hand, and hoping you didn't forget to call it from every place that touched the field.&lt;/p&gt;

&lt;p&gt;Angular 22.1 closes that gap. &lt;code&gt;linkedSignal()&lt;/code&gt; can now take a custom &lt;code&gt;set()&lt;/code&gt; option, and the linkedSignal becomes a genuine two-way field: read from a larger piece of state, write back to it, no disconnected duplicate copy of the data anywhere.&lt;/p&gt;

&lt;p&gt;My first reaction when I saw this: if the linkedSignal reads a source signal, and writing to the linkedSignal updates that same source, doesn't that loop forever? It didn't feel obvious to me, so I went and traced through the actual reactive graph source to settle it, and that turned into a nice little tour of how Angular's signals actually work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Availability:&lt;/strong&gt; the custom &lt;code&gt;set()&lt;/code&gt; option shipped in Angular 22.1.0 (stable) and is documented in the official &lt;a href="https://angular.dev/guide/signals/linked-signal#customizing-the-set-operation" rel="noopener noreferrer"&gt;&lt;code&gt;linkedSignal&lt;/code&gt; guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧩 Before: Reading Was Clean, Writing Wasn't
&lt;/h2&gt;

&lt;p&gt;Here's the classic shape. A &lt;code&gt;profile&lt;/code&gt; signal holds the real state, and a &lt;code&gt;linkedSignal&lt;/code&gt; exposes one field of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Brian&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;brian@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxMarketingEmailsPerWeek&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;maxMarketingEmailsPerWeek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;linkedSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;maxMarketingEmailsPerWeek&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading &lt;code&gt;maxMarketingEmailsPerWeek()&lt;/code&gt; is effortless. Writing back to &lt;code&gt;profile&lt;/code&gt; is not: you need a second method that does the &lt;code&gt;profile.update()&lt;/code&gt; dance, and every button, every input handler, has to remember to call that method instead of the linkedSignal directly. The read side and the write side live in two different places for what is conceptually one piece of state.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 After: A Custom set()
&lt;/h2&gt;

&lt;p&gt;The fix is a &lt;code&gt;set&lt;/code&gt; function passed alongside the computation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;maxMarketingEmailsPerWeek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;linkedSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;maxMarketingEmailsPerWeek&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxMarketingEmailsPerWeek&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;maxMarketingEmailsPerWeek.set(5)&lt;/code&gt; and &lt;code&gt;.update(v =&amp;gt; v + 1)&lt;/code&gt; both flow through that setter and land on &lt;code&gt;profile&lt;/code&gt;. The helper method is gone. The simplest version of this idea, straight from the &lt;a href="https://angular.dev/guide/signals/linked-signal#customizing-the-set-operation" rel="noopener noreferrer"&gt;Angular docs&lt;/a&gt;, is a temperature converter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tempC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tempF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;linkedSignal&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;valF&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;tempF&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;212&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 100&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tempF&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 212&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧠 Wait, Doesn't This Loop?
&lt;/h2&gt;

&lt;p&gt;Here's where I got stuck. &lt;code&gt;tempF&lt;/code&gt; reads &lt;code&gt;tempC()&lt;/code&gt; inside its computation, so &lt;code&gt;tempF&lt;/code&gt; is a genuine dependent of &lt;code&gt;tempC&lt;/code&gt; in the reactive graph. When &lt;code&gt;tempC.set(100)&lt;/code&gt; runs inside the custom setter, that dependency absolutely gets marked dirty and &lt;code&gt;tempF&lt;/code&gt; absolutely does recompute. If it didn't, &lt;code&gt;tempF()&lt;/code&gt; would show a stale value after the conversion, and the whole feature would be pointless.&lt;/p&gt;

&lt;p&gt;So the question I actually needed answered was: does that recompute call the custom setter again? It doesn't, and the reason is architectural, not incidental.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Under the Hood: The Write Path
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;linkedSignal&lt;/code&gt;'s public write API, defined in &lt;a href="https://github.com/angular/angular/blob/main/packages/core/src/render3/reactivity/linked_signal.ts" rel="noopener noreferrer"&gt;&lt;code&gt;linked_signal.ts&lt;/code&gt;&lt;/a&gt;, looks like this once a custom setter is supplied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;linkedSignalSetFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;upgradedGetter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;customSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rawSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;upgradedGetter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;customSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;updateFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;untracked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getter&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nx"&gt;rawSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling &lt;code&gt;tempF.set(212)&lt;/code&gt; invokes exactly one thing: your &lt;code&gt;customSet&lt;/code&gt; function. It never touches &lt;code&gt;tempF&lt;/code&gt;'s own internal value directly, unless your function explicitly calls the &lt;code&gt;rawSet&lt;/code&gt; escape hatch it's handed. In the temperature example, it doesn't, it only calls &lt;code&gt;tempC.set(100)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The temperature example never reaches for that second parameter, but there are two situations where you would. One is performance: when &lt;code&gt;computation&lt;/code&gt; is expensive and you already know the exact result, rerunning it is wasted work. The other is less obvious: not every linkedSignal has an invertible relationship with its source the way &lt;code&gt;tempF&lt;/code&gt;/&lt;code&gt;tempC&lt;/code&gt; do. Plenty of real &lt;code&gt;computation&lt;/code&gt; functions, summarizing, rounding, picking one field out of several possible source states, can't be inverted into a single correct value to write back. &lt;code&gt;rawSet&lt;/code&gt; covers both: it's a direct line to the exact primitive that backs the default &lt;code&gt;.set()&lt;/code&gt; when there's no custom setter at all, letting you plant the value directly instead of inverting through the source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;upgradedGetter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;linkedSignalSetFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;upgradedGetter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateFn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;linkedSignalUpdateFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateFn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rawSet&lt;/code&gt; is &lt;code&gt;(newValue) =&amp;gt; linkedSignalSetFn(node, newValue)&lt;/code&gt;, the same call, just handed to you as an argument instead of wired up automatically. Here's what that function actually does, from the &lt;a href="https://github.com/angular/angular/blob/main/packages/core/primitives/signals/src/linked_signal.ts" rel="noopener noreferrer"&gt;signal primitives&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;linkedSignalSetFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LinkedSignalNode&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;producerUpdateValueVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// resolve any pending staleness first&lt;/span&gt;
  &lt;span class="nf"&gt;signalSetFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// plain field write: node.value = newValue, version++&lt;/span&gt;
  &lt;span class="nf"&gt;producerMarkClean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// mark the node up to date&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three steps: settle any recompute that's already overdue, write &lt;code&gt;node.value&lt;/code&gt; directly with the same primitive a plain &lt;code&gt;signal()&lt;/code&gt; uses (never touching &lt;code&gt;node.computation&lt;/code&gt; or &lt;code&gt;node.sourceValue&lt;/code&gt;), then mark the node clean.&lt;/p&gt;

&lt;p&gt;That first step is worth pausing on, because "settle any overdue recompute" is literal. If the linkedSignal is currently dirty, its source changed since anyone last read it, &lt;code&gt;producerUpdateValueVersion&lt;/code&gt; runs &lt;code&gt;producerRecomputeValue&lt;/code&gt; before your value ever gets written, which means &lt;code&gt;computation&lt;/code&gt; fires once, and if it has any observable side effects, they happen. Your value always wins in the end, the freshly recomputed result gets overwritten immediately after, but "computation never runs" isn't quite true in that specific case. The mark-clean step is what matters for everything &lt;em&gt;after&lt;/em&gt; this call: it tells the next read's staleness check that the node is current, so from then on &lt;code&gt;producerRecomputeValue&lt;/code&gt; is skipped, regardless of what the source is doing in the meantime.&lt;/p&gt;

&lt;p&gt;Line them up and there are three distinct write paths, not two:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;.set()&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;&lt;code&gt;computation&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Default&lt;/strong&gt;, no custom setter&lt;/td&gt;
&lt;td&gt;Calls &lt;code&gt;linkedSignalSetFn(node, v)&lt;/code&gt; directly.&lt;/td&gt;
&lt;td&gt;Does not run. Exception: if the node was already &lt;code&gt;stale&lt;/code&gt; due to an earlier unread source change, it runs once first to settle the pending recomputation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Custom setter&lt;/strong&gt;, setter writes to the source&lt;/td&gt;
&lt;td&gt;Only the source's version is bumped. This node is marked dirty but is otherwise untouched.&lt;/td&gt;
&lt;td&gt;Re-runs &lt;strong&gt;lazily&lt;/strong&gt;, the next time the &lt;code&gt;linkedSignal&lt;/code&gt; is read.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Custom setter&lt;/strong&gt;, setter calls &lt;code&gt;rawSet(v)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Identical to the default case: calls &lt;code&gt;linkedSignalSetFn(node, v)&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Does not run, except when there is already a pending stale recomputation that must settle first.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's the performance case: a custom setter with independent knowledge of the right answer, where redoing the computation would be wasted work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hugeInitialList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sortedItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;linkedSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;expensiveSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="c1"&gt;// recomputes whenever items() changes&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rawSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// We just got an already-sorted page back from the server —&lt;/span&gt;
      &lt;span class="c1"&gt;// no need to re-run expensiveSort() for a value we already know.&lt;/span&gt;
      &lt;span class="nf"&gt;rawSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;sortedItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageFromServer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// writes directly, skipping a fresh expensiveSort() run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that to the temperature example: there, the setter calls &lt;code&gt;tempC.set(...)&lt;/code&gt; because it genuinely needs &lt;code&gt;tempF&lt;/code&gt; to re-derive from the new &lt;code&gt;tempC&lt;/code&gt;. Here, the setter already has the final value, so it bypasses the derivation instead of inverting it.&lt;/p&gt;

&lt;p&gt;And here's the other case, where inverting through the source isn't just wasteful, it doesn't make sense at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;42.7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roundedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;linkedSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rawInput&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rawSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Multiple rawInput strings round to the same integer, there's&lt;/span&gt;
      &lt;span class="c1"&gt;// no single correct string to write back, so just store the value.&lt;/span&gt;
      &lt;span class="nf"&gt;rawSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rounding is lossy: many different &lt;code&gt;rawInput&lt;/code&gt; values produce the same &lt;code&gt;roundedValue&lt;/code&gt;, so there's no correct string for the setter to write back to &lt;code&gt;rawInput&lt;/code&gt;. &lt;code&gt;rawSet&lt;/code&gt; sidesteps the question entirely, it's the same direct write the default &lt;code&gt;.set()&lt;/code&gt; would give you if there were no custom setter at all.&lt;/p&gt;

&lt;p&gt;Both cases share the same shape: &lt;code&gt;rawSet&lt;/code&gt; is for when inverting through the source is either wasteful or doesn't make sense, and you'd rather just set the value. It also keeps a single public method for callers: &lt;code&gt;.set(x)&lt;/code&gt; works the same way from the outside whether this particular value is a normal edit that should flow through the source, or a precomputed result the setter recognizes and stores directly. The linkedSignal, not the caller, decides which applies.&lt;/p&gt;

&lt;p&gt;That's the entire write path, everything &lt;code&gt;.set()&lt;/code&gt; and &lt;code&gt;.update()&lt;/code&gt; can reach, custom setter or not. &lt;code&gt;rawSet&lt;/code&gt; can reach into the recompute machinery to settle a pending stale value, but never the other way around.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Under the Hood: The Recompute Path
&lt;/h2&gt;

&lt;p&gt;The recompute lives somewhere else entirely, in the &lt;a href="https://github.com/angular/angular/blob/main/packages/core/primitives/signals/src/linked_signal.ts" rel="noopener noreferrer"&gt;signal primitives&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;linkedSignalGetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;producerUpdateValueVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// check staleness, recompute if needed&lt;/span&gt;
  &lt;span class="nf"&gt;producerAccessed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every signal getter, plain &lt;a href="https://angular.dev/guide/signals" rel="noopener noreferrer"&gt;&lt;code&gt;signal()&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://angular.dev/guide/signals#computed-signals" rel="noopener noreferrer"&gt;&lt;code&gt;computed()&lt;/code&gt;&lt;/a&gt;, or &lt;code&gt;linkedSignal()&lt;/code&gt;, is shaped this way. &lt;a href="https://github.com/angular/angular/blob/main/packages/core/primitives/signals/src/graph.ts" rel="noopener noreferrer"&gt;&lt;code&gt;producerUpdateValueVersion&lt;/code&gt;&lt;/a&gt; checks whether any dependency's version changed since the last read, and if so, calls &lt;a href="https://github.com/angular/angular/blob/main/packages/core/primitives/signals/src/linked_signal.ts" rel="noopener noreferrer"&gt;&lt;code&gt;producerRecomputeValue&lt;/code&gt;&lt;/a&gt;, which reruns the computation and assigns straight to &lt;code&gt;node.value&lt;/code&gt; as a plain field write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newSourceValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sourceValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newSourceValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole answer. &lt;code&gt;tempC.set(100)&lt;/code&gt; only flips a dirty flag and bumps a version counter, no computation runs at that moment. The actual recomputation is deferred until the next time something reads &lt;code&gt;tempF()&lt;/code&gt;, and when it happens, it happens through &lt;code&gt;producerRecomputeValue&lt;/code&gt;, a function that has never heard of &lt;code&gt;customSet&lt;/code&gt;. Not directly, not indirectly. There's no code path in the primitives package that leads back to your setter. That direction is the one that would actually matter: if recomputation could call back into &lt;code&gt;customSet&lt;/code&gt;, that's the loop you'd have to worry about, and it provably can't. The reverse isn't quite as absolute, &lt;code&gt;rawSet&lt;/code&gt; can call into &lt;code&gt;producerRecomputeValue&lt;/code&gt; to settle a pending stale value, as covered above, but even then it only ever reaches this same recompute logic, never back into your setter. That asymmetry, not a strict mutual isolation, is the whole reason a linkedSignal can safely both read from and write to the same piece of state.&lt;/p&gt;

&lt;p&gt;One detail worth keeping in mind: this recompute isn't scheduled for later, it's synchronous and pull-based. If &lt;code&gt;tempF()&lt;/code&gt; is read inside another &lt;code&gt;computed()&lt;/code&gt;, the staleness check and recompute for &lt;code&gt;tempF&lt;/code&gt; happen inline, in the same call stack, before that outer computed can finish. Angular's signals are lazy all the way down: nothing recalculates until someone actually asks for a value, but once something does ask, the whole stale chain resolves immediately, in dependency order.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;So, back to the question that opened this piece: no, it doesn't loop, and now you know exactly why: &lt;code&gt;producerRecomputeValue&lt;/code&gt; can't reach back into &lt;code&gt;customSet&lt;/code&gt;, only &lt;code&gt;rawSet&lt;/code&gt; reaches the other way. That one-way gap is what makes it safe.&lt;/p&gt;

&lt;p&gt;The custom &lt;code&gt;set()&lt;/code&gt; option is a small addition on paper, one new field in an options object, but it changes what &lt;code&gt;linkedSignal()&lt;/code&gt; is for. Before, it was a read-only derivation with a writable escape hatch bolted on by convention (the default &lt;code&gt;set&lt;/code&gt;/&lt;code&gt;update&lt;/code&gt;, with no custom setter, just overwrites the linkedSignal's own value directly). Now it can be the field-level API for a slice of larger state, without ever creating a second source of truth. And it does that safely because Angular's signal graph keeps "someone wrote to me" and "one of my dependencies changed" as two structurally separate events, not because of anything defensive in the code you write.&lt;/p&gt;

&lt;p&gt;If you haven't read &lt;a href="https://medium.com/@giorgio.galassi/angular-v19-computed-vs-linkedsignal-signals-3130b70861b8" rel="noopener noreferrer"&gt;my earlier piece comparing &lt;code&gt;computed()&lt;/code&gt; and &lt;code&gt;linkedSignal()&lt;/code&gt;&lt;/a&gt;, that's the conceptual "when to reach for which" companion to this one, this article is the deep end: the internals that explain why the two-way version is safe.&lt;/p&gt;

&lt;p&gt;If you're touching &lt;code&gt;linkedSignal()&lt;/code&gt; in a codebase that also uses &lt;a href="https://angular.dev/guide/forms/signals/overview" rel="noopener noreferrer"&gt;Signal Forms&lt;/a&gt; or &lt;a href="https://angular.dev/guide/signals/resource" rel="noopener noreferrer"&gt;&lt;code&gt;resource()&lt;/code&gt;&lt;/a&gt;, the same lazy, pull-based recompute model is running underneath all of them. Worth understanding once, pays off everywhere.&lt;/p&gt;




&lt;p&gt;If you found this helpful, follow me here and on &lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for more deep dives into Angular, web performance, and modern frontend development.&lt;/p&gt;

&lt;p&gt;See you in the next one! 🤙🏻&lt;br&gt;
— G.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>angularsignals</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Angular v19+ — Understanding @defer: Blocks, Triggers, and Deferrable Views (Part 2) 🔥🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Sun, 07 Dec 2025 10:12:11 +0000</pubDate>
      <link>https://dev.to/ggalassi/angular-v19-understanding-defer-blocks-triggers-and-deferrable-views-part-2-31kj</link>
      <guid>https://dev.to/ggalassi/angular-v19-understanding-defer-blocks-triggers-and-deferrable-views-part-2-31kj</guid>
      <description>&lt;p&gt;One of the main goals in modern Angular apps is reducing hydration costs and improving user experience. With Angular v19, we finally get native tools to achieve this: &lt;strong&gt;Incremental Hydration&lt;/strong&gt; and &lt;strong&gt;Event Replay&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://medium.com/@giorgio.galassi/angular-v18-understanding-defer-blocks-triggers-and-deferrable-views-part-1-5a5dfaf52cd2" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, we explored the fundamentals of &lt;code&gt;@defer&lt;/code&gt; blocks and triggers. Now, let’s see how Angular extends that foundation to make hydration smarter and interactions smoother.&lt;/p&gt;




&lt;h3&gt;
  
  
  💧 Incremental Hydration
&lt;/h3&gt;

&lt;p&gt;Hydration is the process of transforming a pre-rendered HTML page (generated by server-side rendering) into a fully interactive Angular app on the client side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://angular.dev/guide/incremental-hydration" rel="noopener noreferrer"&gt;&lt;strong&gt;Incremental Hydration&lt;/strong&gt;&lt;/a&gt; takes this a step further by letting you control &lt;em&gt;when&lt;/em&gt; specific parts of your application should become interactive.&lt;/p&gt;

&lt;p&gt;This means better &lt;a href="https://developers.google.com/search/docs/appearance/core-web-vitals?hl=en" rel="noopener noreferrer"&gt;&lt;strong&gt;Core Web Vitals&lt;/strong&gt;&lt;/a&gt;, faster &lt;strong&gt;initial paints&lt;/strong&gt;, and a smoother user experience overall.&lt;/p&gt;

&lt;p&gt;Activate it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// other providers&lt;/span&gt;
    &lt;span class="nf"&gt;provideClientHydration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withIncrementalHydration&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once configured, you can start deferring hydration for parts of your UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@defer (hydrate on idle) {
  &lt;span class="nt"&gt;&amp;lt;large-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @placeholder {
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Large component placeholder&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hydrate syntax mirrors the classic &lt;code&gt;@defer&lt;/code&gt; structure, supporting the same triggers and patterns. You can even combine behaviors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@defer (on idle; hydrate on interaction) {
  &lt;span class="nt"&gt;&amp;lt;large-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @placeholder {
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Example Placeholder&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;on interaction&lt;/code&gt; trigger behaves differently when used with &lt;code&gt;hydrate&lt;/code&gt;. Classic &lt;code&gt;@defer&lt;/code&gt; requires an element reference, while &lt;code&gt;hydrate&lt;/code&gt; doesn’t—the interaction happens directly with the de-hydrated version of the component.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Classic @defer --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;#greeting&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello!&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

@defer (on interaction(greeting)) {
  &lt;span class="nt"&gt;&amp;lt;greetings-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}

&lt;span class="c"&gt;&amp;lt;!-- @defer with hydrate --&amp;gt;&lt;/span&gt;
@defer (hydrate on interaction) {
  &lt;span class="nt"&gt;&amp;lt;greetings-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;hydrate&lt;/code&gt;, the component itself is the trigger, simplifying setup and making it easier to use in real-world hydration scenarios.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why it&amp;nbsp;matters
&lt;/h4&gt;

&lt;p&gt;Incremental Hydration is especially valuable for large apps with multiple deferred views — dashboards, content-heavy pages, or e-commerce layouts. It helps you decide &lt;em&gt;when&lt;/em&gt; to activate interactivity, improving both &lt;strong&gt;performance&lt;/strong&gt; and &lt;strong&gt;responsiveness&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚡ Event&amp;nbsp;Replay
&lt;/h3&gt;

&lt;p&gt;Server-Side Rendering (SSR) is key for performance and SEO, but it introduces a common problem: &lt;strong&gt;lost interactions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When HTML is pre-rendered, users can start clicking before the JavaScript is fully hydrated. Without proper handling, those interactions are lost — leading to broken UX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://angular.dev/guide/hydration#capturing-and-replaying-events" rel="noopener noreferrer"&gt;&lt;strong&gt;Event Replay&lt;/strong&gt;&lt;/a&gt; fixes this by capturing events during hydration and replaying them in the same order they occurred.&lt;/p&gt;

&lt;p&gt;This ensures every user action is processed, even if it happened before the app became interactive.&lt;/p&gt;

&lt;p&gt;Activate it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// other providers&lt;/span&gt;
    &lt;span class="nf"&gt;provideClientHydration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withEventReplay&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re already using &lt;code&gt;withIncrementalHydration()&lt;/code&gt;, you don’t need to add &lt;code&gt;withEventReplay()&lt;/code&gt; separately—it’s included automatically.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why it&amp;nbsp;matters
&lt;/h4&gt;

&lt;p&gt;Event Replay ensures that &lt;strong&gt;no user action is lost&lt;/strong&gt;. It captures input events (like clicks, scrolls, or typing) and replays them after hydration. The result is a seamless, frustration-free experience for users.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧩 Combined&amp;nbsp;Example
&lt;/h3&gt;

&lt;p&gt;Here’s how both features can work together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideClientHydration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withIncrementalHydration&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@defer (hydrate on interaction) {
  &lt;span class="nt"&gt;&amp;lt;user-profile&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @placeholder {
  &lt;span class="nt"&gt;&amp;lt;loading-spinner&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, Angular hydrates the &lt;code&gt;user-profile&lt;/code&gt; component only after the user interacts with its placeholder. Any clicks made before hydration are safely replayed, thanks to &lt;strong&gt;Event Replay&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 Behind the&amp;nbsp;Scenes
&lt;/h3&gt;

&lt;p&gt;Under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Incremental Hydration&lt;/strong&gt; reuses the server-generated DOM and attaches event listeners lazily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Replay&lt;/strong&gt; hooks into the browser’s event queue, caching early interactions and re-dispatching them once hydration is complete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These optimizations work together to provide a faster, more resilient user experience especially in apps with complex, interactive UIs.&lt;/p&gt;




&lt;h3&gt;
  
  
  🪄 Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Incremental Hydration&lt;/strong&gt; lets you decide &lt;em&gt;when&lt;/em&gt; to make parts of your app interactive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Replay&lt;/strong&gt; ensures &lt;em&gt;no interaction is lost&lt;/em&gt; during hydration.&lt;/li&gt;
&lt;li&gt;Combined, they improve &lt;strong&gt;Core Web Vitals&lt;/strong&gt;, reduce &lt;strong&gt;Time to Interactive&lt;/strong&gt;, and simplify performance optimization in Angular.&lt;/li&gt;
&lt;li&gt;👉 Try it live on &lt;a href="https://stackblitz.com/~/github.com/giorgiogalassi/defer" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt; to see Incremental Hydration and Event Replay in action! &lt;strong&gt;Core Web Vitals&lt;/strong&gt;, reduce &lt;strong&gt;Time to Interactive&lt;/strong&gt;, and simplify performance optimization in Angular.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you found this helpful, follow me here and on &lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt; for more deep dives into Angular, web performance, and modern frontend development.&lt;/p&gt;

&lt;p&gt;See you in the next one! 🤙🏻&lt;br&gt;
— &lt;strong&gt;G&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>hydration</category>
      <category>incrementalhydration</category>
      <category>defer</category>
    </item>
    <item>
      <title>Angular v18+ — Understanding @defer: Blocks, Triggers, and Deferrable Views (Part 1) 🔥🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Sun, 07 Dec 2025 10:02:30 +0000</pubDate>
      <link>https://dev.to/ggalassi/angular-v18-understanding-defer-blocks-triggers-and-deferrable-views-part-1-1m56</link>
      <guid>https://dev.to/ggalassi/angular-v18-understanding-defer-blocks-triggers-and-deferrable-views-part-1-1m56</guid>
      <description>&lt;p&gt;Angular v18 continues to introduce powerful new features, some aimed at simplifying development workflows, such as the &lt;a href="https://medium.com/@giorgio.galassi/angular-v18-introducing-let-a-new-way-to-declare-variables-and-do-logic-in-templates-8b3f4d196b23" rel="noopener noreferrer"&gt;&lt;code&gt;@let&lt;/code&gt;&lt;/a&gt; syntax, &lt;a href="https://medium.com/@giorgio.galassi/angular-v18-functional-redirection-using-redirectto-10c853d9d837" rel="noopener noreferrer"&gt;&lt;code&gt;redirectTo&lt;/code&gt;&lt;/a&gt; as a function, and &lt;a href="https://medium.com/@giorgio.galassi/angular-v18-ng-content-fallback-content-dd3d2c238373" rel="noopener noreferrer"&gt;&lt;code&gt;ng-content&lt;/code&gt;&lt;/a&gt; fallback content. Other functionalities, however, are designed to enhance application performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Angular’s &lt;code&gt;@defer&lt;/code&gt;&amp;nbsp;Block?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@defer&lt;/code&gt; block is part of Angular's newly introduced control-flow syntax family (&lt;code&gt;@if&lt;/code&gt;, &lt;code&gt;@for&lt;/code&gt;, &lt;code&gt;@switch&lt;/code&gt;, and more). This feature allows developers to delay the loading of specific elements until certain pre-determined triggers are met. These triggers can be selected from a predefined list or customized. You can also create a sequence of triggers to control exactly when the component is loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@defer {
  &lt;span class="nt"&gt;&amp;lt;my-very-heavy-component&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Why use Angular’s deferrable views for optimized performance?
&lt;/h3&gt;

&lt;p&gt;Deferrable views offer the advantage of significantly reducing the initial bundle size. By deferring a view, you can avoid loading a heavy component until it’s needed, improving both performance and user experience. For example, you might delay loading a component that becomes relevant later in the application’s flow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Not all components are deferrable. To use &lt;code&gt;@defer&lt;/code&gt;, a component must be standalone, and it's essential to avoid referencing it outside the &lt;code&gt;@defer&lt;/code&gt; block (e.g., using &lt;code&gt;@ViewChild&lt;/code&gt;).*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Starting with &lt;a href="https://blog.angular.dev/the-future-is-standalone-475d7edbc706" rel="noopener noreferrer"&gt;Angular v19&lt;/a&gt;, newly created components will be standalone by default, meaning they will also be deferrable by default.&lt;/p&gt;
&lt;h3&gt;
  
  
  Exploring &lt;code&gt;@defer&lt;/code&gt; Blocks: Syntax and&amp;nbsp;Usage
&lt;/h3&gt;

&lt;p&gt;This section breaks down the different &lt;code&gt;@defer&lt;/code&gt; sub-blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@placehodelr\&lt;/code&gt;&lt;/strong&gt;: Displays default content while the deferred component is loading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@loading\&lt;/code&gt;&lt;/strong&gt;: Shows a loading indicator while the deferred content is being fetched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@error\&lt;/code&gt;&lt;/strong&gt;: Displays a fallback message if the deferred content fails to load.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; All components, directives, or pipes used within any of these sub-blocks are &lt;strong&gt;eagerly loaded&lt;/strong&gt;.*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  How to Combine Multiple Triggers with &lt;code&gt;@defer&lt;/code&gt; in Angular&amp;nbsp;v18
&lt;/h3&gt;

&lt;p&gt;Angular offers various triggers for controlling when deferred content is loaded:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;on idle&lt;/strong&gt;: Loads when the browser is idle; it’s the default trigger for the &lt;code&gt;@defer&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;on viewport&lt;/strong&gt;: Loads when the content enters the viewport.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;on interaction&lt;/strong&gt;: Loads based on specific user interactions, such as clicks or form submissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;on hover&lt;/strong&gt;: Triggers when the user hovers over the element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;on immediate&lt;/strong&gt;: Loads content immediately under specific conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;on timer&lt;/strong&gt;: Loads after a specified time delay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;when:&lt;/strong&gt; Specifies a condition as an expression that returns a boolean.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also combine triggers to create complex loading scenarios, such as loading content when it enters the viewport or when the user hovers over it.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- More than one `on` example --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Multiple on triggers are always `OR` conditions --&amp;gt;&lt;/span&gt;
@defer (on viewport; on timer(5s)) {
  &lt;span class="nt"&gt;&amp;lt;my-very-heavy-component&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @placeholder {
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"placeholder.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}

&lt;span class="c"&gt;&amp;lt;!-- Defer `when` example --&amp;gt;&lt;/span&gt;
@defer (when cond) {
  &lt;span class="nt"&gt;&amp;lt;my-very-heavy-component&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}

&lt;span class="c"&gt;&amp;lt;!-- Defer `on` and `when` example --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- `on` and `where` conditions are always `OR` conditions --&amp;gt;&lt;/span&gt;
@defer (on viewport; when cond) {
  &lt;span class="nt"&gt;&amp;lt;calendar-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @placeholder {
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"placeholder.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Best Practices for Using &lt;code&gt;@defer&lt;/code&gt; in Angular v18: Tips and Considerations
&lt;/h3&gt;

&lt;p&gt;To use &lt;code&gt;@defer&lt;/code&gt; effectively, it’s essential to understand potential performance implications and optimize its usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoiding Layout Shifts&lt;/strong&gt;: Ensure that deferred components do not cause layout shifts that could impact the user experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standalone Components Only&lt;/strong&gt;: Make sure components used with &lt;code&gt;@defer&lt;/code&gt; are standalone to avoid dependency conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combining Triggers Wisely&lt;/strong&gt;: Use combinations of triggers strategically to maximize performance and user engagement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Nested &lt;code&gt;@defer&lt;/code&gt;&amp;nbsp;Blocks
&lt;/h3&gt;

&lt;p&gt;It is possible to have nested &lt;code&gt;@defer&lt;/code&gt; blocks, with each block respecting and waiting for its own trigger. To prevent unexpected behavior, it is recommended to assign different triggers to each block, unless you intentionally want multiple blocks to respond to the same triggers.&lt;/p&gt;

&lt;p&gt;Below, you can find a StackBlitz project where you can explore nested &lt;code&gt;@defer&lt;/code&gt; blocks:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://stackblitz.com/~/github.com/giorgiogalassi/defer?file=src%2Fmain.ts&amp;amp;amp;source=post_page-----5a5dfaf52cd2---------------------------------------" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;stackblitz.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;At the time of this article’s publication, the StackBlitz contains examples of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;on interaction&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;on viewport&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Nested &lt;code&gt;@defer&lt;/code&gt; blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To observe the deferred loading in action, open the network tab in your browser’s developer tools and filter by JavaScript (JS) to see only the downloaded files. Experiment with the application, and you will notice different chunks of components being loaded in a deferred manner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Starting with Angular v18, the &lt;code&gt;@defer&lt;/code&gt; feature provides a powerful way to optimize your application’s loading strategy. By leveraging triggers and fallback blocks (&lt;code&gt;@placeholder&lt;/code&gt;, &lt;code&gt;@loading&lt;/code&gt;, &lt;code&gt;@error&lt;/code&gt;), you can create efficient, user-friendly experiences.&lt;/p&gt;

&lt;p&gt;Adopting a “deferrable-first” mindset is both useful and important, as it allows us to deliver smoother user experiences, thanks in part to a lighter bundle.&lt;/p&gt;

&lt;p&gt;Future articles will dive deeper into advanced &lt;code&gt;@defer&lt;/code&gt; usage and its integration with other Angular features, so stay tuned!&lt;/p&gt;

&lt;h3&gt;
  
  
  Next steps
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Prefetching&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;@defer&lt;/code&gt; block also supports configuration for pre-fetching components. &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Prefetch" rel="noopener noreferrer"&gt;Prefetching&lt;/a&gt; is a process where we anticipate where the user might navigate after a specific action and begin loading resources that could be needed. In Angular, this can be achieved using the &lt;code&gt;@defer&lt;/code&gt; block. The prefetch syntax works similarly to main &lt;code&gt;@defer&lt;/code&gt; conditions; &lt;code&gt;when&lt;/code&gt; and/or &lt;code&gt;on&lt;/code&gt; can be used to declare different triggers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@defer (on interaction; prefetch on idle) {
  &lt;span class="nt"&gt;&amp;lt;calendar-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @placeholder {
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"placeholder.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Incremental Hydration&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Hydration transforms a pre-rendered HTML page (produced by server-side rendering) into a fully interactive Angular application on the client side. With &lt;a href="https://www.youtube.com/watch?v=I4n1IcZ3vRs" rel="noopener noreferrer"&gt;incremental hydration&lt;/a&gt; (&lt;a href="https://github.com/angular/angular/discussions/57664" rel="noopener noreferrer"&gt;here&lt;/a&gt; the complete RFC), you can determine when specific parts of your application need to become interactive.&lt;/p&gt;

&lt;p&gt;This helps improve &lt;a href="https://developers.google.com/search/docs/appearance/core-web-vitals?hl=it" rel="noopener noreferrer"&gt;Core Web Vitals&lt;/a&gt; and provides a faster initial paint with better performance for the end-user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Hydrate `when` example --&amp;gt;&lt;/span&gt;
@defer (hydrate when user() !== null){
  &lt;span class="nt"&gt;&amp;lt;some-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}

&lt;span class="c"&gt;&amp;lt;!-- Hydrate `on` example --&amp;gt;&lt;/span&gt;
@defer (hydrate on interaction) {
  &lt;span class="nt"&gt;&amp;lt;some-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}

&lt;span class="c"&gt;&amp;lt;!-- Defer plus Hydrate `on` example --&amp;gt;&lt;/span&gt;
@defer (on viewport; hydrate on interaction) {
  &lt;span class="nt"&gt;&amp;lt;some-cmp&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you found this helpful, follow me here and on &lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt; for more deep dives into Angular, web performance, and modern frontend development.&lt;/p&gt;

&lt;p&gt;See you in the next one! 🤙🏻&lt;br&gt;
— &lt;strong&gt;G&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>incrementalhydration</category>
      <category>defer</category>
      <category>prefetch</category>
    </item>
    <item>
      <title>Angular v19+ — computed vs linkedSignal signals 🔥🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Sun, 07 Dec 2025 09:53:01 +0000</pubDate>
      <link>https://dev.to/ggalassi/angular-v19-computed-vs-linkedsignal-signals-27o7</link>
      <guid>https://dev.to/ggalassi/angular-v19-computed-vs-linkedsignal-signals-27o7</guid>
      <description>&lt;p&gt;Angular’s move toward a signal‑first ecosystem didn’t end with &lt;a href="https://medium.com/@giorgio.galassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-8a387c7d9351" rel="noopener noreferrer"&gt;&lt;code&gt;resource()&lt;/code&gt;&lt;/a&gt;&lt;a href="https://medium.com/@giorgio.galassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-8a387c7d9351" rel="noopener noreferrer"&gt;, &lt;/a&gt;&lt;a href="https://medium.com/@giorgio.galassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-8a387c7d9351" rel="noopener noreferrer"&gt;&lt;code&gt;rxResource()&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://medium.com/@giorgio.galassi/angular-v19-understanding-the-new-httpresource-api-837e1dadc990" rel="noopener noreferrer"&gt;&lt;code&gt;httpResource()&lt;/code&gt;&lt;/a&gt;. Those primitives made async flows cleaner, but they also exposed a deeper shift: the way we model state is changing. When you start structuring components around signals, two primitives quickly become the backbone of state organization: &lt;a href="https://angular.dev/guide/signals#computed-signals" rel="noopener noreferrer"&gt;&lt;strong&gt;computed&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://angular.dev/guide/signals/linked-signal" rel="noopener noreferrer"&gt;&lt;strong&gt;linkedSignal&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At a glance they seem similar, but they solve very different problems. One focuses on pure derivation. The other acts as a controlled bridge between component state and something external. Understanding the difference is key to avoiding subtle bugs.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔧 Signals in Angular — a quick&amp;nbsp;recap
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;signal&lt;/code&gt; stores writable state. A &lt;code&gt;computed&lt;/code&gt; value is automatically recalculated whenever its dependencies change. You never set it manually, and it shouldn’t perform side effects. It’s a clean, predictable way to model values that depend entirely on other signals.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;linkedSignal&lt;/code&gt; builds on this mechanism but shifts the focus: instead of deriving values, it synchronizes state with a source that lives outside the local signal graph.&lt;/p&gt;




&lt;h3&gt;
  
  
  📐 What computed actually&amp;nbsp;does
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;computed&lt;/code&gt; value is nothing more than a function of other signals. Its job is to keep a derived value always in sync with its inputs. You give it the signals it depends on, and Angular takes care of the rest.&lt;/p&gt;

&lt;p&gt;For example, if you have two form fields and want a combined value and a quick validity check, a &lt;code&gt;computed&lt;/code&gt; is exactly the right tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here everything is deterministic: every value can be recalculated at any time from the source signals. You never push data back, and nothing lives outside the model. As long as your flow is one‑way, state that drives other state, &lt;code&gt;computed&lt;/code&gt; is the cleanest and simplest option.&lt;/p&gt;

&lt;p&gt;Where it falls short is when you need to mirror or update something that doesn’t originate inside the component. For that, it’s the wrong tool.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔗 Why linkedSignal exists
&lt;/h3&gt;

&lt;p&gt;Some state relationships require a value to reset reactively when its source changes while still remaining fully writable by the user. A &lt;code&gt;computed&lt;/code&gt; can handle the reactive reset but cannot be written to. A plain &lt;code&gt;signal&lt;/code&gt; can be written to, but it cannot reactively recalculate itself from a source. &lt;code&gt;linkedSignal&lt;/code&gt; fills this exact gap.&lt;/p&gt;

&lt;p&gt;Instead of wiring effects to manually reset values, a pattern that becomes noisy and risks unintended write loops, &lt;code&gt;linkedSignal&lt;/code&gt; expresses the relationship declaratively: &lt;em&gt;“derive from this source unless the user overrides it.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;linkedSignal&lt;/code&gt; listens to the source and recalculates its value whenever that source changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔍 &lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;computation&lt;/code&gt; function receives two arguments: the new source value and a &lt;code&gt;previous&lt;/code&gt; object. &lt;code&gt;previous.source&lt;/code&gt; holds the source value before the update, while &lt;code&gt;previous.value&lt;/code&gt; contains the previous &lt;em&gt;&lt;code&gt;linkedSignal&lt;/code&gt;&lt;/em&gt; value, including user overrides. This allows you to implement simple resets, conditional resets, or more advanced transition rules based on both old and new state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In parallel, the linked signal continues to expose the usual writable signal API (&lt;code&gt;set&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;), so user overrides remain straightforward.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧪 Two practical examples
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Pure derived state — computed
&lt;/h3&gt;

&lt;p&gt;If a value can always be derived directly from its inputs and never needs manual intervention, a &lt;code&gt;computed&lt;/code&gt; is the simplest fit. Filtering a list based on user input is a classic example where nothing external is involved and no override is necessary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;React&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reactive default with user override — linkedSignal
&lt;/h3&gt;

&lt;p&gt;A more nuanced scenario is when the value depends on another piece of state but must still allow direct user edits. Consider an app with a theme selector and a user‑adjustable contrast slider.&lt;/p&gt;

&lt;p&gt;The contrast should reset to the optimal default whenever the theme changes, &lt;strong&gt;but&lt;/strong&gt; the user must be able to override that value afterward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The source signal&lt;/span&gt;
&lt;span class="nx"&gt;selectedTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Linked signal that recomputes when the theme changes&lt;/span&gt;
&lt;span class="nx"&gt;contrastLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;linkedSignal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selectedTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;computation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Default contrast for dark theme&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Default contrast for light theme&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// User interaction and manual override&lt;/span&gt;
&lt;span class="nf"&gt;onContrastChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newContrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contrastLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newContrast&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup, &lt;code&gt;linkedSignal&lt;/code&gt; makes the relationship explicit and predictable. When the theme changes, &lt;code&gt;contrastLevel&lt;/code&gt; recomputes based on the new theme. When the user adjusts the value, the override takes priority. This avoids side‑effect‑driven resets and keeps the logic clean and declarative.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 How to reason about the&amp;nbsp;two
&lt;/h3&gt;

&lt;p&gt;The difference is straightforward once you think in terms of data direction.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;computed&lt;/code&gt; value is one‑way. Data flows from your signals into the computed output. There’s no feedback loop and no external synchronization.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;linkedSignal&lt;/code&gt; is two‑way. It sits between your component and another system. Reading it pulls in the current external value, while updating it pushes data out.&lt;/p&gt;

&lt;p&gt;Whenever you need writable state that remains aligned with an external layer, &lt;code&gt;linkedSignal&lt;/code&gt; is the right choice.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎯 Picking the right&amp;nbsp;tool
&lt;/h3&gt;

&lt;p&gt;Use a &lt;code&gt;computed&lt;/code&gt; when the value is entirely derived from other signals and doesn’t need to be written manually. It’s ideal for internal one‑direction flows within a component.&lt;/p&gt;

&lt;p&gt;Use a &lt;code&gt;linkedSignal&lt;/code&gt; when the component must expose a writable signal that represents an external state and must stay synchronized with it. This applies to inputs, stores, and any layer where the source of truth lives outside the component.&lt;/p&gt;

&lt;p&gt;When you’re unsure, start with &lt;code&gt;computed&lt;/code&gt;. If you later realize you’re wiring manual read‑and‑write glue between the component and external state, that’s usually the moment when switching to &lt;code&gt;linkedSignal&lt;/code&gt; cleans everything up.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✨ Closing&amp;nbsp;thoughts
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;computed&lt;/code&gt; and &lt;code&gt;linkedSignal&lt;/code&gt; address distinct needs. The first keeps derived values predictable and local. The second gives you a structured way to mirror and update external state without abandoning the signal mindset.&lt;/p&gt;

&lt;p&gt;Most of your application logic will rely on &lt;code&gt;signal&lt;/code&gt; and &lt;code&gt;computed&lt;/code&gt;. Reach for &lt;code&gt;linkedSignal&lt;/code&gt; only when synchronization with something external is required.&lt;/p&gt;




&lt;p&gt;If you found this helpful, follow me here and on &lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt; for more deep dives into Angular, web performance, and modern frontend development.&lt;/p&gt;

&lt;p&gt;See you in the next one! 🤙🏻&lt;br&gt;
 — &lt;strong&gt;G&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>linkedsignal</category>
      <category>signal</category>
      <category>computed</category>
    </item>
    <item>
      <title>Angular v18+ — Introducing @let syntax: A New Way to Declare Variables and do Logic in Templates 🔥🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Fri, 21 Mar 2025 10:50:03 +0000</pubDate>
      <link>https://dev.to/ggalassi/angular-v18-introducing-let-syntax-a-new-way-to-declare-variables-and-do-logic-in-templates-3k1i</link>
      <guid>https://dev.to/ggalassi/angular-v18-introducing-let-syntax-a-new-way-to-declare-variables-and-do-logic-in-templates-3k1i</guid>
      <description>&lt;p&gt;Here we are again with another article that dives into the new features of &lt;strong&gt;&lt;em&gt;Angular v18&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, you will explore, understand the pros and cons, and learn how to use the new &lt;code&gt;@let&lt;/code&gt; template syntax. This powerful feature comes with great responsibility, as Uncle Ben would say.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 What’s &lt;code&gt;@let&lt;/code&gt; syntax?
&lt;/h2&gt;

&lt;p&gt;As TypeScript developers, we are familiar with creating variables using &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt;, and &lt;code&gt;let&lt;/code&gt;. In this scenario, &lt;code&gt;@let&lt;/code&gt; works similarly, but with one key difference: you can now create variables directly at the template level.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 How to use the &lt;code&gt;@let&lt;/code&gt; Syntax in Angular?
&lt;/h2&gt;

&lt;p&gt;Nothing is better to see some code in order to understand something.&lt;/p&gt;

&lt;p&gt;Let’s see a basic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@let name = 'Giorgio';

&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;{{ name }}&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you might expect, the content of the variable will be rendered correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Giorgio&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens up many new ways to handle our code, allowing us to move some logic directly within the template itself. With &lt;code&gt;@let&lt;/code&gt;, you can declare variables and use them throughout your template.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Advanced usage of &lt;code&gt;@let&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Of course, the power of &lt;code&gt;@let&lt;/code&gt; does not stop here. You can also perform conditional logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@let isAuth = authService.isAuth();
@let username = authService.getUsername();

@let message = isAuth ? 'Welcome, ' + username + '!' : 'Welcome Anonymus!'

&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;{{ message }}&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this simple example, you can already see how much cleaner our code can be. Let’s take it a step further.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧮 Working with Arrays
&lt;/h2&gt;

&lt;p&gt;Sometimes you want to map or filter your arrays. Generally, you would write a function for that, save the output within the &lt;strong&gt;&lt;em&gt;TypeScript&lt;/em&gt;&lt;/strong&gt; file, and use it in the template. Now, you can do it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@let onlineUsers = users.filter(({ status }) =&amp;gt; status === 'online');

@for(user of onlineUsers; track user.id) {
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;{{ user.name }} {{ user.surname }}&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔄 Working with the Angular Async Pipe
&lt;/h2&gt;

&lt;p&gt;If you’ve worked with Angular, you’ve likely used the &lt;code&gt;async&lt;/code&gt; pipe. Here's how you can combine it with &lt;code&gt;@let&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@let user = user$ | async;

&lt;span class="nt"&gt;&amp;lt;per&amp;gt;&lt;/span&gt;{{ user | json }}&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;

@let userPosts = user.posts || [];

@for(post of userPosts; track post.id) {
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;{{ post.title }}&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ post.content }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
} @empty {
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;No post to show!&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;@let&lt;/code&gt; is used to handle asynchronous data and iterate over user posts. The code is cleaner and keeps the logic within the template, reducing the need for additional component code.&lt;/p&gt;

&lt;p&gt;However, be careful when using &lt;code&gt;@let&lt;/code&gt; in combination with the &lt;code&gt;async&lt;/code&gt; pipe, as it may cause some performance issues.&lt;/p&gt;

&lt;p&gt;To avoid this, declare a variable to handle the subscription and use that variable wherever needed, as shown in the example above.&lt;/p&gt;

&lt;p&gt;This approach ensures a single subscription and avoids multiple re-evaluations, which can improve performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Conclusions
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;@let&lt;/code&gt; syntax in Angular v18 offers a powerful way to declare variables and handle logic directly in templates. It simplifies the code and enhances readability, making it easier to manage dynamic and conditional content. However, with great power comes great responsibility, so use it wisely to keep your templates clean and maintainable.&lt;/p&gt;




&lt;p&gt;Thank you for staying with me, and I hope everything was clear. Feel free to explore more of my articles and &lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;&lt;strong&gt;follow me on LinkedIn!&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you in the next one!&lt;/p&gt;

&lt;p&gt;Best, G.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Angular v19+ — Understanding the New httpResource() API 🔥🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Thu, 20 Mar 2025 10:50:11 +0000</pubDate>
      <link>https://dev.to/ggalassi/angular-v19-understanding-the-new-httpresource-api-23h</link>
      <guid>https://dev.to/ggalassi/angular-v19-understanding-the-new-httpresource-api-23h</guid>
      <description>&lt;p&gt;&lt;a href="https://blog.angular.dev/angular-19-2-is-now-available-673ec70aea12" rel="noopener noreferrer"&gt;Angular v19.2&lt;/a&gt; is here, bringing yet another set of features designed to simplify our development experience.&lt;/p&gt;

&lt;p&gt;This time, the spotlight is on the &lt;strong&gt;new &lt;code&gt;httpResource()&lt;/code&gt; API&lt;/strong&gt;, which joins the resource family, alongside &lt;code&gt;resource()&lt;/code&gt; and &lt;code&gt;rxResource()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We previously explored these APIs in another article that you can find below:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-3393" class="crayons-story__hidden-navigation-link"&gt;Angular v19+ — Understanding the New resource() and rxResource() APIs 🔥🚀&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ggalassi" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1717803%2F0624b9d4-7bae-4e4e-a1f1-ae7a5eb7f02b.png" alt="ggalassi profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ggalassi" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Giorgio Galassi
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Giorgio Galassi
                
              
              &lt;div id="story-author-preview-content-2345222" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ggalassi" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1717803%2F0624b9d4-7bae-4e4e-a1f1-ae7a5eb7f02b.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Giorgio Galassi&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-3393" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 20 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-3393" id="article-link-2345222"&gt;
          Angular v19+ — Understanding the New resource() and rxResource() APIs 🔥🚀
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/angular"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;angular&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/resource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;resource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/http"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;http&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-3393#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  ❓How Does It Work?
&lt;/h2&gt;

&lt;p&gt;Let’s first take a look at how the &lt;code&gt;rxResource()&lt;/code&gt; API is typically used to perform a backend call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rxResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core/rxjs-interop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;rxUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rxResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;distinctUntilChanged&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to load!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this example, we still rely on &lt;strong&gt;&lt;code&gt;HttpClient&lt;/code&gt;&lt;/strong&gt;, and the &lt;code&gt;.get()&lt;/code&gt; method returns an &lt;strong&gt;&lt;code&gt;Observable&lt;/code&gt;&lt;/strong&gt;, which we process using &lt;strong&gt;&lt;code&gt;RxJS operators&lt;/code&gt;&lt;/strong&gt; like &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;catchError()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Nothing is wrong with this approach&lt;/strong&gt;, but what if we wanted a &lt;strong&gt;simpler API&lt;/strong&gt; that integrates directly with HttpClient and eliminates the need to manually handle Observables?&lt;/p&gt;
&lt;h2&gt;
  
  
  🚀 Exploring &lt;code&gt;httpResource()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Since &lt;code&gt;httpResource()&lt;/code&gt; is &lt;strong&gt;built directly on top of &lt;code&gt;HttpClient&lt;/code&gt;&lt;/strong&gt;, we no longer need to manually handle API requests.&lt;/p&gt;

&lt;p&gt;Instead, we can simply pass the API &lt;strong&gt;URL as a parameter&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;httpUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With just &lt;strong&gt;this single line of code&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;httpResource()&lt;/code&gt;&lt;/strong&gt; automatically &lt;strong&gt;manages the request logic&lt;/strong&gt;, leveraging Angular's &lt;code&gt;HttpClient&lt;/code&gt; behind the scenes.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;But what if we need more flexibility?&lt;/strong&gt;&lt;br&gt;
Real-world applications often require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting &lt;strong&gt;default values&lt;/strong&gt; to prevent UI issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parsing API responses&lt;/strong&gt; before assigning them to the Signal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s explore how &lt;code&gt;httpResource()&lt;/code&gt; handles this next.&lt;/p&gt;
&lt;h2&gt;
  
  
  📌 &lt;strong&gt;Using&lt;/strong&gt; &lt;code&gt;defaultValue&lt;/code&gt; and &lt;code&gt;parse&lt;/code&gt; &lt;strong&gt;for Safer Data Handling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By default, httpResource() expects valid data from the API. However, in real-world scenarios, we might need to:&lt;/p&gt;

&lt;p&gt;✅ Ensure a &lt;strong&gt;default value&lt;/strong&gt; when the API returns null or undefined.&lt;br&gt;
✅ &lt;strong&gt;Validate and parse&lt;/strong&gt; responses before assigning them to the signal.&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;Zod&lt;/strong&gt; for schema validation, we can enforce &lt;strong&gt;type safety&lt;/strong&gt; directly within &lt;code&gt;httpResource()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define a schema using Zod&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;surname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserResultsSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;infer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;httpUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="c1"&gt;// Ensures an empty array if no data is returned&lt;/span&gt;
    &lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;UserResultsSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Validates and parses the API response&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;Why is this useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prevents UI errors&lt;/strong&gt; from unexpected null values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensures type safety&lt;/strong&gt; by validating API responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eliminates extra validation logic&lt;/strong&gt; in the component&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  🔍 Advanced Use Case
&lt;/h2&gt;

&lt;p&gt;Passing just the &lt;strong&gt;API URL&lt;/strong&gt; is often &lt;strong&gt;not enough&lt;/strong&gt;. In our &lt;code&gt;rxResource()&lt;/code&gt; example, we included a &lt;strong&gt;search query parameter&lt;/strong&gt; tied to the &lt;code&gt;query()&lt;/code&gt; signal. Let’s explore how to achieve the &lt;strong&gt;same behavior&lt;/strong&gt; using the &lt;code&gt;httpResource()&lt;/code&gt; API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;httpUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By passing a &lt;strong&gt;reactive function&lt;/strong&gt; to &lt;code&gt;httpResource()&lt;/code&gt;, the API &lt;strong&gt;automatically listens&lt;/strong&gt; for changes in the &lt;code&gt;query&lt;/code&gt; signal.&lt;br&gt;
&lt;strong&gt;Every time &lt;code&gt;query()&lt;/code&gt; updates, a new request is triggered, ensuring that the latest data is always fetched&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  ⚠️ Important Considerations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Each new API request cancels the previous one&lt;/strong&gt;, similar to how the &lt;code&gt;switchMap&lt;/code&gt; operator works in RxJS.&lt;br&gt;
This is the &lt;strong&gt;default behavior&lt;/strong&gt; of &lt;code&gt;httpResource()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be cautious when using this API for updates&lt;/strong&gt; (PUT, POST, DELETE requests).&lt;br&gt;
A subsequent update &lt;strong&gt;could cancel a previous request&lt;/strong&gt; before it completes, &lt;strong&gt;leading to potential data loss&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Avoid using &lt;code&gt;httpResource()&lt;/code&gt; for mutations&lt;/strong&gt;&lt;br&gt;
✔️ &lt;strong&gt;Use it for fetching data (GET requests) instead&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  ⏳ Enabling API Calls Only When Needed
&lt;/h2&gt;

&lt;p&gt;By default, &lt;code&gt;httpResource()&lt;/code&gt; &lt;strong&gt;automatically executes the API call&lt;/strong&gt; whenever the request function changes. However, in some cases, &lt;strong&gt;we don't want the API to trigger immediately&lt;/strong&gt;—for example, when data fetching should only happen &lt;strong&gt;after a user action&lt;/strong&gt; (e.g., clicking a button).&lt;/p&gt;

&lt;p&gt;We can &lt;strong&gt;conditionally enable&lt;/strong&gt; the request by returning undefined when it should not be executed:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;httpUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isApiEnabled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;✅ Why is this useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoids &lt;strong&gt;unnecessary API calls&lt;/strong&gt; when data isn’t required.&lt;/li&gt;
&lt;li&gt;Helps &lt;strong&gt;control execution flow&lt;/strong&gt;, ensuring the request happens &lt;strong&gt;only&lt;/strong&gt; when a condition is met.&lt;/li&gt;
&lt;li&gt;Ideal for scenarios &lt;strong&gt;like lazy loading, conditional search&lt;/strong&gt;, or &lt;strong&gt;toggle-based requests&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ⚡ What If I Want to Use Pipeable Operators?
&lt;/h2&gt;

&lt;p&gt;Everything is working as expected, but there’s a key difference between rxResource() and httpResource().&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;httpResource()&lt;/code&gt; does &lt;strong&gt;not expose an observable&lt;/strong&gt;, we &lt;strong&gt;can't apply RxJS operators&lt;/strong&gt; like &lt;code&gt;distinctUntilChanged()&lt;/code&gt; or &lt;code&gt;debounceTime()&lt;/code&gt; directly.&lt;br&gt;
This means we need an &lt;strong&gt;alternative approach&lt;/strong&gt; to manage request optimizations.&lt;/p&gt;

&lt;p&gt;There are &lt;strong&gt;two possible solutions&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;strong&gt;&lt;code&gt;rxResource()&lt;/code&gt;&lt;/strong&gt; when you need to &lt;strong&gt;transform or process&lt;/strong&gt; the response using RxJS operators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a utility function&lt;/strong&gt; that allows us to &lt;strong&gt;bridge the gap&lt;/strong&gt;, applying RxJS operators to Signals before passing them to &lt;code&gt;httpResource()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Let’s explore Approach #2 in detail&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  🔄 From Signal to Observable and Back to Signal
&lt;/h2&gt;

&lt;p&gt;Angular provides &lt;strong&gt;interoperability functions&lt;/strong&gt; that help bridge the gap between &lt;strong&gt;Signals&lt;/strong&gt; and &lt;strong&gt;Observables&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;toObservable()&lt;/code&gt;&lt;/strong&gt; → Converts a &lt;code&gt;Signal&lt;/code&gt; into an &lt;code&gt;Observable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;toSignal()&lt;/code&gt;&lt;/strong&gt; → Converts an &lt;code&gt;Observable&lt;/code&gt; back into a &lt;code&gt;Signal&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging these utilities, we can &lt;strong&gt;apply RxJS operators&lt;/strong&gt; like &lt;code&gt;debounceTime()&lt;/code&gt; and &lt;code&gt;distinctUntilChanged()&lt;/code&gt; while keeping &lt;code&gt;httpResource()&lt;/code&gt; reactive.&lt;/p&gt;
&lt;h2&gt;
  
  
  🔗 Applying RxJS Operators to Signals
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toSignal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core/rxjs-interop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;distinctUntilChanged&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;inputQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;inputQuery$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputQuery&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;distinctUntilChanged&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
  &lt;span class="nf"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputQuery$&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;httpUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, we use a &lt;strong&gt;Signal&lt;/strong&gt; to manage the input event and trigger API requests while leveraging an &lt;strong&gt;Observable&lt;/strong&gt; to handle the stream flow efficiently.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note:&lt;br&gt;
This example works as expected, but since we are delaying the event and not the actual API request, the isLoading state could not reflect the truth.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  🛠 Simplifying with a Utility Function
&lt;/h2&gt;

&lt;p&gt;To avoid writing this logic repeatedly, we can create a &lt;strong&gt;utility function&lt;/strong&gt; that applies &lt;strong&gt;debouncing and distinct filtering&lt;/strong&gt; to any &lt;code&gt;Signal&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toSignal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core/rxjs-interop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;distinctUntilChanged&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debounceTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;debounceDistinctSignal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obs$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;distinctUntilChanged&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obs$&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, we can &lt;strong&gt;simplify our original implementation&lt;/strong&gt; like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;debounceDistinctSignal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signal-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;debounceDistinctSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;httpUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  🎯 Try it in Action! 🚀
&lt;/h2&gt;

&lt;p&gt;Want to see &lt;code&gt;httpResource()&lt;/code&gt; in action? Check out this &lt;strong&gt;interactive StackBlitz demo&lt;/strong&gt;, where you can:&lt;/p&gt;

&lt;p&gt;✅ Experiment with &lt;code&gt;httpResource()&lt;/code&gt; for API calls&lt;br&gt;
✅ Test a &lt;strong&gt;search bar with debounce &amp;amp; distinctUntilChanged&lt;/strong&gt;&lt;br&gt;
✅ Compare side-by-side with &lt;code&gt;rxResource()&lt;/code&gt; and &lt;code&gt;resource()&lt;/code&gt;&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://stackblitz.com/~/github.com/giorgiogalassi/resource?file=src/app/pages/http-resources.ts" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;stackblitz.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  📖 Summary &amp;amp; Next Steps
&lt;/h2&gt;

&lt;p&gt;Here’s what we’ve learned in this article:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;httpResource()&lt;/code&gt; simplifies API calls&lt;/strong&gt; by integrating directly with &lt;code&gt;HttpClient&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;&lt;code&gt;toObservable()&lt;/code&gt; and &lt;code&gt;toSignal()&lt;/code&gt; provide interoperability&lt;/strong&gt; between Signals and RxJS.&lt;br&gt;
&lt;strong&gt;A custom utility function (&lt;code&gt;debounceDistinctSignal()&lt;/code&gt;) helps clean up our code&lt;/strong&gt; and makes this approach reusable.&lt;/p&gt;




&lt;p&gt;Thank you for staying with me, and I hope everything was clear. Feel free to explore more of my articles and &lt;strong&gt;&lt;a href="https://www.linkedin.com/in/giorgiogalassi/" rel="noopener noreferrer"&gt;follow me on LinkedIn!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;See you in the next one!&lt;/p&gt;

&lt;p&gt;Best, G.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>httpresource</category>
      <category>http</category>
    </item>
    <item>
      <title>Angular v19+ — Understanding the New resource() and rxResource() APIs 🔥🚀</title>
      <dc:creator>Giorgio Galassi</dc:creator>
      <pubDate>Thu, 20 Mar 2025 10:49:13 +0000</pubDate>
      <link>https://dev.to/ggalassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-3393</link>
      <guid>https://dev.to/ggalassi/angular-v19-understanding-the-new-resource-and-rxresource-apis-3393</guid>
      <description>&lt;p&gt;&lt;strong&gt;Angular 19 is here, and its renaissance continues, further embracing the power of signals.&lt;/strong&gt;&lt;br&gt;
This release introduces two new primitives that simplify handling asynchronous requests: &lt;strong&gt;&lt;code&gt;resource()&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;rxResource()&lt;/code&gt;&lt;/strong&gt;. These APIs enable a cleaner and more intuitive way to manage asynchronous data in Angular applications.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;resource()&lt;/code&gt; and &lt;code&gt;rxResource()&lt;/code&gt; provide powerful tools for handling API calls reactively, &lt;strong&gt;Angular 19.2&lt;/strong&gt; introduces yet another step forward: &lt;strong&gt;&lt;code&gt;httpResource()&lt;/code&gt;&lt;/strong&gt;, a new API that integrates seamlessly with &lt;code&gt;HttpClient&lt;/code&gt;, eliminating even more boilerplate.&lt;br&gt;
If you're interested in taking reactive API calls to the next level, don't miss our deep dive into &lt;strong&gt;&lt;code&gt;httpResource()&lt;/code&gt;&lt;/strong&gt; and how it compares with the existing solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out the new article on httpResource() 👇🏻&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-httpresource-api-23h" class="crayons-story__hidden-navigation-link"&gt;Angular v19+ — Understanding the New httpResource() API 🔥🚀&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ggalassi" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1717803%2F0624b9d4-7bae-4e4e-a1f1-ae7a5eb7f02b.png" alt="ggalassi profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ggalassi" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Giorgio Galassi
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Giorgio Galassi
                
              
              &lt;div id="story-author-preview-content-2345296" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ggalassi" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1717803%2F0624b9d4-7bae-4e4e-a1f1-ae7a5eb7f02b.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Giorgio Galassi&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-httpresource-api-23h" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 20 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-httpresource-api-23h" id="article-link-2345296"&gt;
          Angular v19+ — Understanding the New httpResource() API 🔥🚀
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/angular"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;angular&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/httpresource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;httpresource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/http"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;http&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/ggalassi/angular-v19-understanding-the-new-httpresource-api-23h#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: These APIs are still in experimental state!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Signals: A Quick Refresher
&lt;/h2&gt;

&lt;p&gt;Signals represent a new paradigm for Angular application development. Paired with a &lt;strong&gt;&lt;a href="https://angular.dev/guide/experimental/zoneless" rel="noopener noreferrer"&gt;Zoneless&lt;/a&gt;&lt;/strong&gt; approach, they not only enhance application performance but also improve the Developer Experience (DX).&lt;/p&gt;

&lt;p&gt;By using a signal in your view, you can precisely inform Angular which node in the component tree needs to be updated. This fine-grained reactivity results in faster updates and a more efficient rendering pipeline.&lt;/p&gt;

&lt;p&gt;Here’s a quick example demonstrating how to create and manipulate a signal in Angular:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-component&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;p&amp;gt;{{ counter() }}&amp;lt;/p&amp;gt;

    &amp;lt;button (click)="add()"&amp;gt;Add&amp;lt;/button&amp;gt;
    &amp;lt;button (click)="remove()"&amp;gt;Remove&amp;lt;/button&amp;gt;
    &amp;lt;button (click)="reset()"&amp;gt;Reset&amp;lt;/button&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Which Problem Are They Solving?
&lt;/h2&gt;

&lt;p&gt;In the early days of Angular signals, several new patterns emerged. One of these patterns tackled how to combine signals with the &lt;code&gt;HttpClient&lt;/code&gt; to manage API responses. A common implementation might look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asReadonly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;doApiCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;While functional, this approach feels clunky and verbose. It introduces unnecessary boilerplate and requires developers to manually manage loading and error states. Moreover, mixing signals with observables can lead to code that is harder to maintain and reason about, reducing clarity and increasing cognitive overhead.&lt;/p&gt;
&lt;h2&gt;
  
  
  resource(): Simplifying Asynchronous API Calls
&lt;/h2&gt;

&lt;p&gt;The new &lt;a href="https://angular.dev/guide/signals/resource" rel="noopener noreferrer"&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/a&gt; primitive in Angular provides an elegant way to manage asynchronous API calls in a signal-based application. By leveraging &lt;code&gt;resource&lt;/code&gt;, you can seamlessly integrate reactive API calls into your app with better state handling and cleaner code.&lt;/p&gt;

&lt;p&gt;Here’s an example of how &lt;code&gt;resource()&lt;/code&gt; can be used:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-component&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;input (input)="search($event)" placeholder="Search user..."/&amp;gt;

    &amp;lt;br /&amp;gt;
    &amp;lt;ul&amp;gt;
      @let error = users.error();
      @if (error) {
        &amp;lt;p&amp;gt;{{ error }}&amp;lt;/p&amp;gt;
      }

      @if (users.isLoading()) {
        &amp;lt;p&amp;gt;Loading Users...&amp;lt;/p&amp;gt;
      }

      @for (user of users.value(); track user.id) {
        &amp;lt;li&amp;gt;{{ user.firstName }} {{ user.lastName }}&amp;lt;/li&amp;gt;
      } @empty {
        &amp;lt;p&amp;gt;No Users!&amp;lt;/p&amp;gt;
      }
    &amp;lt;/ul&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// The resource can be typed as follows:&lt;/span&gt;
  &lt;span class="c1"&gt;// resource&amp;lt;response type, request type&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;abortSignal&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;abortSignal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unable to load users!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Diving Deeper into the resource() API
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;resource()&lt;/code&gt; API introduces two essential components to handle asynchronous data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;request&lt;/code&gt;&lt;/strong&gt;: A function that returns a value used for the loader computation. Every time &lt;code&gt;query()&lt;/code&gt; changes, the &lt;code&gt;loader&lt;/code&gt; function is re-triggered, behaving similarly to a computed signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;loader&lt;/code&gt;&lt;/strong&gt;: The function where the actual API call is performed. It must return a promise, and errors can be handled like any standard promise.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Built-In Properties of resource()
&lt;/h2&gt;

&lt;p&gt;The resource primitive comes with several useful out-of-the-box properties, all of which are signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;value()&lt;/code&gt;&lt;/strong&gt;: Retrieves the current value of the resource's response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;isLoading()&lt;/code&gt;&lt;/strong&gt;: Indicates whether the resource is currently loading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;error()&lt;/code&gt;&lt;/strong&gt;: Contains the error, if any, encountered during the API call.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What About rxResource()?
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;resource()&lt;/code&gt; uses a promise-based loader, &lt;code&gt;rxResource()&lt;/code&gt; provides a similar abstraction but leverages Observable streams instead. This makes it a perfect fit for scenarios where your application is already heavily reliant on RxJS or where Observables are the preferred choice for handling asynchronous data.&lt;/p&gt;

&lt;p&gt;To illustrate this, let’s look at an example of how rxResource() can be used to perform a backend call while applying RxJS operators like distinctUntilChanged and map:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rxUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rxResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;distinctUntilChanged&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to load!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Practical Example and Hands-On Exploration
&lt;/h2&gt;

&lt;p&gt;The power of &lt;code&gt;resource()&lt;/code&gt; and &lt;code&gt;rxResource()&lt;/code&gt; becomes evident when managing complex asynchronous workflows in your Angular application. By combining signals and these primitives, you can reduce boilerplate code while maintaining clarity and reactivity.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamically update UI components based on isLoading() or error() states.&lt;/li&gt;
&lt;li&gt;Create powerful search interfaces, as shown in the code above, that adapt in real-time as the user interacts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To see these concepts in action, check out this interactive StackBlitz example:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://stackblitz.com/~/github.com/giorgiogalassi/resource?file=src/app/pages/resources.ts" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;stackblitz.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;In this demo, you’ll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fully functional search bar integrated with the resource() API.&lt;/li&gt;
&lt;li&gt;Examples of handling loading states, error handling, and real-time updates using signals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to experiment by tweaking the code and observing how Angular’s reactivity makes the process seamless.&lt;/p&gt;




&lt;p&gt;Thank you for staying with me, and I hope everything was clear. Feel free to explore more of my articles and &lt;a href="https://www.linkedin.com/in/giorgiogalassi" rel="noopener noreferrer"&gt;&lt;strong&gt;follow me on LinkedIn!&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you in the next one!&lt;/p&gt;

&lt;p&gt;Best, G.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>resource</category>
      <category>http</category>
    </item>
  </channel>
</rss>
