<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by 🪄 OZ 🎩 on Medium]]></title>
        <description><![CDATA[Stories by 🪄 OZ 🎩 on Medium]]></description>
        <link>https://medium.com/@eugeniyoz?source=rss-ec188195efcc------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*2GPqA1VmorPWlZuF-tOlQA.jpeg</url>
            <title>Stories by 🪄 OZ 🎩 on Medium</title>
            <link>https://medium.com/@eugeniyoz?source=rss-ec188195efcc------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 15 Jun 2026 16:16:51 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@eugeniyoz/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Reactivity in Angular]]></title>
            <link>https://medium.com/@eugeniyoz/reactivity-in-angular-844444741c7e?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/844444741c7e</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[reactivity]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Sun, 15 Jun 2025 12:59:46 GMT</pubDate>
            <atom:updated>2025-12-20T09:41:44.361Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6LTv-6EYv_Z8Klu_EvcSpg.jpeg" /><figcaption>“The Great Wave off Kanagawa”, Katsushika Hokusai, 1831</figcaption></figure><p>This article explains why we should build our Angular apps with reactivity in mind and how immutability helps with it.</p><h3>The Magic</h3><p>Look at this example:</p><pre>@Component({<br>  template: `<br>    &lt;div&gt;Name: {{user.name}}&lt;/div&gt;<br>    &lt;div&gt;Age: {{user.age}}&lt;/div&gt;<br><br>     &lt;button (click)=&quot;updateName()&quot;&gt;Set Random Name&lt;/button&gt;<br>     &lt;button (click)=&quot;updateAge()&quot;&gt;Set Random Age&lt;/button&gt;<br>  `,<br>})<br>export class App {<br>  user = {<br>    name: &#39;Alice&#39;,<br>    age: 25,<br>  }<br><br>  updateName() {<br>    const i = Math.floor(Math.random() * names.length);<br>    this.user.name = names[i];<br>  }<br><br>  updateAge() {<br>    this.user.age = Math.floor(Math.random() * 20 + 20);<br>  }<br>}</pre><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-edq9u6rq%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26hideNavigation%3D1%26theme%3Dlight&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-edq9u6rq%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26hideNavigation%3D1%26theme%3Dlight&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-edq9u6rq%2Fa487860f9ff937f80dd987710109bf13&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/feab2a931a3948782e5a270cf8ecb53c/href">https://medium.com/media/feab2a931a3948782e5a270cf8ecb53c/href</a></iframe><p>It works, but it only works because of magic. Dark magic. I wish it didn’t work :) What’s much worse is that code like this is scattered across Angular tutorials for beginners, so it shouldn’t be surprising that many Angular apps have similar code, and that many readers right now are asking, “What the hell is wrong with this code, dude? It just works. Chill, man.”</p><h4>What works</h4><p>In our template, we have bindings:</p><pre>&lt;div&gt;Name: {{user.name}}&lt;/div&gt;<br>&lt;div&gt;Age: {{user.age}}&lt;/div&gt;</pre><p>When user.name changes, Angular reflects this change and updates the HTML element. That seems quite reasonable and logical.</p><p>But how does Angular know that user.name has changed? We didn’t call anything like heyAngularWeChangedTheUserObject() or even checkBindingsPlease(). This is where the dark magic hides — and this magic has its price.</p><h4>What is the price</h4><p>The magic tricks here are “dirty checking” and “monkey patching”.</p><p>They hurt performance and can cause data pollution (undesired mutations) in non-trivial apps. But they work just fine in small apps, especially in the simple examples provided in tutorials.</p><p>To cast the “dirty checking” spell, Angular has to check every binding (and every “input”) in every component of your application. Some of these components are used in “for” loops, some are reused multiple times, so the number of rendered components might be higher than the total number of components your app has.</p><p>And Angular doesn’t know <strong><em>when</em></strong> your bindings might change. So Angular reacts to every change of every [input] and (output) and DOM event listeners like (click). And still, it is not enough.</p><p>Data in your app might change asynchronously. Not right after the (output) has emitted (for example), but a little bit later, after loading some data, for example. And Angular will run its dirty checking too early to notice the changes.</p><p>For that, Angular uses another spell: “monkey patching.”</p><p>A special tool, Zone.js, wraps asynchronous APIs like setTimeout, requestAnimationFrame, Promise, MutationObserver, and events like click, change, mousemove, and many others [1]. So when they are called, Zone.js notifies Angular — and that’s how it knows when to cast the “dirty checking” spell.</p><h4>Did you just say mousemove?</h4><p>Yes. And scroll is also patched. Now you can imagine how many times Angular has to check every binding in your components.</p><p>The existence of this dark magic led to a special rule:</p><blockquote><strong><em>Thou shalt not call functions in your templates! ☝️</em></strong></blockquote><p>Even beginners know this rule, because it’s practically a commandment in many tutorials.</p><p>The reason: comparing two variables by reference is a very <em>cheap</em> operation in terms of performance. But if a binding involves a function, that function will be called, and computing its value might not be so cheap.</p><p>This rule is nothing but an adaptation to the shortcomings of the dark magic we just explored.</p><h4>Is the OnPush Change Detection Strategy Enough?</h4><p>There are two change detection strategies in Angular that components can use, and one of them, OnPush, instructs the framework to check not every component in the app, but only the modified component and its ancestors.</p><p>But just changing the strategy in an existing component is not enough. First, every binding should be reactive — otherwise, we’ll do more harm than good.</p><p>Let’s modify our example a bit:</p><pre>@Component({<br>  changeDetection: ChangeDetectionStrategy.OnPush,<br>// ...<br><br>  updateAge() {<br>    // Any asynchronous code.<br>    // Imagine we are loading the allowed ages list.<br>    setTimeout(() =&gt; {<br>      this.user.age = Math.floor(Math.random() * 70 + 20);<br>    }, 1);<br>  }<br>}</pre><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-qzs61s3o%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26hideNavigation%3D1%26theme%3Dlight&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-qzs61s3o%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26hideNavigation%3D1%26theme%3Dlight&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-qzs61s3o%2F525b521b5d0c7851ce9b5be4c5eb5dc2&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/c3cceb126ef82614e51f1312c4e1f559/href">https://medium.com/media/c3cceb126ef82614e51f1312c4e1f559/href</a></iframe><p>Now, if you click “Set Age” first, you will see no changes. But if you click “Set Name” afterwards, you’ll see both the name and the age updated.</p><p>Explanation of the bug:</p><ol><li>When we click the button, the event handler not only calls updateAge(), but also marks our component as “dirty” and schedules a Change Detection cycle;</li><li>Change Detection runs, but can’t see any changes because they will be applied later (asynchronously). It removes the “dirty” mark from our component;</li><li>setTimeout() is patched by Zone.js, so after 1ms, it schedules a Change Detection cycle;</li><li>Change Detection runs, but our component has the OnPush strategy and is not marked as “dirty” (see step <em>2</em>), so the component is skipped;</li><li>When we click another button, the event handler calls updateName(), marks our component as dirty, and schedules a Change Detection cycle;</li><li>Change Detection runs and sees changes in both of our bindings: user.name, which we just changed, and user.age, which we changed previously. Both changes are then reflected.</li></ol><p>This is an example of why we should not use OnPush with non-reactive bindings.<br>We should only use OnPush when every binding in our template is reactive.</p><p>As you can see from the steps <em>3</em> and <em>4</em>, Zone.js can notify the framework about <strong><em>when</em></strong>, but not <strong><em>where</em></strong> a change detection is required.</p><p>Angular is moving away from black magic. In the Zone.js repo, you can find [2]:</p><blockquote>As Angular moves towards a zoneless application development model, Zone.js is no longer accepting new features, including additional patches for native platform APIs. The team will also not be accepting any low priority bug fixes. Any critical bug fixes that relate to Angular’s direct use of Zone.js will still be accepted.</blockquote><p>And this is the direction we all should move as well — towards…</p><h3>Pure Reactivity</h3><p>If we notify the framework that some parts of our templates have changed, then the framework will not need all that dark magic and can:</p><ul><li>Check only the modified parts;</li><li>Only when needed;</li><li>Optimize DOM updates even with asynchronous changes;</li><li>Stop using monkey patching.</li></ul><p>It might sound too good to be true, but we can do this already — starting with Angular version 20! We’ll need 3 things:</p><ul><li>bindings in our templates should use only Signals;</li><li>every component should use OnPush change detection strategy;</li><li>provideZonelessChangeDetection() in bootstrapApplication() providers.</li></ul><p>If, for some reason, you can’t use Angular v20 yet, or some old libraries are not compatible with zoneless change detection — don’t worry. If you can use Signals for bindings (or at least the async pipe with Observables), and all your components use the OnPush change detection strategy, the performance of your app will be quite close to that of a zoneless app.</p><p>An additional benefit of a zoneless app: it <strong><em>forces</em></strong> your code not to rely on Zone.js and to be purely reactive.</p><h4>Reactive bindings</h4><p>The point of reactive bindings is to notify Angular about the changes (when and where they happened), so Angular can react (pun intended) and reflect the changes with minimal performance costs.</p><p>Let’s fix our bug by replacing our non-reactive bindings with reactive ones:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-gmo6eqde%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26hideNavigation%3D1%26theme%3Dlight&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-gmo6eqde%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26hideNavigation%3D1%26theme%3Dlight&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-gmo6eqde%2Ffcb7f1d1430f4aa4d66e98302729b83c&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/b42ac25ae290e3115e028a746dc3f033/href">https://medium.com/media/b42ac25ae290e3115e028a746dc3f033/href</a></iframe><p>Let’s analyze our code:</p><pre>@Component({<br>  selector: &#39;app-root&#39;,<br>  changeDetection: ChangeDetectionStrategy.OnPush,<br>  template: `<br>    &lt;div&gt;Name: {{name()}}&lt;/div&gt;<br>    &lt;div&gt;Age: {{age()}}&lt;/div&gt;<br>    ...<br>  `,<br>})<br>export class App {<br>  user = signal&lt;User&gt;({<br>    name: &#39;Alice&#39;,<br>    age: 25,<br>  });<br><br>  name = computed(() =&gt; this.user().name);<br>  age = computed(() =&gt; this.user().age);<br><br>  updateName() {<br>    const i = Math.floor(Math.random() * names.length);<br>    this.user.update((user) =&gt; ({<br>      ...user,<br>      name: names[i],<br>    }));<br>  }<br><br>  updateAge() {<br>    setTimeout(() =&gt; {<br>      this.user.update((user) =&gt; ({<br>        ...user,<br>        age: Math.floor(Math.random() * 70 + 20),<br>      }));<br>    }, 1);<br>  }<br>}</pre><p>Now user is a signal, we have two derived (computed) signals: name and age, and our updateName() and updateAge() methods have been modified a little.</p><p>Every time we modify the user signal, it sends notifications to name and age, and they send notifications to the template. <br>When this happens, the template marks our component as “dirty” and schedules a Change Detection cycle.</p><p>Without any magic, we let Angular know where and when changes happened, so Angular can granularly update only the needed parts — and only when it’s needed.</p><blockquote><em>But wait, we are calling functions in our template! 😱</em></blockquote><p>Yes, and it’s ok. Signals are functions, but their call is super cheap because they don’t compute their value on every call. They memoize their value, so calling them doesn’t hurt performance. <br>In fact, any function that memoizes its value can be safely called from the template [3]. Signals just guarantee this out of the box.</p><h4>Immutability</h4><p>In the updateName() and updateAge() methods, we use the update() method of an Angular Signal and create a shallow copy of the existing object. Why not just mutate the fields of the existing object?</p><p>Because when we update an Angular Signal using set() or update(), Angular compares the new value with the old one. A notification will be sent only if they are not equal. And they are compared by reference (===).</p><p>Two references to the same object are always considered equal — and that’s why we create a shallow copy: to trigger the value change notification.</p><p>You can verify this behavior in the following new methods:</p><pre>mutateAge() {<br>  const user = this.user();<br>  user.age = Math.floor(Math.random() * 70 + 20);<br>}<br><br>mutateAgeAndSet() {<br>  const user = this.user();<br>  user.age = Math.floor(Math.random() * 70 + 20);<br>  this.user.set(user);<br>}</pre><p>In mutateAge(), we mutate the field of a signal value. As you can check, this will not work.</p><p>In mutateAgeAndSet(), we mutate the field and call the signal’s set() method. It will compare two user objects, they are equal by reference, so this will not trigger any changes either.</p><p>You can read more about Angular Signals reactivity and mutations in my other article [4].</p><p>There is an easy trick to protect ourselves from this mistake:</p><pre>export type User = {<br>  readonly name: string;<br>  readonly age: number;<br>};</pre><p>There are more advanced tools to provide immutability, but this one doesn’t require any libraries to use.</p><p>Immutability is not only helpful for reactivity. It has other benefits: your data will not be polluted (modified by code that has no clue it’s modifying shared data), it protects from unexpected side effects, and it makes testing easier.</p><p>It is possible to override the default equality check function in Angular Signals (in most of their kinds), but you should only do this in exceptional cases, when providing the new values in an immutable way is more expensive than checking equality manually. <br>Checking equality in nested objects recursively can be quite expensive in terms of performance and might require special handling if the objects have circular references.</p><h4>Performance</h4><p>Let’s explore why the <em>Signals in Templates</em> + <em>OnPush</em> + <em>Zoneless</em> formula provides the best possible performance for your Angular app.</p><p>A template is a consumer of signals and behaves similarly to effect(), but it also schedules a Change Detection cycle when a signal is updated.</p><p>Unlike the async pipe [9], the template will not be triggered on every update of the signal. As we learned above, only non-equal values will trigger an update. <br>Also, if a signal is updated 100 times synchronously (each time with a new value), the template will schedule just one Change Detection cycle.</p><p>Here is the difference in how Observables and Signals deliver their update notifications:</p><ul><li>Observables emit every time we ask them to. They don’t check if the next value is equal to the previous one. <br>When an Observable emits a value, the subscriber must handle it, because it can’t predict whether there will be another. <br>We could use debounceTime(0), but that would introduce unnecessary asynchrony when a synchronous update would be sufficient;</li><li>Consumers of signals receive only a notification about the update, not the new value. To get the new value, they must read the updated signal — and they are free to do this whenever they want. In the case of computed signals, this means that the new value will be recomputed not at the moment when the signals used in the computation are updated, but when the consumer reads the value of that computed signal. This can eliminate a lot of unnecessary computations, because consumers (effect(), templates) schedule the reads of the signals they are subscribed to, so they don’t read more often than needed. You can read more about signals timings in the article linked below [6].</li></ul><p>When the OnPush strategy is combined with signals (your template uses only signals for reactivity and all components use the OnPush strategy), Angular applies an optimization called “<strong>Local Change Detection</strong>” [5]. It doesn’t mark every ancestor as “dirty”; instead, it marks only the component as “dirty” and marks each ancestor “for traversal.” <br>This means that non-dirty ancestors will not be checked.</p><p>And when every binding (that can change) in our templates uses a signal, we simply don’t need Zone.js and its performance overhead — Angular already knows where and when changes happened.</p><p><em>Combined with the “Single Source of Truth” and “Template First” approaches ([7], [8]), this article will help you create super-performant, declarative, and scalable code. </em><strong><em>To the stars! 🚀</em></strong></p><p><a href="https://buymeacoffee.com/evgeniyoz?source=user_about----------------------ec188195efcc----------------------">Buy Me a Coffee ☕️</a></p><h4>Links:</h4><ol><li>“<a href="https://github.com/angular/angular/blob/main/packages/zone.js/STANDARD-APIS.md">Zone.js’s support for standard apis</a>” (<a href="https://github.com/angular/angular/blob/2e3925a65ce4280f8b31c4f8184666880d03acae/packages/zone.js/STANDARD-APIS.md">permalink</a>).</li><li>“<a href="https://github.com/angular/angular/blob/main/packages/zone.js/README.md#development-status-of-zonejs">Development Status of Zone.js</a>” (<a href="https://github.com/angular/angular/blob/2e3925a65ce4280f8b31c4f8184666880d03acae/packages/zone.js/README.md#development-status-of-zonejs">permalink</a>).</li><li>“<a href="https://dev.to/this-is-angular/its-ok-to-use-function-calls-in-angular-templates-4029">It’s ok to use function calls in Angular templates!</a>” by Enea Jahollari.</li><li>“<a href="https://medium.com/@eugeniyoz/angular-signals-keeping-the-reactivity-train-c22511e72f5e">Angular Signals: Keeping the Reactivity Train</a>”.</li><li>“<a href="https://itnext.io/onpush-and-signals-local-change-detection-in-angular-17-c70e830fd209">Local Change Detection in Angular 17</a>” by Ilir Beqiri.</li><li>“<a href="https://medium.com/@eugeniyoz/angular-signals-timing-a875659c5a1a">Angular Signals — Timing</a>”.</li><li>“<a href="https://medium.com/@eugeniyoz/angular-inputs-and-single-source-of-truth-75939491f701">Angular Inputs and Single Source of Truth</a>”.</li><li>“<a href="https://medium.com/@eugeniyoz/creating-angular-components-template-first-declarative-approach-00c4a4791270">Creating Angular Components: Template-First Declarative Approach</a>”.</li><li><a href="https://stackblitz.com/edit/stackblitz-starters-9hrznumk?file=src%2Fmain.ts">StackBlitz, example with async pipe</a>.</li><li><a href="https://stackblitz.com/edit/stackblitz-starters-edq9u6rq?file=src%2Fmain.ts">StackBlitz, example 1</a>.</li><li><a href="https://stackblitz.com/edit/stackblitz-starters-qzs61s3o?file=src%2Fmain.ts">StackBlitz, example 2</a>.</li><li><a href="https://stackblitz.com/edit/stackblitz-starters-gmo6eqde?file=src%2Fmain.ts">StackBlitz, example 3</a>.</li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=844444741c7e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Examples of linkedSignal() usage in Angular applications]]></title>
            <link>https://medium.com/@eugeniyoz/examples-of-linkedsignal-usage-in-angular-applications-415fcd5e243a?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/415fcd5e243a</guid>
            <category><![CDATA[reactivity]]></category>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Fri, 06 Jun 2025 09:31:12 GMT</pubDate>
            <atom:updated>2025-12-10T12:45:10.656Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4pEbOsB4aXYzC15oW2lSNg.jpeg" /><figcaption>“Cicada”, Omoda Seiju, 1930</figcaption></figure><p>Angular 20 has been released, linkedSignal() is now stable, and I want to share my experience using this powerful function.</p><h4>The main use case</h4><p>linkedSignal() is a perfect tool when you want to compute some initial value, then let the user modify it, and still keep it in a reactive signal.</p><pre>export class CategoriesComponent {<br>  categories = input.required&lt;Category[]&gt;();<br><br>  selectedCategory = linkedSignal(() =&gt; this.categories()[0]);<br><br>  pickCategory(cat: Category) {<br>    this.selectedCategory.set(cat);<br>  }<br>}</pre><p>How it works: the initial value of the selectedCategory signal will be computed as this.categories()[0] .</p><p>this.categories is a signal, and every time this signal changes, selectedCategory will be re-computed.</p><blockquote>Technically, it is a bit more complicated: linkedSignal() will not re-compute its value at the moment when any of the signals it consumes notifies about a possible change, but rather when a consumer of the linkedSignal() reads it and the linkedSignal() has unchecked notifications about changes. However, stating this every time would make it much more difficult to read.</blockquote><p>Those who are familiar with computed() might say that linkedSignal() works the same, but there are two important differences between them.</p><p>Look at the pickCategory() method: Here we set the new value for the selectedCategory signal, and this value will remain until the categories signal “emits” a new value — then selectedCategory will be recomputed again, and the value we previously set in that method will be overridden.</p><p>It is very useful for:</p><ul><li>displaying pre-computed forms (using previously saved data or default values) and letting the user change the values (to save them later, or just to affect other parts of the app);</li><li>pages where the initial properties should be loaded from the API or default values, not the URL, and the URL cannot be the source of truth (large tables with filters and pagination, games, 3D scenes), and the user is supposed to modify the values;</li><li>components where one input control might restrict the range of allowed values of other input controls. For example, a date picker where the user picks the year, month, and date separately;</li><li>computed (derived) signals that should know the previously computed value.</li></ul><p>And that is the second important difference: linkedSignal() not only returns a WritableSignal, but also lets us use the previous value.</p><p>Example:</p><pre>export class VehiclesComponent {<br>  showAtv = linkedSignal&lt;boolean, boolean&gt;({<br>    source: () =&gt; this.store.hasATVInfo(),<br>    computation: (source, previous) =&gt; {<br>      if (source === false) {<br>        /**<br>         * If other fields of the form were changed and<br>         * we no longer have ATV vehicle info,<br>         * but we are displaying it as &quot;yes,&quot;<br>         * leave it as &quot;yes&quot; - do not reset this switch.<br>         * Otherwise, the opened form will be unexpectedly<br>         * (for the user) closed, and the user might still<br>         * want to edit this form, bringing in the new ATV info.<br>         */<br>        if (previous?.value === true) {<br>          return true;<br>        }<br>      }<br>      return source;<br>    }<br>  });<br>}</pre><p>Sometimes you don’t need a WritableSignal, and you would like to use computed(), but you absolutely need the previous value to compute the new one, even if computed() is supposed to be pure. It’s not an idiomatic example, but for these exceptional cases, you can use this trick:</p><pre>export class Example {<br>  someInput = input&lt;string&gt;();<br><br>  myComputedSignal = linkedSignal&lt;string, string&gt;({<br>    source: this.someInput,<br>    computation: (source, previous) =&gt; {<br>      if (previous?.value === &#39;SOME VALUE&#39;) {<br>        return &#39;SOMETHING ELSE&#39;;<br>      }<br>      return source;<br>    }<br>  }).asReadOnly(); // &lt;- and it&#39;s not writable anymore<br>}</pre><h4>With great power…</h4><p>When we have more than one source of truth, things become less declarative and less safe. We should use linkedSignal() with care.</p><p>Look at this example:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-rbowxrum%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-rbowxrum%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-rbowxrum%2Fe9013d4f25e64a2de43db14275d813e1&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/4fd5637d9a92f2085179c0a453aebf34/href">https://medium.com/media/4fd5637d9a92f2085179c0a453aebf34/href</a></iframe><p>If a user doesn’t want to be named “Goblin,” they will enter something else, for example, “Wizard.” Now they see in the form that their favorite color is Green and their nickname is “Wizard.” But their favorite color is purple, they input “Purple,” and now they are named “Purple Goblin.” Not an expected behavior, to say the least!</p><p>In this form, after “nickname” was edited, it should not be derived (computed) anymore.</p><p><em>linkedSignal() is a wonderful tool that can be extremely useful sometimes. Happy coding!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=415fcd5e243a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Starting a Modern Angular Application]]></title>
            <link>https://medium.com/@eugeniyoz/starting-a-modern-angular-application-9cbe409ee610?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/9cbe409ee610</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[typescript]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Mon, 10 Feb 2025 17:24:52 GMT</pubDate>
            <atom:updated>2025-12-10T12:45:34.818Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jDgzChwWXAEnvknvDzTcAw.jpeg" /><figcaption>“Window Opening on Nice”, Raoul Dufy, 1928</figcaption></figure><p>The initial architecture of a web application is fundamentally important — it will affect your app for multiple years, impacting not only development but also the user experience. Things like lazy loading, bundle size, icon reuse, and other small details will influence the user experience, not just CI time, code reuse, or the onboarding process.</p><p>In this article, I’ll review the tools and options we have when creating a new Angular app and which of them, in my opinion, should be chosen.</p><h4>Workspace</h4><p>So the first thing we should create is a “workspace”. We should decide what structure our application will have — where all the components, services, and other things will be located.</p><p>Let’s discuss 3 ways of creating an initial structure:</p><ol><li>Using Angular CLI, without separation by sub-modules/packages/libraries;</li><li>Using Angular CLI to create a monorepo, all the code is in libraries;</li><li>Using Nx to create a monorepo.</li></ol><p>There are other tools to create a monorepo, but that would take another article to describe them all :)</p><p>So let’s compare what pros and cons every option has:</p><blockquote>Angular CLI, without separation by sub-modules/packages/libraries</blockquote><p><strong>Pros:</strong></p><ul><li>Very easy to follow. Developers of any level can create a new component in the correct folder;</li><li>No additional dependencies.</li></ul><p><strong>Cons:</strong></p><ul><li>Very fragile because of tight coupling. Imports use relative paths: import { NavbarComponent } from &#39;../../../shared-components/navbar/navbar.component&#39;, and if something is moved or renamed, many dependent components/services might need an update. A smart IDE can help with this, but not with a 100% guarantee. Moving and renaming things become dangerous;</li><li>Easy to create circular dependencies and ruin lazy-loading. See the example below;</li><li>Lack of modularity. Moving some code to a separate library requires enormous effort in checking dependencies, and often this task remains forever in the backlog, with functionality just being copy-pasted to another app or project;</li><li>test, lint, and build will always handle all the code you have. The more code you have, the longer these commands take, even if only one line in one component was changed;</li><li>Upgrades of tools like linters are not handled by ng update - configs will remain the same, even if a linter or test runner library has a major version upgrade and requires modifications in configs.</li></ul><p>Example of how the lazy-loading can be broken with this structure:</p><ul><li>Component A imports Component B</li><li>Component B imports Service S and Component C</li><li>Component D imports Component A and Component B</li><li>When any component imports just a single Component D, every part listed above is imported as well. This happens very often in real apps. Ruining a lazy-loading is especially easy with this structure.</li></ul><blockquote>Angular CLI to create a monorepo, all the code is in libraries</blockquote><p><strong>Pros:</strong></p><ul><li>Modular. Every library can be reused by multiple apps or even moved to its own repo and published to a registry. Then, it can be added to dependencies with 0 changes required in the code of other apps/libraries (because all imports are already non-relative);</li><li>No additional dependencies.</li></ul><p><strong>Cons:</strong></p><ul><li>Every time you create a library you’ll need to manually modify the paths in tsconfig.json, because Angular CLI will generate paths to dist folder, so to get updates from any library, you need to build it first. And because all of the code is libraries, that would be a nightmare in ng serve mode, so you’ll change the path in tsconfig to the public-api.ts file of a library you created</li><li>No module boundaries tracking. Developers should create a document that explains the connections across modules to avoid circular dependencies. This document should be kept up to date and strictly followed every time a developer creates a library. There are tools such as <a href="https://www.npmjs.com/package/madge"><em>madge</em></a> which help with this, but they still should be run manually.</li><li>test, lint, and build will always build every existing library and app. The more code you have, the longer these commands take, even if only one line in one library was changed.</li><li>test, lint, and build can only run consecutively, handling one library/app per run.</li><li>Upgrades of tools like linters are not handled by ng update - configs will remain the same, even if a linter or test runner library has a major version upgrade and requires modifications in configs.</li></ul><blockquote>Using Nx to create a monorepo.</blockquote><p><strong>Pros:</strong></p><ul><li>Modular. Every library can be reused by multiple apps or even moved to its own repo and published to a registry. Then, it can be added to dependencies with 0 changes required in the code of other apps/libraries (because all imports are already non-relative).</li><li>Easy to configure e2e-testing with Playwright (comes out of the box with Nx).</li><li>test, lint, and build will:</li><li>handle libraries in parallel (the more cores your machine has, the sooner all the steps will be completed);</li><li>only run for modified code — if just one library is modified, just that library will be linted and tested;</li><li>use local cache — if code is not changed, results will be reused, even if commands are executed for all libraries (this behavior can be modified with --skipNxCache).</li><li>Nx automatically <a href="https://nx.dev/features/enforce-module-boundaries#project-apis">controls</a> module boundaries.</li><li>nx migrate updates configs of linters and test runners when their version is updated.</li></ul><p><strong>Cons:</strong></p><ul><li>Nx as an additional dependency.</li></ul><p>You can <a href="https://nx.dev/blog/virtuous-cycle-of-workspace-structure">read here</a> how Nx recommends organizing your workspace.</p><p>There is also an excellent article, “<a href="https://nx.dev/blog/managing-ts-packages-in-monorepos">Managing TypeScript Packages in Monorepos</a>”.</p><p>What you choose from these options is up to you. My recommendations:</p><ul><li>Do not pick option 1, even if your app is small and you’re intimidated by the amount of boilerplate that other options bring — you’ll generate it once and forget about it;</li><li>If you decide that the maintenance cost of the Nx dependency is too high compared to the benefits it provides, use option 2, and you can add Nx later. If you go this route, <a href="https://angular.dev/reference/configs/file-structure#multiple-projects">generate a multi-project workspace</a> — switching from a single-project to a multi-project workspace might take more time than expected, but doing it from the start will cost you nothing;</li><li>I recommend option 3, as it significantly improves maintenance (not only of your code but also of your tools) and reduces generated boilerplate.</li></ul><h4>Style</h4><p>First of all, use <a href="https://tailwindcss.com/docs/">Tailwind CSS</a>!</p><p>It is an excellent tool. If your team is more familiar with Bootstrap, it might be difficult to make the switch, but consider this argument: at the time of writing, Tailwind CSS has more than twice the <a href="https://www.npmjs.com/package/tailwindcss">weekly downloads</a> on npm <a href="https://www.npmjs.com/package/bootstrap">than Bootstrap</a>.</p><p>You’ll also need UI components (date picker, tabs, and so on). Here we have a lot of libraries, you can easily find them. Some of them:</p><ul><li><a href="https://primeng.org/">PrimeNG</a>: has a lot of components, can be styled using CSS variables, and has a plugin for Tailwind CSS support;</li><li><a href="https://material.angular.io/">Angular Material</a>: has a lot of components and tools, can be styled using CSS variables (including the variables generated by Tailwind CSS), maintained by the Angular team, and they put a lot of effort into supporting accessibility features;</li><li><a href="https://www.spartan.ng/documentation/installation">spartan/ui</a>: based on TailwindCSS, not so many components, in alpha stage;</li><li><a href="https://daisyui.com/">daysiUI</a>: framework-agnostic, based on Tailwind CSS.</li></ul><p>If you wonder why some libraries you know and love are not on this list, the answer is “for brevity” - enlisting them all would be pointless when we have so many search tools.</p><p>When picking a library, I recommend checking the following:</p><ul><li>If it can be styled using CSS variables;</li><li>If it doesn’t require additional dependencies, that are maintained by someone else;</li><li>If Angular is the only JS part that it requires;</li><li>If it’s popular and actively maintained.</li></ul><p>When the tool you picked asks you what format of CSS files you want, pick CSS — Tailwind works better with CSS, and in modern browsers, CSS can do things that were implemented only in SASS before, like nesting or variables.</p><h4>SSR</h4><p>When the workspace generator asks you if you want SSR, answer “yes” even if you are 100% sure your app will never use Server Side Rendering.</p><p>The code you write should be compatible with SSR anyway. Because when you want to reuse some of the libraries you created for this app, you might be surprised how much effort and how many breaking changes are required to make the existing code compatible with SSR. And if you turn on SSR mode for the serve command, you’ll quickly learn what things to avoid to make code SSR compatible, and it will cost you nothing to write such code from the beginning.</p><p>If your app doesn’t need SSR, you can just set &quot;ssr&quot;: false in the configuration file for the build command and forget about it. But your code will still be reusable in any app.</p><h4>Standalone</h4><p>Modern Angular and Nx will generate standalone components, directives, pipes, and libraries by default, without NgModule. Do not try to change it — this is the direction the framework is moving towards, and creating NgModules at this point is pointless. There are still modules, of course, in the libraries and the framework itself, and you’ll use them, just don’t create new modules.</p><h4>Reactivity</h4><p>You are creating a new app, so it should use the latest features of Angular and be aligned with the trends in Angular evolution. Create a “zoneless” application. At the time of writing this article, it means that you’ll have to add provideExperimentalZonelessChangeDetection() to your list of providers in the application config.</p><p>Don’t be afraid of the “experimental” part — it works very well, even in the most complicated cases, and it might only require some workarounds if the libraries you use have usages of NgZone.onMicrotaskEmpty, NgZone.onUnstable, and NgZone.onStable. Because you are creating a new app, the chances of encountering such cases are low, as Angular is moving toward zoneless architecture, and libraries will support it. Right now, if a library supports the OnPush strategy and doesn’t use the methods of NgZone mentioned above, such a library is fully compatible with “zoneless” Angular apps.</p><p>If your app is zoneless, every component has a change detection strategy OnPush and you use <em>only signals</em> in your template — Angular will benefit you with the best possible performance. And in the future, such apps will get even more performance optimizations in the framework core.</p><h4>State Management</h4><p>I’ve built multiple apps using the approach explained in this article of mine: <a href="https://medium.com/@eugeniyoz/application-state-management-with-angular-signals-b9c8b3a3afd7">Application State Management with Angular Signals</a>.</p><p>If you feel that your app is going to be quite complicated and you don’t think createEffect() and signals will be enough — I would recommend giving a try to <a href="https://ngrx.io/guide/signals/signal-store">SignalStore</a> — some professionals I know and respect use this library and love it.</p><p>That’s it, you definitely will not need more complicated solutions.</p><p>And, of course, I recommend my libraries as helpers:</p><ul><li><a href="https://github.com/e-oz/ngx-collection">Collection</a> for managing the state of lists/collections;</li><li><a href="https://github.com/e-oz/ngx-reactive-storage">Reactive Storage</a> for synchronizing data across tabs/windows using Signals, Observables, or Promises.</li></ul><h4>Testing</h4><p>For e2e, I recommend <a href="https://playwright.dev/">Playwright</a>, and if your team really loves to test things — <a href="https://storybook.js.org/">Storybook</a> (in addition).</p><p>For unit tests, I recommend <a href="https://vitest.dev/">Vitest</a>. If you prefer something else — pick a tool that runs tests in a real browser (headless). That makes a huge difference.</p><h4>Strict TypeScript + Linters</h4><p>Here are options in tsconfig I use as basis and recommend for your project:</p><pre>{<br>  &quot;strict&quot;: true,<br>  // DO NOT remove &#39;emitDecoratorMetadata&#39;! &#39;@typescript-eslint/consistent-type-imports&#39; rule requires it<br>  &quot;emitDecoratorMetadata&quot;: true,<br>  &quot;importHelpers&quot;: true,<br>  &quot;noFallthroughCasesInSwitch&quot;: true,<br>  &quot;noImplicitAny&quot;: true,<br>  &quot;noImplicitThis&quot;: true,<br>  &quot;noImplicitReturns&quot;: true,<br>  &quot;noImplicitOverride&quot;: true,<br>  &quot;noUnusedLocals&quot;: true,<br>  &quot;noUnusedParameters&quot;: true,<br>  &quot;strictNullChecks&quot;: true,<br>  &quot;strictPropertyInitialization&quot;: true,<br>  &quot;useUnknownInCatchVariables&quot;: true,<br>  &quot;noPropertyAccessFromIndexSignature&quot;: true,<br>  &quot;angularCompilerOptions&quot;: {<br>    &quot;strictInjectionParameters&quot;: true,<br>    &quot;strictInputAccessModifiers&quot;: true,<br>    &quot;strictTemplates&quot;: true,<br>    &quot;strictStandalone&quot;: true,<br>    &quot;fullTemplateTypeCheck&quot;: true,<br>    &quot;enableI18nLegacyMessageIdFormat&quot;: false,<br>    &quot;disableTypeScriptVersionCheck&quot;: true,<br>    &quot;extendedDiagnostics&quot;: {<br>      &quot;checks&quot;: {<br>        &quot;optionalChainNotNullable&quot;: &quot;suppress&quot;,<br>        &quot;nullishCoalescingNotNullable&quot;: &quot;suppress&quot;,<br>        &quot;suffixNotSupported&quot;: &quot;warning&quot;,<br>        &quot;textAttributeNotBinding&quot;: &quot;warning&quot;,<br>        &quot;invalidBananaInBox&quot;: &quot;error&quot;,<br>        &quot;missingControlFlowDirective&quot;: &quot;error&quot;,<br>        &quot;missingNgForOfLet&quot;: &quot;error&quot;,<br>        &quot;controlFlowPreventingContentProjection&quot;: &quot;error&quot;,<br>        &quot;unusedStandaloneImports&quot;: &quot;warning&quot;<br>      },<br>      &quot;defaultCategory&quot;: &quot;error&quot;<br>    }<br>  }<br>}</pre><p>That’s not the whole config, just options related to code check.</p><p>And ESLint rules:</p><pre>{<br>    &#39;no-extra-boolean-cast&#39;: &#39;error&#39;,<br>    &#39;no-case-declarations&#39;: &#39;error&#39;,<br>    &#39;@typescript-eslint/consistent-type-exports&#39;: &#39;error&#39;,<br>    &#39;@typescript-eslint/consistent-type-imports&#39;: &#39;error&#39;,<br>    &#39;@typescript-eslint/ban-ts-comment&#39;: [&#39;off&#39;],<br>    &#39;@typescript-eslint/no-explicit-any&#39;: [&#39;error&#39;],<br>    &#39;@typescript-eslint/no-non-null-assertion&#39;: [&#39;error&#39;],<br>    &#39;@typescript-eslint/no-unused-vars&#39;: [<br>      &#39;error&#39;,<br>      {<br>        argsIgnorePattern: &#39;^_&#39;,<br>        varsIgnorePattern: &#39;^_&#39;,<br>        caughtErrorsIgnorePattern: &#39;^_&#39;,<br>      },<br>    ],<br>    &#39;rxjs-angular-x/prefer-takeuntil&#39;: [<br>      &#39;error&#39;,<br>      {<br>        checkComplete: false,<br>        checkDecorators: [&#39;Component&#39;, &#39;Directive&#39;, &#39;Injectable&#39;],<br>        alias: [&#39;takeUntilDestroyed&#39;],<br>        checkDestroy: false,<br>      },<br>    ],<br>    &#39;rxjs/ban-observables&#39;: [&#39;error&#39;],<br>    &#39;rxjs/ban-operators&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-connectable&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-cyclic-action&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-compat&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-ignored-replay-buffer&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-unsafe-catch&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-unsafe-first&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-unsafe-switchmap&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-async-subscribe&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-create&#39;: [&#39;error&#39;], //<br>    &#39;rxjs/no-ignored-observable&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-exposed-subjects&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-nested-subscribe&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-ignored-notifier&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-redundant-notify&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-subject-unsubscribe&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-unbound-methods&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-unsafe-subject-next&#39;: [&#39;error&#39;],<br>    &#39;rxjs/no-unsafe-takeuntil&#39;: [&#39;error&#39;],<br>    eqeqeq: [&#39;error&#39;, &#39;smart&#39;],<br>    &#39;prefer-const&#39;: &#39;error&#39;,<br>    &#39;no-unused-expressions&#39;: &#39;error&#39;,<br>    &#39;@angular-eslint/use-lifecycle-interface&#39;: &#39;error&#39;,<br>    &#39;@angular-eslint/no-input-rename&#39;: &#39;off&#39;,<br>    &#39;@angular-eslint/no-empty-lifecycle-method&#39;: [&#39;error&#39;],<br>  }</pre><p>Plugins for RxJS:</p><ul><li><a href="https://www.npmjs.com/package/@smarttools/eslint-plugin-rxjs">@smarttools/eslint-plugin-rxjs</a></li><li><a href="https://www.npmjs.com/package/eslint-plugin-rxjs-angular-x">eslint-plugin-rxjs-angular-x</a></li></ul><p>As always, linters’ rules are a very opinionated thing, but if you are looking for an initial setup — this will be helpful and will help you avoid a lot of errors.</p><h4>Immutability</h4><p>Make your data structures immutable by default. Don’t expect that your view will be updated because you mutated some field of a large structure — that contradicts how Angular change detection works, so it’s the wrong approach.</p><p>Reactivity in Angular works best with Signals, and Signals compare their values by reference (you can change it, but deep checks are expensive and not always safe). This means that you need to create a new value for a signal every time you want to see any changes in your view.</p><p>In <a href="https://medium.com/@eugeniyoz/mastering-angular-essential-code-organization-principles-c09838dea6e2#19bc">this article</a> of mine, you can read more about how to implement the simplest kind of immutability without any additional tools.</p><h4>Nice Little Things</h4><p>If you haven’t tried it yet, try mat-icon from Angular Material + <a href="https://material.angular.io/components/icon/overview#registering-icons">icons registry</a>. The SVG will be inlined, so you can affect currentColor and set CSS variables, which opens up a lot of possibilities. It is much better than icon fonts — you are not limited by a predefined set of images and can use literally any image you want, even with animations (for some very fancy cases). And they look sharper :)</p><p>Nice source of SVG icons: <a href="https://lucide.dev/icons/">Lucide</a>.</p><p><a href="https://github.com/sindresorhus/type-fest">type-fest</a> is a great tool that will add 0 bytes to your bundle.</p><p>Try <a href="https://www.npmjs.com/package/tailwindcss-safe-area">tailwindcss-safe-area</a> if you care about how your app looks in landscape mode on mobile devices.</p><h4>Things to Avoid</h4><p>Some Angular features might stay here for a while, but will not be actively developed. I recommend avoiding the following:</p><ul><li>Angular Animations — use CSS animations instead. Yes, “leave” animations can’t be replaced, but that’s the only thing;</li><li>Old control flow directives: ngIf, ngSwitch*, and, especially ngFor;</li><li>If you use Angular Material, only style it using CSS variables, do not create CSS rules with their class names in selectors. It might cause very painful breaking changes.</li></ul><p><em>That’s what I use to create super-performant modern Angular apps. I hope this information will be useful for your awesome apps as well!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9cbe409ee610" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Powering Angular with Rust (Wasm)]]></title>
            <link>https://medium.com/@eugeniyoz/powering-angular-with-rust-wasm-0eed1668a51c?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/0eed1668a51c</guid>
            <category><![CDATA[wasm]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[webassembly]]></category>
            <category><![CDATA[rust]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Tue, 11 Jun 2024 09:25:51 GMT</pubDate>
            <atom:updated>2025-12-10T12:45:54.765Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-vFvRegHJgaUUnbi0pKCow.jpeg" /><figcaption>“Red Sunset on the Dnipro”, Arkhip Kuindzhi, 1905</figcaption></figure><p>This article explains how to set up and start using Rust in your Angular application.</p><h4>Why?</h4><p>If the code of your app has parts where you need to work with a large amount of data (especially numbers), <a href="https://developer.mozilla.org/en-US/docs/WebAssembly/Concepts#what_is_webassembly">WebAssembly</a> can do it much faster.</p><p>Examples include drawing, computing dynamic reports, complicated calculations (geodesy, astronomy, physics…), cryptography, LLM, image editing, video processing, games, and so on.</p><p>In this article, we’ll create an Angular workspace with an application and a library, written in Rust. Rust library we will compile into the WebAssembly (Wasm) module, and our Angular app will use this module.</p><h4>Installation</h4><ul><li>Install Rust. Visit <a href="https://www.rust-lang.org/tools/install">https://www.rust-lang.org/tools/install</a> and you’ll find a command to install rustup. I know, an option that says “run some script fetched by curl” might look suspicious, but it is a safe and very convenient way. Currently, the command is:</li></ul><pre>curl --proto &#39;=https&#39; --tlsv1.2 -sSf https://sh.rustup.rs | sh</pre><ul><li>Install wasm-pack:</li></ul><pre>cargo install wasm-pack</pre><h4>Angular Workspace</h4><p>There are multiple ways to create an Angular workspace, and <a href="https://medium.com/@eugeniyoz/angular-for-junior-developers-repository-and-file-structure-f3084c982415">I would recommend Nx</a>, but let’s not increase the complexity of this article and use the required tools only.</p><p>Don’t forget to update your Angular CLI:</p><pre>npm i -g @angular/cli</pre><p>In your terminal, navigate to the folder where your workspace should be created, and run:</p><pre>ng new ng-wasm-example --no-create-application</pre><p>Go to that new folder and generate an app:</p><pre>cd ng-wasm-example<br>ng g application example-app</pre><p>There are multiple ways how to use a Wasm module in our Angular app, and we’ll go with the easiest* one: inside our Angular workspace, we’ll create a regular Angular library, that will be a wrapper for our Rust library. This wrapper will also export types and a function to initialize our Wasm module.</p><pre>ng g library wasm-example</pre><p>*We could create a Rust library directly inside our app, and it would be even easier to compile .wasm file to some assets folder. But then other libraries of our application could not use this Wasm module, so it’s not a scalable and maintainable approach.</p><h4>Rust Library</h4><p>Let’s create a Rust library inside our Angular :</p><pre>cd projects/wasm-example/src/lib<br>cargo new --lib example-rust-lib --vcs none</pre><p>Option --vcs none will prevent the creation of a git repository — we are already inside an Angular workspace repository.</p><p>Now let’s edit Cargo.toml file of our library:</p><pre>[package]<br>name = &quot;example-rust-lib&quot;<br>version = &quot;0.1.0&quot;<br>edition = &quot;2021&quot;<br><br>[lib]<br>crate-type = [&quot;cdylib&quot;]<br><br>[profile.release]<br>lto = true<br>#opt-level = &#39;s&#39;<br><br>[dependencies]<br>wasm-bindgen = &quot;0.2&quot;</pre><p>We’ve added [lib] section to specify crate-type, and added wasm-bindgen dependency.</p><p>In lib.rs replace content with this line:</p><pre>use wasm_bindgen::prelude::*;</pre><p>This line runs a bunch of tools to create a communication bridge between JavaScript and Rust.</p><p>Now you are ready to write your next masterpiece in Rust.</p><p>But that’s an exercise for you, dear reader. This article will go the classic way and calculate something useless, but computationally expensive:</p><pre>use wasm_bindgen::prelude::*;<br><br>pub fn factorial(num: u128) -&gt; u128 {<br>    match num {<br>        0 =&gt; 1,<br>        1 =&gt; 1,<br>        _ =&gt; factorial(num - 1) * num,<br>    }<br>}<br><br><br>#[wasm_bindgen]<br>pub fn get_factorial(num: u8) -&gt; String {<br>    let mut f: u128 = 0;<br>    for _ in 0..10000000 {<br>      f = factorial(num as u128); <br>    }<br>    f.to_string()<br>}</pre><p>The get_factorial function has an instruction above that tells wasm_bindgen to make this function callable from JavaScript.</p><p>Now save your files and create a package:</p><pre>cd example-rust-lib<br>wasm-pack build --target web</pre><p>It will take some time when you run it for the first time.</p><p>The second command (wasm-pack build --target web) we have to run every time we modify our Rust library.</p><p>wasm-pack will generate pkg folder with a few files:</p><pre>📂 pkg<br>├── 📄 .gitignore<br>├── 📄 example_rust_lib.d.ts<br>├── 📄 example_rust_lib.js<br>├── 📄 example_rust_lib_bg.wasm<br>├── 📄 example_rust_lib_bg.wasm.d.ts<br>└── 📄 package.json</pre><p>File example_rust_lib_bg.wasm is our Wasm module!</p><p>Now in our wrapper library, let’s export information about get_factorial.</p><p>In file projects/wasm-example/src/public-api.ts (public API of our wrapper library), let’s replace content with:</p><pre>import init from &#39;./lib/example-rust-lib/pkg&#39;;<br>export { get_factorial } from &#39;./lib/example-rust-lib/pkg&#39;;<br>export { init as initExampleRust} ;</pre><p>Replace the name of the last export (initExampleRust) with something more suitable for your library, but don’t export it just as init, because you might have multiple libraries.</p><p>In the .gitignore file generated by wasm-pack (projects/wasm-example/src/lib/example-rust-lib/pkg/.gitignore), replace * with *.wasm, because other files are quite useful for us (they provide types).</p><p>That’s it, our library is ready to use!</p><h4>Preparing our workspace</h4><p>In tsconfig.json (that is located in our repository root), modify “paths”:</p><pre>    &quot;paths&quot;: {<br>      &quot;wasm-example&quot;: [<br>        &quot;./projects/wasm-example/src/public-api.ts&quot;<br>      ]<br>    }</pre><p>This way we can get code completion in IDE without re-building a library.</p><p>Our app will need the .wasm file that our library generates, let’s declare it in angular.json. <br>Add path to .wasm files, generated by example-rust-lib, into assets option:</p><pre>{<br>  &quot;projects&quot;: {<br>    &quot;example-app&quot;: {<br>      &quot;architect&quot;: {<br>        &quot;build&quot;: {<br>          &quot;options&quot;: {<br>            &quot;assets&quot;: [<br>              {<br>                &quot;glob&quot;: &quot;**/*.wasm&quot;,<br>                &quot;input&quot;: &quot;projects/wasm-example/src/lib/example-rust-lib/pkg&quot;<br>              }<br>...</pre><h4>Calling Rust function in Angular app</h4><p>After all these preparations, we can finally start using our awesome Rust library, our masterpiece!</p><p>Replace the content of projects/example-app/src/app/app.component.html with this:</p><pre>&lt;input max=&quot;22&quot; min=&quot;1&quot; type=&quot;number&quot; placeholder=&quot;Input a number&quot; #inp/&gt;<br><br>&lt;button <br>  type=&quot;button&quot; <br>  (click)=&quot;calculate(inp.value)&quot; <br>  [disabled]=&quot;calculating()&quot;<br>&gt;<br>  Calculate<br>&lt;/button&gt;<br><br>@if (!calculating()) {<br>  @if (jsResult()) {<br>    &lt;div&gt;Result in JS: {{ jsResult() }}, calculated in {{ jsTime() }}&lt;/div&gt;<br>  }<br>  @if (rsResult()) {<br>    &lt;div&gt;Result in Rust: {{ rsResult() }}, calculated in {{ rsTime() }}&lt;/div&gt;<br>  }<br>}</pre><p>For app.component.scss:</p><pre>:host {<br>  display: flex;<br>  flex-flow: column;<br>  gap: 1em;<br><br>  input {<br>    width: 10em;<br>    padding: 0.75em;<br>  }<br><br>  button {<br>    padding: 0.5em;<br>    width: 11.75em;<br>  }<br>}</pre><p>And in app.component.ts we are initializing (loading) our Wasm module and then using it:</p><pre>import { ChangeDetectionStrategy, Component, type OnInit, signal } from &#39;@angular/core&#39;;<br>import { get_factorial, initExampleRust } from &#39;wasm-example&#39;;<br><br>@Component({<br>  selector: &#39;app-root&#39;,<br>  standalone: true,<br>  imports: [],<br>  templateUrl: &#39;./app.component.html&#39;,<br>  styleUrl: &#39;./app.component.scss&#39;,<br>  changeDetection: ChangeDetectionStrategy.OnPush,<br>})<br>export class AppComponent implements OnInit {<br>  jsResult = signal&lt;string&gt;(&#39;&#39;);<br>  rsResult = signal&lt;string&gt;(&#39;&#39;);<br>  jsTime = signal&lt;string&gt;(&#39;&#39;);<br>  rsTime = signal&lt;string&gt;(&#39;&#39;);<br>  calculating = signal&lt;boolean&gt;(false);<br><br>  ngOnInit() {<br>    initExampleRust();<br>  }<br><br>  calculate(inp: number | string) {<br>    this.calculating.set(true);<br><br>    setTimeout(() =&gt; {<br>      const n = typeof inp === &#39;number&#39; ? inp : parseInt(inp, 10);<br>      const jsTimeStart = performance.now();<br>      let f = 0;<br>      for (let i = 0; i &lt; 10000000; i++) {<br>        f = factorial(n);<br>      }<br>      this.jsResult.set(f.toString());<br>      this.jsTime.set(((performance.now() - jsTimeStart) / 1000).toFixed(4) + &#39;s&#39;);<br><br>      const rsTimeStart = performance.now();<br>      this.rsResult.set(get_factorial(n));<br>      this.rsTime.set(((performance.now() - rsTimeStart) / 1000).toFixed(4) + &#39;s&#39;);<br><br>      this.calculating.set(false);<br>    }, 50);<br>  }<br>}<br><br><br>function factorial(x: number): number {<br>  if (x === 0) {<br>    return 1;<br>  } else {<br>    return x * factorial(x - 1);<br>  }<br>}</pre><p>You can notice, that in our Rust code and our TypeScript code, we are calculating the same factorial 1 000 000 times, using the same algorithm without optimizations. That’s because otherwise, we would spend more time on the overhead of calling functions from a Wasm module.</p><p>In your terminal, run ng serve and in the opened app try to input “22”:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/908/1*eK9OlLPYHi7do-FJx4i9_A.png" /><figcaption>And now it works!</figcaption></figure><p>This article only explains how to <em>start</em> using Rust in your Angular apps. The real usage will require much more knowledge, and one article is not enough to explain all the details about wasm-bindgen, WebAssembly modules, their size optimizations, and other things.</p><p>But now you have a starting point — the first step is always the hardest.</p><h4>References</h4><ul><li><a href="https://github.com/e-oz/ng-wasm-example">Source code of the example app</a></li><li><a href="https://developer.mozilla.org/en-US/docs/WebAssembly">WebAssembly MDN</a></li><li><a href="https://rustwasm.github.io/docs/book/reference/crates.html#crates-you-should-know">“Crates You Should Know”</a></li><li><a href="https://rustwasm.github.io/docs/book/reference/code-size.html">“Shrinking .wasm Code Size”</a></li><li><a href="https://rustwasm.github.io/wasm-bindgen/">The `wasm-bindgen` Guide</a></li><li><a href="https://github.com/WebAssembly/design/issues/1231#issuecomment-420466909">Different ways of transferring large data</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0eed1668a51c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Improving Code Reusability in Angular Projects]]></title>
            <link>https://medium.com/@eugeniyoz/improving-code-reusability-in-angular-projects-b169d4a1c786?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/b169d4a1c786</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[code-reusability]]></category>
            <category><![CDATA[code-maintainability]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Fri, 03 May 2024 12:02:28 GMT</pubDate>
            <atom:updated>2025-12-10T12:43:55.177Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YsD5noSIX8ymdECqSf-WFA.jpeg" /><figcaption>“The Pond at Benten Shrine in Shiba”, Hasui Kawase, 1929</figcaption></figure><p>In this article, you’ll find recommendations on how to make your Angular components and services more reusable, maintainable, and scalable.</p><h4>Pure Functions</h4><p>The most effective way to make your code reusable is to move as much code as possible to <a href="https://en.wikipedia.org/wiki/Pure_function">pure functions</a>.</p><p>Such functions have no side effects, have no state, and don’t mutate their arguments. Every time we call a pure function with the same arguments, it will return the same result.</p><p>Let’s refactor a component’s method into a pure function:</p><pre>export class ExampleComponent {<br>  @Input() items: Item[];<br>  @Input() users: User[];<br><br>  @Output() connected = new EventEmitter&lt;Item&gt;();<br><br>  private readonly errEmptyItemMsg = &#39;Item data can not be empty&#39;;<br><br>  //...<br><br>  protected addConnection(toItem: Item) {<br>    if (!toItem.data) {<br>      this.displayError(this.errEmptyItemMsg);<br>      return;<br>    }<br><br>    let hasNewConnection = false;<br><br>    for (const user of this.users) {<br>      if (!user.itemRefs.includes(toItem.id)) {<br>        user.itemRefs.push(toItem.id);<br>        hasNewConnection = true;<br>      }<br>    }<br><br>    if (hasNewConnection) {<br>      this.connected.emit(toItem);<br>    }<br>  }<br><br>  private displayError(errMsg: string) {<br>    // ...<br>  }<br>}</pre><p>Method addConnection() validates toItem, then adds a new item ID to every user. It can display an error and emit an event.</p><p>Let’s imagine that this method has not 18 lines, but 180, with complex validation rules. And suddenly, we need exactly this logic, with exactly the same validation (or a new data construction) in a completely different module of our application. Do not look at the “Copy” and “Paste” menu items in your editor — if validation logic is changed in one place, it should be modified in every other place.</p><p>Even if we were to swear to update this code in every place on every change, it’s still tightly coupled to our component. So, we simply cannot copy this code to any other place without also copying dependent parts of the component. You can easily spot this by looking for the usages of this.:</p><pre>this.displayError()<br>this.errEmptyItemMsg<br>this.users<br>this.connected</pre><p>We can split addConnection() into two methods: validation and linking.</p><pre>export class ExampleComponent {<br>  //...<br>  @Output() usersChange = new EventEmitter&lt;User[]&gt;();<br>  <br>  protected addConnection(toItem: Item) {<br>    if (!isItemConnectable(toItem)) {<br>      this.displayError(this.errEmptyItemMsg);<br>      return;<br>    }<br><br>    const { users, hasNewConnection } = addItemToUsers(toItem, this.users);<br><br>    if (hasNewConnection) {<br>      this.connected.emit(toItem);<br>      this.usersChange.emit(users);<br>    }<br>  }<br>  <br>  // ...<br>}<br><br>export function isItemConnectable(item: Item): boolean {<br>  // here we have 100 lines of validation...<br>  // ...<br>  // ... very complex logic here ...<br>  // ...<br>  // ... not like this :)<br>  return !!item.data;<br>}<br><br>export function addItemToUsers(item: Item, users: User[]): {<br>  hasNewConnection: boolean,<br>  users: User[],<br>} {<br>  let hasNewConnection = false;<br>  const updatedUsers = [] as User[];<br>  for (const user of users) {<br>    if (!user.itemRefs.includes(item.id)) {<br>      updatedUsers.push({ ...user, itemRefs: [...user.itemRefs, item.id] });<br>      hasNewConnection = true;<br>    } else {<br>      updatedUsers.push(user);<br>    }<br>  }<br>  return {<br>    users: updatedUsers,<br>    hasNewConnection<br>  };<br>}</pre><p>Because the new functions should be pure, they cannot modify their arguments. So, in addItemToUsers(), we had to create a new array of users and make a shallow copy for the users we want to connect, instead of mutating them directly.</p><pre>// before<br>if (!user.itemRefs.includes(toItem.id)) {<br>  user.itemRefs.push(toItem.id);<br>}<br><br>// after<br>if (!user.itemRefs.includes(item.id)) {<br>  updatedUsers.push({ ...user, itemRefs: [...user.itemRefs, item.id] });<br>} else {<br>  updatedUsers.push(user);<br>}<br><br>this.usersChange.emit(users);</pre><p>This way, we actually fixed quite a nasty bug of data de-synchronization, described in more detail in <a href="https://medium.com/@eugeniyoz/angular-inputs-and-single-source-of-truth-75939491f701">this article</a>. Now we will emit a new array of users every time a new connection is created, so the parent component will have the correct data. Bingo!</p><p>Now we can reuse our super complex logic in other places — the new functions have no dependencies, all they need are their arguments.</p><p>When refactoring your code into pure functions, require as little information in arguments as possible. If your function requires product: HugeProductDataStructure and in its code it uses product just for product.id and product.price, then it is much better to define arguments like productId and productPrice, rather than requiring a structure that might be difficult to fetch in some places of your code. But don’t over-explode your list of arguments ;)</p><h4>Component State Management</h4><p>Some methods just can not avoid using this because they have to modify the state of your component. You can still make such code reusable, by moving this logic into a separate file, called “local store”.</p><p>A local store might be represented as a class or as a function (see examples in <a href="https://ngrx.io/guide/signals/signal-store">NgRx SignalStore docs</a>).</p><p>Here is an example of how we could move logic, responsible for saving users, to a local store:</p><pre>@Injectable()<br>export class ExampleStore {<br>  private readonly api = inject(ApiService);<br><br>  readonly saveUsers = sideEffect&lt;User[]&gt;(_ =&gt; _.pipe(<br>    exhaustMap((users) =&gt; this.api.saveUsers(users)),<br>    catchError((err) =&gt; {<br>      this.displayError(err.msg);<br>      return EMPTY;<br>    })<br>  ));<br>}<br><br>@Component({<br>  selector: &#39;app-example&#39;,<br>  // ...<br>  providers: [ExampleStore]<br>})<br>export class ExampleComponent {<br>  // ...<br>  private readonly store = inject(ExampleStore);<br><br>  protected saveUsers() {<br>    this.store.saveUsers(this.users);<br>  }<br>}</pre><p>Because of this refactoring, we now can reuse logic, responsible for saving users&#39; data (which often is not as primitive as this example) in other places of our code, simply by injecting the ExampleStore.</p><p>You can read more about the benefits that this refactoring brings, and about different kinds of stores, in <a href="https://medium.com/@eugeniyoz/application-state-management-with-angular-signals-b9c8b3a3afd7">this article</a>.</p><p>In the previous code example, you might notice that saveUsers() has just one line, and some of you may ask, “Maybe we could just call this method directly from the template? Why the extra wrapping?”</p><p>The answer here is: it is up to you to decide. Some developers trust their local stores and IDEs, so if the store’s method name or signature changes, the IDE will automatically update it in the template. Other developers say that templates calling only component’s methods look cleaner and more reliable to them. This topic is very subjective, and there cannot be only one correct answer.</p><h4>Miscellaneous</h4><ul><li>Prefer <a href="https://en.wikipedia.org/wiki/Composition_over_inheritance">composition to inheritance</a>. Never extend components (yes, never). In some very rare cases, when you see that only inheritance can be used, it is okay to extend stores. However, never extend components. We might think that we increase code reusability this way, but there are other ways to achieve the same result without inheritance. What we sacrifice when we extend components is <strong>maintainability</strong>, and that is a very important aspect of the code.</li><li>Even if ::ng-deep is un-deprecated, try to avoid it. When it’s not possible (and there are such cases), use it with extreme care. To help your future self, use very specific selectors inside ::ng-deep. When you reuse components that use ::ng-deep, and these components have multiple levels of sub-components, things might quickly go out of control if the selectors inside ::ng-deep are not specific enough.</li><li>When using Observables, avoid nested subscriptions. Additionally, methods of your services that return observables are much more reusable than methods that subscribe. You don’t always need that level of reusability, but sometimes you might want to reuse code that is inside a method that subscribes and unsubscribes. In such cases, just refactor that method into two: one that returns an observable, and the second one that uses that observable.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b169d4a1c786" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Angular Inputs and Single Source of Truth]]></title>
            <link>https://medium.com/@eugeniyoz/angular-inputs-and-single-source-of-truth-75939491f701?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/75939491f701</guid>
            <category><![CDATA[ssot]]></category>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Tue, 30 Apr 2024 13:41:56 GMT</pubDate>
            <atom:updated>2025-12-10T12:46:13.276Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZnMUDDGOcNAFcO9De8mD8w.jpeg" /><figcaption>“Two Cypresses”, Paul Signac, 1893</figcaption></figure><p>The main way for Angular components to receive external data is through “inputs.” In this article, I’ll explain why the <strong>Single Source of Truth</strong> principle should be applied to inputs.</p><p>Wikipedia provides a scientific description of the <a href="https://en.wikipedia.org/wiki/Single_source_of_truth">Single Source of Truth</a>, but if we apply it to Angular components and their inputs, it will sound like this:</p><blockquote><strong><em>Inputs of a component should only be modified from outside the component.</em></strong></blockquote><p>This simple rule can save your code from very weird bugs. Let’s explore the simplest case:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-zyojns%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-zyojns%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-zyojns%2F5579501c5c09f77630bcc52fd0dae687&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/5a57e455950a955b48b5ecdfa6d2bec9/href">https://medium.com/media/5a57e455950a955b48b5ecdfa6d2bec9/href</a></iframe><p>In this small demo (<a href="https://stackblitz.com/edit/stackblitz-starters-zyojns?file=src%2Fmain.ts">source code</a>), you can add items to your shopping cart, but after 10 seconds, the list will be replaced with some items you didn’t pick.</p><p>This demo emulates a situation where the shopping cart data is loaded from the server, but while the request was loading, the user was able to add some items to the cart. Maybe it’s not the best demo, but I hope it illustrates the idea. Creating code with bugs intentionally is not as easy as it happens unintentionally 😉</p><p>Generally speaking, the state of our component is globally mutable. We should avoid it everywhere, not only in components. But let’s see how we can avoid it in components.</p><h4>Immutable Inputs</h4><p>Thankfully, Angular gives us two ways to create inputs, that can not be modified inside the component: accessors and signals.</p><p>In Angular before v17 accessors were the only way:</p><pre>@Component()<br>export class ShoppingCart {<br>  private _items: Item[] = [];<br><br>  @Input() set items(items: Item[]) {<br>    this._items = items;<br>  }<br><br>  get items() {<br>    return this._items;<br>  }<br>}</pre><p>In Angular v17.1 we got Signal Inputs:</p><pre>@Component()<br>export class ShoppingCart {<br>  items = input&lt;Item[]&gt;([]);<br>}</pre><p>Fewer lines, more awesomeness.</p><blockquote><em>There are also more practical reasons to use signal inputs over setters, you can read about them in </em><a href="https://riegler.fr/blog/2024-05-01-input-setters-caveats"><em>this article</em></a><em> by </em><a href="https://twitter.com/Jean__Meche"><em>Matthieu Riegler</em></a><em>.</em></blockquote><p>Now if we try to update items inside the component:</p><pre>@Component()<br>export class ShoppingCart {<br>  items = input&lt;Item[]&gt;([]);<br><br>  private readonly api = inject(Api);<br><br>  constructor() {<br>    this.api.getCartItems().subscribe((items) =&gt; {<br>      this.items.set(items);<br>      //         ^^:<br>      // 🔴 Property &#39;set&#39; does not exist on type &#39;InputSignal&lt;Item[]&gt;&#39;.<br>    });<br>  }<br>}</pre><p>TypeScript generates an error and protects our code.</p><p>If you encounter an error like this, do not try to find a workaround — Angular and TypeScript are on your side here and trying to help you. Modify your code to fix the bug. Sometimes it will require effort and time, but you’ll fix one of the nastiest kinds of bugs.</p><p>To fix our example, we should decide which part of our code should be the source of truth for the list of items in our shopping cart. Only that code will be allowed to modify (mutate) the list of items. Also, this code should be responsible for protecting the list from undesired modifications.</p><p>In our example, the root component doesn’t need to know what items are in the shopping cart, so it should either the Api service (which is shared globally), or the ShoppingCart component.</p><p>In real apps, we might need information about the shopping cart items not only in the ShoppingCart, but also in the navigation components.</p><p>So, let’s make the Api service the source of truth for this list:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-k9phju%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-k9phju%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-k9phju%2F08cff32206ebf72cd14f3e6cd6ed8219&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/14c53950aed6006579b097ac1a4fa4a9/href">https://medium.com/media/14c53950aed6006579b097ac1a4fa4a9/href</a></iframe><p>Now, if you explore the <a href="https://stackblitz.com/edit/stackblitz-starters-k9phju?file=src%2Fapi.ts">source code</a>, you’ll find that most of the logic is moved to the Api service, and this service now has the only source of truth for the shopping cart items. And now, this service protects this source of truth from unintentional modifications.</p><pre>  addItemToCart(item: Item) {<br>    if (untracked(this.$isLoading)) {<br>      return;<br>    }<br>    this._$cartItems.update((items) =&gt; Array.from(new Set([...items, item])));<br>  }</pre><blockquote><em>If you are curious why </em><em>untracked() is used here, </em><a href="https://medium.com/@eugeniyoz/angular-signals-reactive-context-and-dynamic-dependency-tracking-d2d6100568b0"><em>this article</em></a><em> will be interesting for you.</em></blockquote><p>The code of our components becomes much more compact, and the ShoppingCart component is now a “dumb” component.</p><pre>export class ShoppingCart {<br>  items = input&lt;Item[]&gt;([]);<br>  removeItem = output&lt;Item&gt;();<br><br>  protected remove(item: Item) {<br>    this.removeItem.emit(item);<br>  }<br>}</pre><p>The Api service has a little more code now, but for a very good reason — we’ve fixed a bug!</p><p>This article uses new features of Angular v17.1, but you could achieve it with just accessors and observables in any version of Angular. Code examples are far from perfect, and it was done intentionally to illustrate how a bug can be introduced and fixed.</p><p>The “Signal Source of Truth” architecture (or “rule,” or “principle”) states that if some information is shared across the application, there should be only one part of the code that can mutate this information. This principle can protect your application from bugs, which, as is often the case in practice, are much worse than theoretical examples in articles. Apply it to your components and services to make them more robust!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=75939491f701" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Angular Signals, Reactive Context, and Dynamic Dependency Tracking]]></title>
            <link>https://medium.com/@eugeniyoz/angular-signals-reactive-context-and-dynamic-dependency-tracking-d2d6100568b0?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/d2d6100568b0</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[signal]]></category>
            <category><![CDATA[reactive-programming]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Thu, 11 Apr 2024 19:04:52 GMT</pubDate>
            <atom:updated>2025-12-10T12:46:31.974Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uXxcb9By_Z8JrhWoyk9RLg.jpeg" /><figcaption>“The Ball”, Félix Vallotton, 1899</figcaption></figure><p>To effectively use Angular Signals, it’s crucial to understand the concept of the “reactive context” and how dependency tracking works. In this article, I’ll explain both these things, and show how to avoid some related bugs.</p><h4>Dependency Tracking</h4><p>When you use Angular Signals, you don’t need to worry about subscribing and unsubscribing. To understand how it works, we’ll need a few terms:</p><ul><li><strong>Dependency Graph</strong>: graph of nodes, every node implements ReactiveNode interface;</li><li><strong>Producers</strong>: nodes that contain values and notify about new values (they “produce reactivity”);</li><li><strong>Consumers</strong>: nodes that read produced values (they “consume reactivity”);</li></ul><p>Signals are <em>producers</em>, computed() is <em>producer</em> and <em>consumer</em> simultaneously, effect() is <em>consumer</em>, templates are <em>consumers</em>.</p><blockquote><em>You can read a more detailed article about the </em><a href="https://medium.com/@eugeniyoz/dependency-graph-in-angular-signals-53ee47f75e21"><em>Dependency Graph in Angular Signals</em></a><em> (with animated graph example).</em></blockquote><p><strong>How automatic dependency tracking works</strong>: there is a variable, global for all the reactive nodes, activeConsumer, and every time computed() runs its computation function, every time effect() runs its side-effects function, or a template is being checked for changes, they:</p><ol><li>Read the value of activeConsumer (to remember the previous consumer);</li><li>Register themselves as a activeConsumer;</li><li>Run the function or execute a template (some signals might be read during this step);</li><li>Register the previous consumer (from Step 1) as an activeConsumer.</li></ol><p>When any producer is read, it retrieves the value of activeConsumer and includes this active consumer in the list of consumers dependent on the signal. When a signal is updated, it subsequently sends a notification to every consumer from its list.</p><p>Let’s examine what happens step by step in this example:</p><pre>@Component({<br>  template: `<br>   Items count: {{ $items().length }}<br>   Active items count: {{ $activeItemsCount() }}<br>`   <br>})<br>class ExampleComponent {<br>  protected readonly $items = signal([{id: 1, $isActive: signal(true) }]);<br><br>  protected readonly $activeItemsCount = computed(() =&gt; {<br>    return this.getActiveItems().length;<br>  });<br><br>  private getActiveItems() {<br>    return this.$items().filter(i =&gt; i.$isActive());<br>  }<br>}</pre><ol><li>The template reads the value of activeConsumer and saves it to the prevConsumer variable (this variable is local to the template);</li><li>The template sets itself as activeConsumer;</li><li>It calls $items() signal to get a value;</li><li>$items signal retrieves the value of activeConsumer;</li><li>The received value is not empty (it contains a link to the template), so $items signal puts this value (link to our template) into the list of consumers. After that, every time $items is updated, the template will be notified — a new link has been created in the dependency graph;</li><li>$items returns a value to the template;</li><li>The template reads the value of $activeItemsCount signal. To return a value, $activeItemsCount needs to run its computation function (the function we pass in our code to computed();</li><li>Before running the computation function, $activeItemsCount reads the value of activeConsumer and saves it to its local variable prevConsumer. Because $activeItemsCount is also a consumer, it puts a link to itself to the activeConsumer variable;</li><li>Computation function calls getActiveItems() function;</li><li>Inside this function, we read the value of $items — steps from 3 to 6 are repeated, but because our template is already dependent on $items, Step 5 will not add a new consumer to the list;</li><li>When the value (an array of items) is returned, getActiveItems() reads every element of this array and reads the value of $isActive();</li><li>$isActive is a signal. So, before it returns a value, it repeats steps 3–6. During step 4, $isActive retrieves the value of activeConsumer. At this moment activeConsumer contains a link to $activeItemsCount, so at step 5 $isActive (each one from the array) will add $activeItemsCount to the list of dependent consumers. Any time $isActive is updated, $activeItemsCount will be notified, $activeItemsCount will notify our template that its value is stale and needs to be recomputed. After that, our template <strong><em>eventually</em></strong> (not right after the notification) will ask $activeItemsCount what is the new value, and steps from 7 to 14 will be repeated;</li><li>getActiveItems() returns a value. $activeItemsCount uses this value for computation and before returning it, it puts the value of its local variable prevConsumer to the activeConsumer variable;</li><li>$activeItemsCount returns a value;</li><li>The template puts the previously saved value of prevConsumer to activeConsumer.</li></ol><p>It is not a short list, but please read it thoroughly.</p><p>The most important thing here is: that consumers (computed(), effect(), templates) don’t need to worry about adding signals they read to the list of dependencies. Signals will do it themselves, using the activeConsumer variable. This variable is accessible to any reactive node, so it doesn’t matter how deep in the functions chain some signal will be read — any signal will get the value of activeConsumer and add it to the list of consumers.</p><p>Remember: if you call a function in a template, computed() or effect() (a consumer), and that function reads another function and that function reads another function…, and finally, at some level, a function reads a signal, and that signal adds that consumer to its list and will notify it about the updates.</p><p>Debugging-like reading can be tedious, so let me entertain you with this <a href="https://stackblitz.com/edit/stackblitz-starters-zvhx3t?file=src%2Fmain.ts">small app</a>:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-zvhx3t%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-zvhx3t%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-zvhx3t%2F86b4fd2e5da130f13662c69ccd0de346&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/00e53618d5af774783c6a72cea5a5164/href">https://medium.com/media/00e53618d5af774783c6a72cea5a5164/href</a></iframe><p>Please do the following in that app:</p><ol><li>Click button “2” to make it active, then click it again. Notice that the “Active items” text above the buttons reflects the change;</li><li>Click the “Add Item” button;</li><li>Click button “4”. Notice that “Active items” doesn’t reflect the change;</li><li>Click button “2”;</li><li>Now click button “4” a few times and notice that the “Active items” text reflects it as expected.</li></ol><p>But why? Let’s check the code:</p><pre>export type Item = {<br>  id: number;<br>  $isActive: WritableSignal&lt;boolean&gt;;<br>};<br><br>@Component({<br>  selector: &#39;my-app&#39;,<br>  template: `<br>    &lt;div&gt;Active items: {{ $activeItems() }}&lt;/div&gt;<br>    &lt;div&gt;<br>      &lt;span&gt;Click to to toggle:&lt;/span&gt;<br>      @for(item of items; track item.id) {<br>        &lt;button (click)=&quot;item.$isActive.set(!item.$isActive())&quot; <br>                [class.active]=&quot;item.$isActive()&quot;&gt;<br>          {{ item.id }}<br>       &lt;/button&gt;<br>      }<br>    &lt;/div&gt;<br>    &lt;div&gt;<br>      &lt;button (click)=&quot;addItem()&quot;&gt;Add Item&lt;/button&gt;<br>    &lt;/div&gt;<br>  `,<br>})<br>export class App {<br>  protected readonly items: Item[] = [<br>    { id: 1, $isActive: signal(true) },<br>    { id: 2, $isActive: signal(false) },<br>    { id: 3, $isActive: signal(true) },<br>  ];<br><br>  protected readonly $activeItems = computed(() =&gt; {<br>    const ids = [];<br>    for (const item of this.items) {<br>      if (item.$isActive()) {<br>        ids.push(item.id);<br>      }<br>    }<br>    return ids.join(&#39;, &#39;);<br>  });<br><br>  protected addItem() {<br>    this.items.push({<br>      id: this.items.length + 1,<br>      $isActive: signal(false),<br>    });<br>  }<br>}</pre><p>Now let’s analyze why the “Active items” line is not updated correctly.</p><p>Binding in our template:</p><pre>&lt;div&gt;Active items: {{ $activeItems() }}&lt;/div&gt;</pre><p>$activeItems is a signal, provided by computed():</p><pre>protected readonly $activeItems = computed(() =&gt; {<br>  const ids = [];<br>  for (const item of this.items) {<br>    if (item.$isActive()) {<br>      ids.push(item.id);<br>    }<br>  }<br>  return ids.join(&#39;, &#39;);<br>});</pre><p>The function we pass to computed() will be re-executed every time any of the signals it reads is updated. What signals do we read there?</p><p>It’s $isActive signal of every item in the this.items array.</p><blockquote><em>Notice how the </em><em>$ sign in the name of the variable helps quickly find the sources of reactivity. This principle applies similarly to the </em><em>effect() function and component templates. That’s why I use it, but it’s simply a matter of personal preference.</em></blockquote><p>So why $activeItems was not updated after steps 2 and 3?</p><p>The computation function will only be re-executed when one of the signals it depends on is updated.</p><p>When we click “Add Item”, we modify this.items and create a new signal inside the new item. But before this moment, our computed() function had never read that signal, so it does not have it in the list of dependencies.</p><p>Before and <strong>after</strong> clicking “Add Item,” the list of signals that $activeItems depends on remains unchanged: three $isActive signals from the three items in this.items.</p><p>Because none of these signals is modified when we click “Add Item”, computed() will not be notified and the computation function will not be re-executed.</p><p>We can toggle our new item in the list of buttons as many times as we want, but only the signals of the 3 first items will notify $activeItems and it will re-execute the function we sent.</p><p>But if we re-execute our computation function, it will read all the items from this.items again and will read the new signal, finally. The new signal will become the new dependency of the $activeItems node, and it will be notified every time one of them is changed.</p><p>To do this, we need to modify one of the existing dependencies: that’s why we click button “2” in step 4.</p><p>This example is created to remind you, that functions we pass to computed() and effect() will only be re-executed, when one of the producers they read is updated.</p><p>This is why it is always useful to double-check, what dependencies your computed() has and what of them should cause a re-computation. If some of them should not — use untracked().</p><p>Some of the functions we pass to computed() or effect() might read signals (or functions they call might read signals).</p><pre>this.$petWalkingIsAllowed = computed(() =&gt; {<br>  return this.$isFreeTime() &amp;&amp; this.isItGoodWeatherOutside();<br>});<br><br>isItGoodWeatherOutside() {<br>  return $isSunny() &amp;&amp; $isWarm() &amp;&amp; !$isStormy();<br>}</pre><p>To understand if we should wrap such calls with untracked() to avoid non-desired recomputations, we can use this logic:</p><ul><li>If we <strong>don’t want</strong> our computed() to compute a new result when that function (isItGoodWeatherOutside()) returns a new value, then wrap it with untracked():</li></ul><pre>this.$petWalkingIsAllowed = computed(() =&gt; {<br>  return this.$isFreeTime() &amp;&amp; untracked(() =&gt; this.isItGoodWeatherOutside());<br>});<br><br>isItGoodWeatherOutside() {<br>  return $isSunny() &amp;&amp; $isWarm() &amp;&amp; !$isStormy();<br>}</pre><ul><li>If on every new value from that function we <strong>do want</strong> to re-run our computation, do not wrap it with untracked().</li></ul><p>As you can see, untracked() helps us control which dependencies we want to track. It also helps to manage another important aspect:</p><h4>Reactive Context</h4><p>Above, in “How automatic dependency tracking works,” I’ve mentioned the variable activeConsumer.</p><p>When activeConsumer is not null, signals we read will add that activeConsumer to the list of consumers, to later notify members of this list about modifications of a signal. If a reactive node is read while activeConsumer is empty, it will not create any new link in the reactive nodes dependency graph.</p><p>In other words, while activeConsumer is set, we are reading signals within the <em>Reactive Context</em>.</p><p>In the majority of cases, the reactive context will be handled automatically, and only the intended links and dependencies will be created and removed.</p><p>But sometimes we unintentionally <em>leak</em> the reactive context.</p><p>Let’s try out this <a href="https://stackblitz.com/edit/stackblitz-starters-hgrfbo?file=src%2Fmain.ts">app</a>:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-hgrfbo%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-hgrfbo%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-hgrfbo%2F9186d6943a8031f2c104e625ed5808d8&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/27323da5b59136548e2554b27ec4a062/href">https://medium.com/media/27323da5b59136548e2554b27ec4a062/href</a></iframe><p>If you try to use it, you’ll notice that:</p><ul><li>clicking “Add Item” leads to a complete reset of all statuses;</li><li>clicking to toggle the status changes it randomly, affecting more than just one button.</li></ul><p>Can you quickly spot the bug?</p><pre>@Component({<br>  template: `<br>    &lt;div&gt;Active items: {{ $activeItems() }}&lt;/div&gt;<br>    &lt;div class=&quot;flex-row&quot;&gt;<br>      &lt;span&gt;Click to to toggle:&lt;/span&gt;<br>      @for(item of $items(); track item.id) {<br>      &lt;button (click)=&quot;item.$isActive.set(!item.$isActive())&quot; [class.active]=&quot;item.$isActive()&quot; [style.transform]=&quot;&#39;scale(&#39;+item.$scale()+&#39;)&#39;&quot;&gt;<br>        {{ item.id }}<br>      &lt;/button&gt;<br>      }<br>    &lt;/div&gt;<br>    &lt;div&gt;<br>      &lt;button (click)=&quot;addItem()&quot;&gt;Add Item&lt;/button&gt;<br>    &lt;/div&gt;<br>  `,<br>})<br>export class App {<br>  private readonly $itemsCount = signal(3);<br><br>  protected readonly $items: Signal&lt;Item[]&gt; = computed(() =&gt; {<br>    console.warn(&#39;Generating items!&#39;);<br>   <br>    const items: Item[] = [];<br>    for (let id = 0; id &lt; this.$itemsCount(); id++) {<br>      const $isActive = signal(Math.random() &gt; 0.5);<br>      const $scale = signal($isActive() ? 1.2 : 1);<br>      items.push({ id, $isActive, $scale });<br>    }<br>    return items;<br>  });<br><br>  protected readonly $activeItems = computed(() =&gt; {<br>    const ids = [];<br>    for (const item of this.$items()) {<br>      if (item.$isActive()) {<br>        ids.push(item.id);<br>      }<br>    }<br>    return ids.join(&#39;, &#39;);<br>  });<br><br>  protected addItem() {<br>    this.$itemsCount.update(c =&gt; c + 1);<br>  }<br>}</pre><p>What can we see here:</p><ul><li>We render the list of items from $items, which is computed();</li><li>$items generates a new array of items, and their count is controlled by the $itemsCount signal. Every time we modify $itemsCount, items are regenerated;</li><li>addItem() simply increments $itemsCount, triggering recomputation of $items.</li></ul><p>Now we can see why “Add Item” works this way. Let’s try to figure out why the status toggling behaves strangely.</p><p>If we open the console, we’ll notice that every time we click a button, a “Generating items!” warning is logged. But why? We’re not modifying $itemsCount, so why is $items recomputed?</p><p>Perhaps you’ve already noticed that the computation function of $items reads one more source of reactivity: the signal $isActive:</p><pre>const $scale = signal($isActive() ? 1.2 : 1);</pre><p>This signal ($isActive) is being read in the reactive context: activeConsumer contains $items, so $isActive will notify $items about every change. Therefore, when we modify $isActive in an attempt to toggle this status, we trigger recomputation of $items.</p><p>There are multiple ways to fix this bug, but this approach prevents the leakage of the reactive context:</p><pre>const $scale = signal(untracked($isActive) ? 1.2 : 1);</pre><p>What does untracked() <a href="https://github.com/angular/angular/blob/75a186e321cb417685b2f13e9961906fc0aed36c/packages/core/src/render3/reactivity/untracked.ts#L15">do</a>?</p><pre>/** <br> * https://github.com/angular/angular/blob/75a186e321cb417685b2f13e9961906fc0aed36c/packages/core/src/render3/reactivity/untracked.ts#L15<br> *<br> * packages/core/src/render3/reactivity/untracked.ts<br> *<br> **/<br>export function untracked&lt;T&gt;(nonReactiveReadsFn: () =&gt; T): T {<br>  const prevConsumer = setActiveConsumer(null);<br>  try {<br>    return nonReactiveReadsFn();<br>  } finally {<br>    setActiveConsumer(prevConsumer);<br>  }<br>}</pre><ul><li>sets activeConsumer to null and saves the returned value to the local variable prevConsumer;</li><li>runs the given function;</li><li>restores activeConsumer from prevConsumer.</li></ul><p>It temporarily disables the reactive context, executes our function, and then restores the reactive context.</p><p>Because of that, while our function is being executed, if any signals are being read, they will read null from activeConsumer and won’t add it to their lists of consumers. In other words, no new dependencies will be created.</p><p>In this example, we have some “hints” in the console, and our code is very small and simple. In real apps, signal reading might be buried deep within the function call chain, and the code could be much larger and more complex. Bugs like this can be challenging to debug in real apps, which is why I recommend preventing them by using untracked() whenever you don&#39;t want to leak the reactive context.</p><p>There are quite interesting and <a href="https://github.com/angular/angular/pull/54614#issue-2155451129">unexpected</a> ways to leak the reactive context:</p><ul><li>Creating an instance of a class that reads some signals;</li><li>Calling a function that calls another function, which reads a signal;</li><li><a href="https://github.com/angular/angular/issues/54548#issuecomment-1958154417">Creating</a> a component inside effect();</li><li>Emitting a new value to an observable.</li></ul><p>When you use computed() and effect(),</p><ul><li>Read other signals with caution — they’ll rerun the entire function every time they change, triggered by any other function.</li><li>Make these functions easy to read and understand;</li><li>Double-check every source of reactivity that your function consumes.</li></ul><p>As is often the case, implicit dependency tracking brings not only benefits but also some trade-offs. But when used with skill and caution, you can build wonderful apps with Angular Signals!</p><p>I extend my heartfelt gratitude to the reviewers whose insightful comments and constructive feedback greatly contributed to the refinement of this article:</p><ul><li><a href="https://twitter.com/rainerhahnekamp">Rainer Hahnekamp</a></li><li><a href="https://twitter.com/joshuamorony">Josh Morony</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d2d6100568b0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Angular Signals: Best Practices]]></title>
            <link>https://medium.com/@eugeniyoz/angular-signals-best-practices-9ac837ab1cec?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/9ac837ab1cec</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[signal]]></category>
            <category><![CDATA[best-practices]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Wed, 20 Mar 2024 00:46:12 GMT</pubDate>
            <atom:updated>2025-12-10T12:43:33.816Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8mS3mzDjV6CC7h6IZO_eCA.jpeg" /><figcaption>“Sun in the Afternoon”, Eugenie Bandell, 1913</figcaption></figure><p>In this article, I share my experience of working with Angular Signals after almost a year of using them.</p><h4>When to use Signals?</h4><ol><li>In templates;</li><li>For state management;</li><li>When you need to react to changes in a value without a time aspect.</li></ol><p>In Angular templates, Signals are better than Observables: they schedule Change Detection without any pipes, they are glitch-free, and you can read the same signal multiple times and it will be “free” in terms of performance (and read values are guaranteed to be the same). There are other reasons that are not so easy to explain briefly, but that’s already enough to make a rule: every variable (that might change) in your <strong>new</strong> templates should be a Signal.</p><p>Outside of templates, Signals also can be used for reactivity, but, as I mentioned, without a time aspect.</p><p>I once wrote a post on Twitter about it, and now I’ll post it here, updated and improved:</p><p>There are two ways to create reactive variables in Angular: Observables and Signals. If you describe in words, how your variable should express its reactivity, you’ll see what you need to use.</p><p>If the role of a variable can be described as conditions, then you need a Signal:</p><ul><li>“if this variable has this value, then display this list”</li><li>“if this variable has this value, this button is disabled”</li></ul><p>If the description of a variable’s role includes words, related to time, you need an Observable:</p><ul><li>“<strong>when</strong> the cursor moves…”</li><li>“<strong>wait</strong> for the file uploading event and then…”</li><li>“every <strong>time</strong> this event happens, do this…”</li><li>“<strong>until</strong> this event…”</li><li>“for N <strong>seconds</strong> ignore…”</li><li>“<strong>after</strong> this request…”</li></ul><p>Signals have no time axis, and they can not delay a value — they always have a value, and their consumers should be always able to read it.</p><p>Consumers of Signals, computed(), effect(), and templates do not guarantee that they will read every new value written to the Signals they watch. An updated Signal will be <em>eventually</em> read, not instantly after the update as it happens with Observables. Consumers decide when they will read the new value using their scheduling mechanisms. It might be “in the next task,” “during the next Change Detection cycle,” or at some other moment, up to the consumer.</p><h4>When to use computed()?</h4><p>Whenever you like!</p><p>computed() is the best thing in Angular Signals, incredibly handy and safe to use. Using computed(), you’ll make your code more declarative (you can read more about it in<a href="https://medium.com/@eugeniyoz/creating-angular-components-template-first-declarative-approach-00c4a4791270"> this article</a>).</p><p>There are just two rules about the usage of computed():</p><ol><li>Do not modify things in computed(). It should compute a new result, that’s it. Do not modify the DOM, do not mutate variables using this, and do not call functions that might do that. Do not push values to Observables — it will cause unintentional reactive context propagation (explained below for effect()). computed() should not have side effects, it should be a <a href="https://en.wikipedia.org/wiki/Pure_function">pure function</a>.</li><li>Do not make asynchronous calls in computed(). This function does not allow modification of Signals (and it is amazingly helpful), but it can not track asynchronous code. Moreover, Angular Signals are strictly synchronous, so if you want to use asynchronous code in computed(), you are doing something wrong. So, no setTimeout(), no Promises, no other asynchronous things.</li></ol><h4>When to use effect()?</h4><p>Angular docs say that you’ll rarely need effect() and <a href="https://angular.dev/guide/signals#use-cases-for-effects">discourage</a> you from using it (<a href="https://gist.github.com/e-oz/99d04094abe5007d882f35879eb00da3">copy</a>, if docs will be edited).</p><p>And that info is correct: you rarely need effect()… if your code is declarative ;)</p><p>The more imperative your code, the more often you’ll need effect(). There is no code without imperative parts, but we all should try to make our code as declarative as possible, so we do need to use effect() as rarely as possible.</p><p>Besides the dangers, mentioned by Angular docs (infinite loops, change detection errors), there is another thing, that might be quite nasty: effects are executed in a reactive context, and any code you call in effect, will be executed in a reactive context. If that code reads some signals, they will be added as dependencies to your effect. <a href="https://github.com/angular/angular/pull/54614">Here</a> Alex Rickabaugh explains the details.</p><p>I still don’t want to encourage you to use effect(), but I’ll give you advice on how to use it as safely as possible:</p><ol><li>The function you provide to effect() should be as small as possible. This way it will be easier to read and spot erroneous behavior.</li><li>Read signals first, then wrap the rest of the effect into untracked():</li></ol><pre>effect(() =&gt; {<br>  // reading the signals we need<br>  const a = this.a();<br>  const b = this.b();<br>  <br>  untracked(() =&gt; {<br>    // rest of the code is here - this code should not<br>    // modify the signals we read above!<br>    if (a &gt; b) {<br>      document.title = &#39;Ok&#39;;<br>    }<br>  });<br>});</pre><p>More information about cases where untracked() helps can be found in <a href="https://medium.com/@eugeniyoz/angular-signals-reactive-context-and-dynamic-dependency-tracking-d2d6100568b0">this article</a>.</p><h4>Mixing Signals and Observables</h4><p>…is ok!</p><p>Your code will have Signals and Observables, at least because Signals can not be used for every kind of reactivity (see detailed explanation above). It is not an issue, it is perfectly fine.</p><p>If you need some value from an Observable in your computed(), create a Signal using toSignal() (outside of computed()).</p><p>If you need to read a Signal in Observable’s pipe(), there are two ways:</p><ol><li>If you need to react to changes in that Signal in your Observable, then you need to convert that Signal into Observable and add it using some join operator.</li><li>If you are sure you just need the current value of a Signal and you don’t need to react to its changes — you can read a Signal directly in your operators or subscribe(). Observable is not a reactive context, so you don’t need untracked() here.</li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9ac837ab1cec" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Preparing our Code for Zoneless Angular]]></title>
            <link>https://medium.com/@eugeniyoz/preparing-our-code-for-zoneless-angular-22cb7a738151?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/22cb7a738151</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[change-detection]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Sat, 16 Mar 2024 12:54:42 GMT</pubDate>
            <atom:updated>2025-12-10T12:46:48.729Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yQdqSeEIKMSvi2U4SRAuvw.jpeg" /><figcaption>“Le Pont Neuf Paris”, Pierre-Auguste Renoir, 1872</figcaption></figure><p>This article explains how to modify your code to make your library or app work without Zone.js.</p><h4>What is Zoneless Angular?</h4><p>In the article “<a href="https://medium.com/@eugeniyoz/angular-change-detection-today-and-tomorrow-b9c64bd294f8">Angular Change Detection — Today and Tomorrow</a>”, I’ve explained how different types of change detection work in Angular.</p><p>The main benefit of making Angular apps zoneless is performance. No more unnecessary CD cycles and recomputations of values. No more checking every binding on every mousemove event. No more runOutsideAngular() calls to reduce the amount of CD cycles, no more juggling with detach() and detectChanges().</p><p>Another benefit: we can safely use async..await syntax because we don’t rely anymore on monkey-patching Promises by Zone.js.</p><p>Removing Zone.js dependency also removes the “Angular-compatible” term for JS tools: you don’t need to worry anymore if changes that some JS tool makes will be caught by Angular’s Change Detection and rendered correctly.</p><h4>Prerequisites</h4><p>Before your app goes zoneless, it should be “OnPush compatible”. It means, that every binding in your template is reactive.</p><p>Reactive bindings will notify Angular that their value has changed, so the DOM might need to be modified.</p><p>Examples of non-reactive bindings:</p><pre>@Component({<br>  template: `<br>   &lt;div&gt; {{ name }} &lt;/div&gt;<br>   &lt;child [value]=&quot;value&quot;/&gt;<br>`<br>})<br>export class ExampleComponent {<br>   name = &quot;Alice&quot;;<br>   value = &quot;Bob&quot;;<br>}</pre><p>The bindings {{ name }} and [value]=”value” lack reactivity — they won’t update if the values of name or value are modified because the template cannot know about it.</p><p>Reactive bindings:</p><pre>@Component({<br>  template: `<br>   &lt;div&gt; {{ name() }} &lt;/div&gt;<br>   &lt;child [value]=&quot;value | async&quot;/&gt;<br>`<br>})<br>export class ExampleComponent {<br>   name = signal(&quot;Alice&quot;);<br>   value = new BehaviorSubject(&quot;Bob&quot;);<br>}</pre><p>Here, the template will get a notification when name or value are updated. What happens in the template (what is marked as dirty, what is marked for traversal) — just implementation details, it doesn’t matter. What matters is: when notified about the changes, Angular knows what part of the views tree it should update.</p><p>There are only two requirements:</p><ul><li>Every binding in your templates should be reactive;</li><li>Angular v17.1+.</li></ul><p>No other things are required. If all components in your app have changeDetection: ChangeDetectionStrategy.OnPush then your app will work fine in “zoneless” mode. There are many new things in the latest releases of Angular, but they are not required to be used to make your app zoneless.</p><h4>Preparing your code</h4><p>NgZone has a few methods that your app or library may use:</p><ul><li>onMicrotaskEmpty();</li><li>onStable();</li><li>onUnstable();</li><li>onError();</li><li>run(), runOutsideAngular(), runGuarded(), runTask().</li></ul><p>run*() methods are not needed in Zoneless Angular: you can just call your function directly.</p><p>Was:</p><pre>@Injectable()<br>export class ExampleService {<br>  private ngZone = inject(NgZone);<br>  <br>  draw() {<br>    this.ngZone.runOutsideAngular(() =&gt; this.putPointsToCanvas());<br>  }<br><br>  private putPointsToCanvas() {}<br>}</pre><p>Now:</p><pre>@Injectable()<br>export class ExampleService {<br>  draw() {<br>    this.putPointsToCanvas();<br>  }<br><br>  private putPointsToCanvas() {}<br>}</pre><p>To replace onStable() and onMicrotaskEmpty(), you can use afterRender() or afterNextRender().</p><p>afterRender() will be called after every rendering of the application (not some component), and afterNextRender() will be called after the next rendering event, once. Notice that both of them have <a href="https://angular.dev/guide/components/lifecycle#afterrender-phases">phases</a> (second argument).</p><p>Was:</p><pre>@Injectable()<br>export class ExampleService {<br>  private ngZone = inject(NgZone);<br>  <br>  draw(): Observable&lt;string&gt; {<br>    this.ngZone.onStable().pipe(<br>      tap(() =&gt; this.putPointsToCanvas())<br>    );<br>  }<br><br>  private putPointsToCanvas() {}<br>}</pre><p>Now:</p><pre>@Injectable()<br>export class ExampleService {<br>  private readonly afterNextRender$ = new Subject();<br>  <br>  constructor() {<br>    afterNextRender(() =&gt; this.afterNextRender$.next({}));<br>  }<br>  <br>  draw(): Observable&lt;string&gt; {<br>    this.afterNextRender$.pipe(<br>      tap(() =&gt; this.putPointsToCanvas())<br>    );<br>  }<br><br>  private putPointsToCanvas() {}<br>}</pre><p>This code will work both in Zoneless and “regular” Angular.</p><p>Notice that you can use afterRender() and afterNextRender() whenever you want; just call them in an injection context. Because, as the documentation says: “<em>The execution of render callbacks is not tied to any specific component instance, but instead an application-wide hook</em>”.</p><p>StackBlitz <a href="https://stackblitz.com/edit/stackblitz-starters-rtpn9w?file=src%2Fmain.ts">example</a>:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-rtpn9w%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-rtpn9w%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-rtpn9w%2F3e92d3ba793ae0011b799782a370c8b2&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/40a5362440a5fce76333e0c3458508ed/href">https://medium.com/media/40a5362440a5fce76333e0c3458508ed/href</a></iframe><h4>Third-party libraries</h4><p>The main purpose of this article is to help library maintainers in preparing their code for Zoneless Angular. But sometimes we use a library that is too large to fork, and we need to make it work in our application right now.</p><p>For libraries that are not updated yet and use onStable() or onMicrotaskEmpty(), we can emulate the behavior of onStable() and onMicrotaskEmpty(), and later remove all of these emulations when all the libraries we use are ready for Zoneless Angular (or if and when Angular provides its own emulation).</p><p>I’ve created a helper with functions that override the NoopNgZone implementation and emulate the onStable() behavior: <a href="https://gist.github.com/e-oz/4d64dd47699d3a63d15572ca49dc3db3">GitHub link</a>. I don’t publish it as a library because it is a temporary solution.</p><blockquote><em>I’ll repeat this because it is important: </em><strong><em>please update your apps and libraries to not rely on NgZone interface</em></strong><em> (see above how to modify your code). Do not make your code rely on emulations made by this code snippet. Only use it to temporarily workaround limitations of the libraries you cannot modify.</em></blockquote><p>Let me explain how this emulation works:</p><pre>export class NoopNgZone implements NgZone {<br>  // ...<br><br>  private readonly onStableEmitter = new EventEmitter&lt;any&gt;();<br>  private readonly schedule = getCallbackScheduler();<br><br>  get onStable() {<br>    this.schedule(() =&gt; this.onStableEmitter.emit({}));<br>    return this.onStableEmitter;<br>  }<br>  <br>  // ...<br>}</pre><p>When code calls NgZone.onStable(), our getter returns an Observable. Before returning it, we schedule an emission of the new value (which would mean that the zone is “stable”).</p><p>For scheduling, I use awesome code I found in the Angular <a href="https://github.com/angular/angular/blob/5328be6660b7ef388720edab4b3f2d0fa2699203/packages/core/src/util/callback_scheduler.ts">source code</a>.</p><p>Sometimes, libraries are so tightly bound to Zone.js that they expect onStable() or onMicrotaskEmpty() to emit quite often. For them, one event that we emit is not enough.</p><p>To make them work, you can find an event after which that library wants to do its job and artificially emit onStable() or onMicrotaskEmpty() when this event happens:</p><pre>export class ExampleComponent {<br>  private readonly ngZoneNoop = inject(NgZone) as NoopNgZone;<br>  private readonly docClickHandler = () =&gt; this.ngZoneNoop.emitOnStable();<br><br>  constructor() {<br>    document.addEventListener(&#39;click&#39;, this.docClickHandler);<br>  }<br><br>  ngOnDestroy() {<br>    document.removeEventListener(&#39;click&#39;, this.docClickHandler);<br>  }<br>}</pre><p>In the example above, we are listening for click events and making our NgZone implementation emit onStable event on every click.</p><p>I hope you will never need to use a workaround like this, and all the libraries will quickly adapt to Zoneless Angular.</p><h4>Converting our Angular app to zoneless</h4><p>This part is pretty easy:</p><ol><li>Provide a custom NgZone implementation and a scheduler:</li></ol><pre>import { provideExperimentalZonelessChangeDetection } from &#39;@angular/core&#39;;<br><br>bootstrapApplication(AppComponent, {<br>  providers: [<br>    provideExperimentalZonelessChangeDetection(),<br>  ]<br>});</pre><p>2. Remove the import of zone.js in your polyfills.ts file, or (if you don’t have this file), remove the “zone.js” line from the polyfills array in the angular.json file.</p><p>That’s it!</p><p>You can see that it doesn’t require a lot of changes if your app or lib is already “OnPush-compatible”.</p><p>Authors of libraries, please consider making your code compatible with Zoneless Angular. It will not take a lot of effort: afterRender() here, afterNextRender() there and that’s it 😉</p><blockquote><em>The Angular Team needs your </em><a href="https://github.com/angular/angular/issues"><em>feedback</em></a><em>, they want to know about the cases that don’t work and where </em><em>afterRender() is not good enough. Let’s start preparing our codebases and provide them feedback!</em></blockquote><p>I extend my heartfelt gratitude to the reviewers whose insightful comments and constructive feedback greatly contributed to the refinement of this article:</p><ul><li><a href="https://twitter.com/lilbeqiri">Ilir Beqiri</a></li><li><a href="https://twitter.com/Jean__Meche">Matthieu Riegler</a></li><li><a href="https://twitter.com/rainerhahnekamp">Rainer Hahnekamp</a></li><li><a href="https://twitter.com/tomastrajan">Tomas Trajan</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=22cb7a738151" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hacking Angular Signals]]></title>
            <link>https://medium.com/@eugeniyoz/hacking-angular-signals-42e4c3afba04?source=rss-ec188195efcc------2</link>
            <guid isPermaLink="false">https://medium.com/p/42e4c3afba04</guid>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[ OZ ]]></dc:creator>
            <pubDate>Sun, 18 Feb 2024 13:38:57 GMT</pubDate>
            <atom:updated>2025-12-10T12:47:05.968Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BRDMf8G7iSQ9zbQI2t7twQ.jpeg" /><figcaption>“Serpents II”, Gustav Klimt, 1907</figcaption></figure><p>Let’s check out what Angular Signals have under the hood, and how we can use it for extending their functionality and debugging.</p><h4>What are Angular Signals</h4><p>Angular Signals are functions with methods:</p><pre>const $val = signal(0);<br><br>// function:<br>const value = $val();<br><br>// object:<br>$val.set(4);<br>$val.update((v) =&gt; v * 2);</pre><p>How is this possible? Because in JavaScript, functions are objects:</p><pre>// declaring a function<br>const double = (val: number) =&gt; val * 2;<br><br>// adding a method<br>double.isEven = (number: number) =&gt; number % 2 === 0;<br><br>// use as object<br>double.isEven(15) // false<br><br>// use as function<br>double(8) // 16</pre><p>How can we use it?</p><p>We can:</p><ul><li>add new methods;</li><li>override methods set() and update() to intercept modifications;</li><li>read internal state variables and methods of signal;</li><li>create a proxy signal that will intercept reads.</li></ul><h4>Extending our Signals</h4><p>Sometimes you need a variable to be a Signal, but some parts of your code would like to use it as an Observable instead.</p><p>Fortunately, interfaces of Signals and Observables have no collisions in method names. So let’s create a hybrid:</p><pre>export function toObservableSignal&lt;T&gt;(<br>  s: Signal&lt;T&gt;, <br>  options?: ToObservableOptions<br>) {<br>  if (isDevMode() &amp;&amp; !options?.injector) {<br>    assertInInjectionContext(toObservableSignal);<br>  }<br><br>  // create an observable<br>  const obs = toObservable(s, options);<br>  <br>  // add methods of observable to our signal object<br>  for (const obsKey in obs) {<br>    (s as any)[obsKey] = (obs as any)[obsKey];<br>  }<br>  return s;<br>}</pre><p>Here we are simply adding new methods to the existing object.</p><p>Or, we could create a Proxy object and only return methods of an Observable, when our Signal doesn’t have the requested method:</p><pre>export function toObservableSignal&lt;T&gt;(s: Signal&lt;T&gt;) {<br><br>  const obs = toObservable(s, options); <br>   <br>  return new Proxy(s, {<br>    get(_, prop) {<br>      if (prop in s) {<br>        return (s as any)[prop];<br>      }<br>      return (obs as any)[prop];<br>    }<br>  });<br>}</pre><p>Usage <a href="https://stackblitz.com/edit/stackblitz-starters-5xxadr?file=src%2Fmain.ts">example</a>:</p><pre>@Component({<br>  //...<br>  template: `<br>    &lt;h4&gt;Signal A: {{ a() }}&lt;/h4&gt;<br>    &lt;h4&gt;Observable A: {{a|async}}&lt;/h4&gt;<br>    &lt;h4&gt;Signal C (computed() A*3): {{c()}}&lt;/h4&gt;<br><br>    {{quote()}}<br>  `,<br>})<br>export class App {<br>  a = toObservableSignal(signal&lt;number&gt;(1));<br><br>  // use as Observable<br>  b = this.a.pipe(<br>    debounceTime(500),<br>    distinctUntilChanged(),<br>    switchMap((v) =&gt; this.http.get(&#39;https://dummyjson.com/quotes/&#39; + v))<br>  );<br><br>  // use as Signal<br>  c = computed(() =&gt; this.a() * 3);<br><br>  quote = toSignal(this.b);<br><br>  increment() {<br>    // &quot;a&quot; will not stop being a Signal after <br>    // we used it as an Observable<br>    this.a.update((v) =&gt; v + 1);<br>  }<br><br>  decrement() {<br>    this.a.update((v) =&gt; Math.max(1, v - 1));<br>  }<br>}</pre><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-5xxadr%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-5xxadr%3Fctl%3D1%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-5xxadr%2Ffeee29f1ff10141c841cc57b821c4839&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/b706a844f4f00da584f8edc76d1d1d1f/href">https://medium.com/media/b706a844f4f00da584f8edc76d1d1d1f/href</a></iframe><p>You can find this function in the <a href="https://github.com/nartc/ngxtension-platform">NG Extension Platform</a>: <a href="https://ngxtension.netlify.app/utilities/signals/to-observable-signal/">documentation</a>.</p><p>You can extend Signals with any functions you want, your fantasy here has almost no limitations: just don’t use names set, update, and asReadonly.</p><h4>Overriding existing methods</h4><p>Let’s say that we want to intercept writes to our Signal to transform input values, duplicate information somewhere else, or just for debugging.</p><pre>function skipNonEvenNumbers(s: WritableSignal&lt;number&gt;) {<br>  const srcSet = s.set; // we need the source method to avoid recursion<br><br>  s.set = (value: number) =&gt; {<br>    if (value % 2 !== 0) {<br>      console.warn(&#39;[set] skipping:&#39;, value);<br>      return;<br>    }<br>    console.log(&#39;[set]:&#39;, value);<br>    srcSet(value);<br>  };<br>  s.update = (updateFn: (value: number) =&gt; number) =&gt; {<br>    const value = updateFn(s());<br>    if (value % 2 !== 0) {<br>      console.warn(&#39;[update] skipping:&#39;, value);<br>      return;<br>    }<br>    console.log(&#39;[update]:&#39;, value);<br>    srcSet(value);<br>  };<br>}</pre><p>Usage <a href="https://stackblitz.com/edit/stackblitz-starters-yyd2rc?devToolsHeight=50&amp;file=src%2Fmain.ts">example</a>:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-yyd2rc%3Fctl%3D1%26devToolsHeight%3D60%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-yyd2rc%3Fctl%3D1%26devToolsHeight%3D60%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-yyd2rc%2Fba82548e0bf193a7b3d93d42d9102d2c&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/7197f67c8e21efbd396757f61737041a/href">https://medium.com/media/7197f67c8e21efbd396757f61737041a/href</a></iframe><p>This trick is being used in <a href="https://github.com/e-oz/ngx-reactive-storage/">Reactive Storage</a>: <a href="https://github.com/e-oz/ngx-reactive-storage/blob/main/projects/ngx-reactive-storage/src/lib/observer.ts#L92">getWritableSignal()</a>.</p><h4>Inside a Signal</h4><p>Angular Signal is not just a function, it is an object. This object has a hidden field, SIGNAL, which contains some interesting data and functions. I’m glad we have it, and I hope you will not abuse it. Because the tricks above were “a little bit hacky”, and the tricks below are too filthy to use for anything but debugging, creating dev tools, and fun.</p><p>Every Angular Signal extends ReactiveNode:</p><pre>// packages/core/primitives/signals/src/graph.ts<br><br>export const REACTIVE_NODE: ReactiveNode = {<br>  version: 0 as Version,<br>  lastCleanEpoch: 0 as Version,<br>  dirty: false,<br>  producerNode: undefined,<br>  producerLastReadVersion: undefined,<br>  producerIndexOfThis: undefined,<br>  nextProducerIndex: 0,<br>  liveConsumerNode: undefined,<br>  liveConsumerIndexOfThis: undefined,<br>  consumerAllowSignalWrites: false,<br>  consumerIsAlwaysLive: false,<br>  producerMustRecompute: () =&gt; false,<br>  producerRecomputeValue: () =&gt; {},<br>  consumerMarkedDirty: () =&gt; {},<br>  consumerOnSignalRead: () =&gt; {},<br>};</pre><p>this way:</p><pre>// packages/core/primitives/signals/src/signal.ts<br><br>export const SIGNAL_NODE = {<br>  ...REACTIVE_NODE,<br>  equal: defaultEquals,<br>  value: undefined,<br>}</pre><p>But an instantiated Signal object does not contain all of this directly. All of them are hidden under the field that uses a Symbol as a name:</p><pre>// packages/core/primitives/signals/src/signal.ts<br><br>export function createSignal&lt;T&gt;(initialValue: T): SignalGetter&lt;T&gt; {<br>  const node: SignalNode&lt;T&gt; = Object.create(SIGNAL_NODE);<br>  node.value = initialValue;<br>  const getter = (() =&gt; {<br>                   producerAccessed(node);<br>                   return node.value;<br>                 }) as SignalGetter&lt;T&gt;;<br><br>  // next line adds a SignalNode to the field SIGNAL:<br>  (getter as any)[SIGNAL] = node;<br>  return getter;<br>}</pre><p>So if you have a signal $value and you access SIGNAL field, then you’ll get all of the fields that SIGNAL_NODE has.</p><p>How can we use it?</p><p>We can <a href="https://stackblitz.com/edit/stackblitz-starters-8xzgwj?file=src%2Fmain.ts">read fields</a> and override methods to intercept access and use it for debugging or to create fancy tools that illustrate what is going on inside a Signal and render the <a href="https://medium.com/@eugeniyoz/dependency-graph-in-angular-signals-53ee47f75e21">Dependency Graph</a>.</p><p>We can even convert some fields to accessors:</p><pre>function getSignalVersion&lt;T&gt;(s: WritableSignal&lt;T&gt;): Signal&lt;number&gt; {<br>  const node = s[SIGNAL];<br>  const $version = signal(0);<br><br>  Object.defineProperty(node, &#39;version&#39;, {<br>    get: () =&gt; {<br>      const v = untracked($version);<br>      console.log(&#39;🟢 reading:&#39;, v);<br>      return v;<br>    },<br>    set: (v) =&gt; {<br>      untracked(() =&gt; $version.set(v));<br>      console.log(&#39;🔴 writing:&#39;, v);<br>    },<br>  });<br><br>  return $version.asReadonly();<br>}</pre><p><a href="https://stackblitz.com/edit/stackblitz-starters-scbktp?file=src%2Fmain.ts">StackBlitz</a>:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-scbktp%3Fctl%3D1%26devToolsHeight%3D60%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-scbktp%3Fctl%3D1%26devToolsHeight%3D60%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-scbktp%2Faec2a16799ea2d4f266e40a891490892&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/01c5c1f62e9a26723cb00f6b3a9370bb/href">https://medium.com/media/01c5c1f62e9a26723cb00f6b3a9370bb/href</a></iframe><p>Or create a proxy that can watch Signal reads without effect():</p><pre>function watchSignalReads&lt;T, M extends Signal&lt;T&gt; | WritableSignal&lt;T&gt;&gt;(s: M): M {<br>  const node = s[SIGNAL];<br>  const newGetter = () =&gt; {<br>    const value = s();<br>    console.log(&#39;Read:&#39;, value);<br>    return value;<br>  };<br>  (newGetter as any)[SIGNAL] = node;<br>  if (s.hasOwnProperty(&#39;set&#39;)) {<br>    const w = s as WritableSignal&lt;T&gt;;<br>    newGetter.set = w.set;<br>    newGetter.update = w.update;<br>    newGetter.asReadonly = w.asReadonly;<br>  }<br><br>  return newGetter as M;<br>}</pre><p>Usage <a href="https://stackblitz.com/edit/stackblitz-starters-jn29gy?devToolsHeight=70&amp;file=src%2Fmain.ts">example</a>:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-jn29gy%3Fctl%3D1%26devToolsHeight%3D60%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;display_name=StackBlitz&amp;url=https%3A%2F%2Fstackblitz.com%2Fedit%2Fstackblitz-starters-jn29gy%3Fctl%3D1%26devToolsHeight%3D60%26embed%3D1%26file%3Dsrc%252Fmain.ts%26hideExplorer%3D1%26theme%3Dlight%26view%3Dpreview&amp;image=https%3A%2F%2Fsocial-img.staticblitz.com%2Fprojects%2Fstackblitz-starters-jn29gy%2Fadac0ab9cbf94d1a6ed84a08260e57c2&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=stackblitz" width="745" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/dfd976f5d8ba597f9ab5a5d27adad862/href">https://medium.com/media/dfd976f5d8ba597f9ab5a5d27adad862/href</a></iframe><p>Again, I hope you won’t even consider using this in “production,” and instead, I hope you’ll utilize it to create some amazing tools, gain recognition, and contribute to enriching the Angular ecosystem 😎</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=42e4c3afba04" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>