<?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 Thomas Laforge on Medium]]></title>
        <description><![CDATA[Stories by Thomas Laforge on Medium]]></description>
        <link>https://medium.com/@thomas.laforge?source=rss-c8632e31eb8c------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*jeh725a8SBIMC5oVkf-WtQ.png</url>
            <title>Stories by Thomas Laforge on Medium</title>
            <link>https://medium.com/@thomas.laforge?source=rss-c8632e31eb8c------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 06 Jul 2026 13:33:01 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@thomas.laforge/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[Debugging Angular Signals: A Guide to the Signal Graph DevTool]]></title>
            <link>https://medium.com/ngconf/debugging-angular-signals-a-guide-to-the-signal-graph-devtool-b0b69804496d?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/b0b69804496d</guid>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Thu, 19 Mar 2026 12:31:00 GMT</pubDate>
            <atom:updated>2026-03-19T12:31:00.874Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sNX1ZJ3qQv6zzCSOuq-5pQ.png" /></figure><p>Thanks to <a href="https://medium.com/u/38e5be4d04a1">Pham Huu Hien</a> and the excellent solutions provided in the <strong>Angular Challenges</strong> series, we have a clear look at how to leverage the Signal Graph within Angular DevTools. Angular offers an incredible suite of tools for developers, and today I’ll showcase the Signal Graph by walking through the resolution of <a href="https://angular-challenges.vercel.app/challenges/signal/50-bug-effect-signal/"><strong>Challenge #50</strong></a>.</p><h3>The Scenario: A Bug in the MacBook Configurator</h3><p>In Challenge #50, the goal is to debug a strange behavior in an effect.</p><p>The application is a simple checkout page for a MacBook. Users can upgrade their machine with extras like more drive space, RAM, or a better GPU. The requirement is simple: <strong>display an alert whenever at least one checkbox is checked.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Gr1qwnOzqGtxIhL9" /><figcaption>application display</figcaption></figure><h3>The Bug</h3><p>The current implementation doesn’t work as expected. While it seems to work initially, your team discovered a flaw:</p><ul><li>If you check the first checkbox (“Drive”), the alert appears.</li><li><strong>However</strong>, while “Drive” is checked, checking or unchecking the other two boxes (“RAM” or “GPU”) does <strong>not</strong> trigger the alert.</li></ul><p>Why is the effect ignoring the other signals?</p><h3>The Initial Implementation</h3><p>Here is the code causing the issue:</p><pre>effect(() =&gt; {<br>  if (this.drive() || this.ram() || this.gpu()) {<br>    alert(&#39;Price increased!&#39;);<br>  }<br>});</pre><p>We have three signals. When this.drive() is true, the effect seems to &quot;lose track&quot; of the other two.</p><h3>Visualizing the Issue with Angular DevTools</h3><p>To debug this, we can use the <strong>Angular DevTools</strong> extension, which includes a powerful <strong>Signal Graph</strong> representation.</p><blockquote><strong><em>Note:</em></strong><em> If you haven’t installed Angular DevTools yet, you can download the Chrome extension </em><a href="https://chromewebstore.google.com/detail/angular-devtools/ienfalfjdbdpebioblfackkekamfmbnh?pli=1"><em>here</em></a><em>.</em></blockquote><p>Once installed, launch the application:</p><pre>npx nx serve signal-bug-in-effect</pre><p>Open your browser’s Developer Tools (<strong>F12</strong>), select the <strong>Angular</strong> tab, and navigate to the <strong>Signal Graph</strong> section.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vAYMYPeQ7pBBNFsBGWzpfA.png" /><figcaption>angular signal graph devtool</figcaption></figure><h3>Analyzing the Graph</h3><p>Initially, when no checkboxes are checked, you will see that the effect is linked to all three signals: drive, ram, and gpu. <em>(red arrows below)</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QPkGfWoU7JyVltGslZqG7w.png" /><figcaption>effect linked to all three signals</figcaption></figure><p>Now, click the first checkbox (<strong>Drive</strong>). The graph updates, and you’ll notice something strange: <strong>the effect is now only listening to </strong><strong>drive.</strong> The connection lines to ram and gpu have disappeared!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*40BiVYnk6H2u-sT03w3JYA.png" /><figcaption>effect only listen to drive when drive is checked</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9kpO-WfEBB22xCpZPL8_xg.png" /><figcaption>Ram is disconnected even if we check it</figcaption></figure><h3>Why is this happening? (Dynamic Dependency Tracking)</h3><p>This happens because of how Angular Signals track dependencies. Angular effects are <strong>dynamic</strong>. They only register a dependency for signals that are <em>actually read</em> during the execution of the effect.</p><p>Look at the condition again: if (this.drive() || this.ram() || this.gpu())</p><p>Because of <strong>JavaScript short-circuiting</strong>, if this.drive() returns true, the browser never evaluates the rest of the expression. Since this.ram() and this.gpu() are never called, the effect &quot;unsubscribes&quot; from them to save resources. Angular assumes that since you didn&#39;t read them, you don&#39;t care about their changes.</p><h3>The Solution: Explicit Dependency Registration</h3><p>To fix this, we need to ensure the effect reads all three signals regardless of their values. The best practice is to “register” your signals at the top of the effect before any conditional logic.</p><pre>effect(() =&gt; {<br>  // Read all signals upfront to register them as dependencies<br>  const drive = this.drive();<br>  const ram = this.ram();<br>  const gpu = this.gpu();<br><br>  if (drive || ram || gpu) {<br>    alert(&#39;Price increased!&#39;);<br>  }<br>});</pre><p><strong>A good rule of thumb:</strong> Always explicitly define your signal dependencies at the top of an effect. This makes the code easier to read, easier to debug, and ensures your logic stays reactive to every change you intend to track.</p><p>Now, if you check the Signal Graph again, you will see the effect remains linked to all three signals, regardless of their boolean state.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MzmLggea7XzeeYGvoZwBCw.png" /><figcaption>bug fixed: effect is listening to all three signals</figcaption></figure><p>I hope this helps you understand the “magic” behind signal tracking.</p><p>✨ “Hungry for more? Dive into more hands-on practice with the <a href="https://angular-challenges.vercel.app/"><strong>Angular Challenges</strong></a> suite here. Happy coding! ✨</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b0b69804496d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/ngconf/debugging-angular-signals-a-guide-to-the-signal-graph-devtool-b0b69804496d">Debugging Angular Signals: A Guide to the Signal Graph DevTool</a> was originally published in <a href="https://medium.com/ngconf">ngconf</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Become a 10x developer with Git Worktree]]></title>
            <link>https://medium.com/@thomas.laforge/become-a-10x-developer-with-git-worktree-ddda5c345af1?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/ddda5c345af1</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Tue, 24 Feb 2026 10:53:03 GMT</pubDate>
            <atom:updated>2026-02-24T10:53:03.322Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J4eDus6RR285D_sW4guMKQ.png" /></figure><p>Have you ever done this?</p><pre>git stash -m &quot;current task&quot;<br>git checkout hotfix-branch<br># fix bug<br>git checkout main<br>git stash pop</pre><p>It works.</p><p>But it’s fragile.</p><ul><li>Your IDE undo history is gone</li><li>Your dev server restarts</li><li>Build artifacts disappear</li><li>Multiple stashes become unmanageable</li><li>You mentally context-switch twice</li></ul><p>This is not a productivity workflow.</p><h3>The Real Problem</h3><p>Git assumes you only work in <strong>one working directory at a time</strong>.</p><p>Modern development doesn’t.</p><p>You:</p><ul><li>Handle hotfixes</li><li>Review PRs</li><li>Work on features</li><li>Run AI tools generating code</li><li>Experiment in parallel</li></ul><p>Trying to serialize all that into one directory creates friction.</p><h3>The Hidden Gem: Git Worktree</h3><p>Git allows multiple working directories connected to the same repository.</p><p>Instead of switching branches, you add a new directory:</p><pre>git worktree add ../my-project-hotfix hotfix-branch</pre><p>Now you have:</p><ul><li>Your feature branch open</li><li>A hotfix branch in another folder</li><li>Independent IDE windows</li><li>Independent dev servers</li><li>Zero interference</li></ul><p>No stash. No resets. No friction.</p><h3>Useful Commands</h3><pre>git worktree list<br>git worktree remove &lt;path&gt;<br>git worktree prune</pre><h3>The Pro Setup: Bare Repository</h3><p>If you want a clean setup:</p><pre>git clone --bare https://github.com/user/my-repo.git my-repo<br>cd my-repo<br>git worktree add main<br>git worktree add feature-xyz</pre><p>Structure:</p><pre>my-repo/<br>├── HEAD (bare repo)<br>├── main/<br>└── feature-xyz/</pre><p>Think of the bare repo as the <strong>Git brain</strong>, and each worktree as an independent body.</p><p>Each worktree:</p><ul><li>Has its own node_modules</li><li>Has its own build output</li><li>Has its own process lifecycle</li></ul><p>This eliminates parent/child directory confusion.</p><h3>The 10x Workflow: Multi-LLM Parallelism</h3><p>Here’s where it becomes powerful.</p><p>Each worktree is fully independent.</p><p>You can:</p><ul><li>Worktree A → Manually fix a bug</li><li>Worktree B → Let Claude generate documentation</li><li>Worktree C → Let Copilot generate tests</li><li>Worktree D → Refactor with Gemini</li></ul><p>Instead of waiting for AI to finish, you work in parallel.</p><p>You eliminate idle time.</p><p>You transform development from sequential to concurrent.</p><p>That’s where the real productivity gain comes from.</p><h3>Cleaning Up</h3><p>Proper way:</p><pre>git worktree remove ../my-project-hotfix</pre><p>Quick way (if deleted manually):</p><pre>git worktree prune</pre><p>Modern IDEs like VS Code now support worktrees natively in the Source Control panel.</p><h3>Conclusion</h3><p>Git Worktree are not a trick.</p><p>They are a structural productivity upgrade.</p><p>With modern AI workflows, running everything in one directory is an artificial limitation.</p><p>Parallel work is the future.</p><p>And Git already supports it. 💪</p><p>Enjoy building faster…</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ddda5c345af1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Discovering Angular’s Hidden EventManagerPlugin]]></title>
            <link>https://medium.com/@thomas.laforge/discovering-angulars-hidden-eventmanagerplugin-d7fba31ffe78?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/d7fba31ffe78</guid>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Mon, 10 Nov 2025 16:41:41 GMT</pubDate>
            <atom:updated>2025-11-10T16:41:41.105Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*QqbSeUO8SbIsEPFbu91jYA.png" /></figure><p>Have you ever written something like this?</p><pre>@Component({<br>  template: `&lt;button (click)=&quot;closeDialog($event)&quot;&gt;Close&lt;/button&gt;`<br>})<br>export class Dialog {<br>  public closeDialog(event: Event) {<br>    event.stopPropagation();<br>    // ... do some action<br>  }<br>}</pre><p>It works… but you can do better. Let’s uncover a hidden gem in Angular.</p><h3>The secret EventManagerPlugin API</h3><p>Here’s the definition from Angular’s documentation:</p><pre>abstract class EventManagerPlugin {<br>  constructor(_doc: any): EventManagerPlugin;<br>  manager: EventManager;<br>  abstract supports(eventName: string): boolean;<br>  abstract addEventListener(<br>    element: HTMLElement,<br>    eventName: string,<br>    handler: Function,<br>    options?: ListenerOptions<br>  ): Function;<br>}</pre><p>Every time you write something like (click)=&quot;...&quot;, Angular uses its <strong>EventManager</strong> to find a matching plugin that “supports” that event.</p><p>That means: you can <strong>create your own</strong>.</p><p>That’s all for today, enjoy this discovery 😁.</p><p>…</p><p>Don’t worry, I will not let you do it alone. Let’s refactor our example to see how it works in practice.</p><h3>Building a .stop Event Plugin</h3><p>Let’s create a plugin that automatically stops event propagation whenever we add .stop to an event name.</p><pre>import { EventManagerPlugin } from &#39;@angular/platform-browser&#39;;</pre><pre>export class StopEventPlugin extends EventManagerPlugin {<br>  protected readonly modifier = &#39;.stop&#39;;<br><br>  supports(event: string): boolean {<br>    return event.includes(this.modifier);<br>  }<br><br>  addEventListener(element: HTMLElement, event: string, handler: Function): Function {<br>    return this.manager.addEventListener(element, this.unwrap(event), (e: Event): void =&gt; {<br>      e.stopPropagation();<br>      handler(e);<br>    });<br>  }<br><br>  private unwrap(event: string): string {<br>    return event<br>      .split(&#39;.&#39;)<br>      .filter((v) =&gt; !this.modifier.includes(v))<br>      .join(&#39;.&#39;);<br>  }<br>}</pre><p>With this implementation, we tell Angular that whenever it encounters (click.stop), it should execute the logic inside addEventListener — calling e.stopPropagation() before running the provided handler.</p><p>However, this code alone won’t work out of the box. We need to <em>provide</em> it to our Angular app.</p><h3>Providing it in your app</h3><p>To activate the plugin globally, provide it at the root of your app:</p><pre>import { EVENT_MANAGER_PLUGINS, makeEnvironmentProviders, EnvironmentProviders } from &#39;@angular/core&#39;;<br>import { StopEventPlugin } from &#39;./stop-event.plugin&#39;;</pre><pre>export function provideEventPlugins(): EnvironmentProviders {<br>  return makeEnvironmentProviders([<br>    {<br>      provide: EVENT_MANAGER_PLUGINS,<br>      multi: true,<br>      useClass: StopEventPlugin,<br>    },<br>  ]);<br>}</pre><p>Then, call it inside your main.ts:</p><pre>bootstrapApplication(AppComponent, {<br>  providers: [provideEventPlugins()],<br>});</pre><p>Done ✅</p><p>Now we can rewrite the introduction example as follow:</p><pre>@Component({<br>  template: `&lt;button (click.stop)=&quot;closeDialog()&quot;&gt;Close&lt;/button&gt;`<br>})<br>export class Dialog {<br>  public closeDialog() {<br>    // ... do some action<br>  }<br>}</pre><p>Isn’t that nice and beautiful?</p><h3>WebStorm tip 🧠</h3><p>If you’re using WebStorm, .stop won’t be recognized by the TypeScript compiler.<br> You can fix that by adding a web-types.json file at the root of your project:</p><pre>{<br>  &quot;$schema&quot;: &quot;https://raw.githubusercontent.com/JetBrains/web-types/master/v2-preview/web-types.json&quot;,<br>  &quot;name&quot;: &quot;my-custom-events&quot;,<br>  &quot;framework&quot;: &quot;angular&quot;,<br>  &quot;version&quot;: &quot;1.0.0&quot;,<br>  &quot;contributions&quot;: {<br>    &quot;js&quot;: {<br>      &quot;ng-custom-events&quot;: [<br>        {<br>          &quot;name&quot;: &quot;Custom modifiers for declarative events handling&quot;,<br>          &quot;pattern&quot;: {<br>            &quot;template&quot;: [<br>              { &quot;items&quot;: { &quot;path&quot;: &quot;/js/events&quot; } },<br>              {<br>                &quot;items&quot;: &quot;ng-event-plugins-modifiers&quot;,<br>                &quot;template&quot;: [&quot;.&quot;, &quot;#...&quot;, &quot;#item:modifiers&quot;],<br>                &quot;repeat&quot;: true<br>              }<br>            ]<br>          },<br>          &quot;ng-event-plugins-modifiers&quot;: [{ &quot;name&quot;: &quot;stop&quot; }]<br>        }<br>      ]<br>    }<br>  }<br>}</pre><p>Now WebStorm will autocomplete (click.stop) like a native event modifier.</p><h3>Bonus ideas 💡</h3><p>Once you understand the pattern, you can build more modifiers such as:</p><ul><li>.prevent → calls event.preventDefault()</li><li>.outside → runs the handler outside of zone.js</li><li>or any other plugin that fits your needs.</li></ul><h4>The power is in your hands.</h4><p>✨ That’s it — now you know one of Angular’s most powerful and least-known extension points.</p><p>If you enjoyed this, check out my other deep dives into Angular internals<br> <a href="https://medium.com/@thomas.laforge/everything-about-ng-content-ca0c12b2e02b"><em>Everything about ng-content</em></a> or <a href="https://medium.com/ngconf/how-angular-dependency-injection-works-under-the-hood-cc210f7040bd"><em>How Angular Dependency Injection works under the hood</em></a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d7fba31ffe78" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Everything about <ng-content/>]]></title>
            <link>https://medium.com/@thomas.laforge/everything-about-ng-content-ca0c12b2e02b?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/ca0c12b2e02b</guid>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Mon, 27 Oct 2025 19:21:37 GMT</pubDate>
            <atom:updated>2025-10-27T19:21:37.271Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*pdVurj_ZOxjfAjnR86eVAQ.png" /></figure><p>If you’ve ever built a reusable Angular component and wished you could <em>just drop content inside</em> instead of passing endless inputs(), then you want to master &lt;ng-content&gt;.</p><p>Let’s talk about <strong>content projection</strong> — how it works, how to use multiple slots, what’s pretty new with default fallbacks, and that mysterious ngProjectAs.</p><h3>What is &lt;ng-content&gt;</h3><p>Content projection lets a component <em>open a hole</em> in its template — a place where the parent component can inject content.</p><pre>@Component({<br>  selector: &#39;app-card&#39;,<br>  template: `<br>    &lt;div class=&quot;card&quot;&gt;<br>      &lt;ng-content/&gt;<br>    &lt;/div&gt;<br>  `<br>})<br>export class CardComponent {}</pre><p>Then use it like this:</p><pre>&lt;app-card&gt;<br>  &lt;h2&gt;Angular Content Projection&lt;/h2&gt;<br>  &lt;p&gt;Reusable components made easy.&lt;/p&gt;<br>  &lt;my-child-comp/&gt;<br>&lt;/app-card&gt;</pre><p>Everything between &lt;app-card&gt; and &lt;/app-card&gt; gets projected inside &lt;ng-content&gt;.</p><p>That’s it. You just made a <em>content container</em>.<br> Think of it as the Angular way of saying: “whatever’s inside me, put it right here.”</p><h3>Multiple &lt;ng-content&gt; slots</h3><p>Sometimes one slot isn’t enough.<br> You want a header, a footer, and a body.<br> Angular’s solution: use multiple &lt;ng-content&gt; elements with a select attribute.</p><pre>@Component({<br>  selector: &#39;my-comp-dialog&#39;,<br>  template: `<br>    &lt;div class=&quot;dialog&quot;&gt;<br>      &lt;div class=&quot;dialog-header&quot;&gt;<br>        &lt;ng-content select=&quot;[header]&quot;/&gt;<br>      &lt;/div&gt;<br>      &lt;div class=&quot;dialog-body&quot;&gt;<br>        &lt;ng-content/&gt;<br>      &lt;/div&gt;<br>      &lt;div class=&quot;dialog-footer&quot;&gt;<br>        &lt;ng-content select=&quot;[footer]&quot;/&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>  `<br>})<br>export class Dialog {}</pre><p>Usage:</p><pre>&lt;my-comp-dialog&gt;<br>  &lt;div header&gt;&lt;h3&gt;John Doe&lt;/h3&gt;&lt;/div&gt;<br>  &lt;p&gt;Frontend Engineer at Angular Corp.&lt;/p&gt;<br>  &lt;div footer&gt;&lt;button&gt;Edit&lt;/button&gt;&lt;/div&gt;<br>&lt;/my-comp-dialog&gt;</pre><p>Each &lt;ng-content&gt; acts like a <strong>target</strong> — Angular matches elements from the parent template using CSS selectors.</p><ul><li>[header] → goes to the header slot</li><li>[footer] → goes to the footer slot</li><li>Everything else → goes to the default slot</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/259/1*KqI7svbT62oIwyR31pXX-Q.png" /><figcaption>The UI vizualiazation</figcaption></figure><p><strong>Notes:</strong> You can create a ng-content with multiple select and be very creative</p><pre>&lt;ng-content select=&quot;[header], ng-template[MyDirective], .my-css-class&quot;/&gt;<br>&lt;ng-content select=&quot;button[myCompanyButton]:not([addToDefaultFooter])&quot;/&gt;</pre><h3>Default fallback content (Angular v18+)</h3><p>What if no one provides content for a slot?</p><p>In <strong>Angular 18+</strong>, &lt;ng-content&gt; can include <strong>default content</strong> directly inside.<br> If nothing is projected, Angular renders the fallback.</p><pre>@Component({<br>  selector: &#39;my-comp-dialog&#39;,<br>  template: `<br>    &lt;div class=&quot;dialog-header&quot;&gt;<br>      &lt;ng-content select=&quot;[header]&quot;&gt;Default Title&lt;/ng-content&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;dialog-body&quot;&gt;<br>      &lt;ng-content&gt;Default body content&lt;/ng-content&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;dialog-footer&quot;&gt;<br>      &lt;ng-content select=&quot;[footer]&quot;&gt;<br>        &lt;button&gt;Upgrade&lt;/button&gt;<br>      &lt;/ng-content&gt;<br>    &lt;/div&gt;<br>  `<br>})<br>export class Dialog {}</pre><p>Usage:</p><pre>&lt;my-comp-dialog&gt;<br>  &lt;div header&gt;&lt;h2&gt;Custom Header&lt;/h2&gt;&lt;/div&gt;<br>&lt;/my-comp-dialog&gt;</pre><p>Output:</p><ul><li>Header → custom</li><li>Body → “Default body content”</li><li>Footer → &lt;button&gt;Upgrade&lt;/button&gt;</li></ul><p>Fallbacks make your components <strong>safer by default</strong> — they “just work” even when users forget to project content.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/179/1*E8JiDlBaQ9CLYNW2PldMHQ.png" /><figcaption>The UI vizualiazation</figcaption></figure><h3>Extra: ngProjectAs</h3><h4>Case 1</h4><p>You may run into this: your child component expects a specific selector, but your parent uses something else.</p><pre>@Component({<br>  selector: &#39;my-comp-dialog&#39;,<br>  template: `<br>    &lt;ng-content select=&quot;card-title&quot;/&gt;<br>    &lt;ng-content/&gt;<br>  `<br>})<br>export class Dialog {}</pre><p>To match select=&quot;card-title&quot;, you’d normally have to write:</p><pre>&lt;my-comp-dialog&gt;<br>  &lt;card-title&gt;Angular FTW&lt;/card-title&gt;<br>&lt;/my-comp-dialog&gt;</pre><p>…but that’s not ideal. You probably want to use a semantic &lt;h2&gt; instead.</p><p>Here’s where ngProjectAs comes in:</p><pre>&lt;my-comp-dialog&gt;<br>  &lt;h2 ngProjectAs=&quot;card-title&quot;&gt;Angular FTW&lt;/h2&gt;<br>  &lt;p&gt;Now it works 🎉&lt;/p&gt;<br>&lt;/my-comp-dialog&gt;</pre><p>It doesn’t change the DOM — just the projection logic.<br> You keep semantic HTML <em>and</em> satisfy your slot selectors.</p><h4>Case 2</h4><p>You have a two-level projection</p><pre>@Component({<br>  selector: &#39;my-parent-dialog&#39;,<br>  template: `<br>    &lt;h1&gt;Parent Dialog&lt;/h1&gt;<br>    &lt;my-child-dialog&gt;<br>      &lt;ng-content select=&quot;[body]&quot; ngProjectAs=&quot;[dialog-body]&quot;/&gt;<br>    &lt;/my-child-dialog&gt;<br>  `<br>})<br>export class ParentDialog {}</pre><pre>@Component({<br>  selector: &#39;my-child-dialog&#39;,<br>  template: `<br>    &lt;h2&gt;Child Dialog&lt;/h2&gt;<br>    &lt;ng-content select=&quot;[dialog-body]&quot;/&gt;<br>  `<br>})<br>export class ChildDialog {}</pre><p>Usage:</p><pre>&lt;my-parent-dialog&gt;<br>  &lt;div body&gt;body of dialog&lt;/div&gt;<br>&lt;/my-parent-dialog&gt;</pre><p>You will get the following result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/214/1*xwezacZBYY4uoRP13AI8kA.png" /></figure><p>You now have all the tools to create beautiful and customizable components. No more excuses.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ca0c12b2e02b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Static imports of lazy-loaded libraries are forbidden.]]></title>
            <link>https://medium.com/ngconf/static-imports-of-lazy-loaded-libraries-are-forbidden-8f965032bf03?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/8f965032bf03</guid>
            <category><![CDATA[eslint]]></category>
            <category><![CDATA[nx]]></category>
            <category><![CDATA[ngconf]]></category>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Thu, 01 Feb 2024 13:11:41 GMT</pubDate>
            <atom:updated>2024-02-01T13:11:41.385Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*oVU-fQGtjy4YqlTgBWqjew.png" /></figure><p>If you are using Nx, you might have already encountered this issue. This Eslint rule comes from the package @nx/enforce-module-boundaries developed by the Nx team.</p><blockquote>If you don’t know or you are not using Nx, this article can still be valuable and you might learn something from it.</blockquote><h3>Context</h3><p>Nx is a workspace organizer. You will create folders called libraries to organize your code, and you will expose components and functions to be used outside of the library. <strong>This error happens when you try to lazy import an element and at the same time you eager import another element from the same library.</strong></p><p>In this article, we will see what this error really means and the side effect that this error is preventing. Lastly we will see how to resolve it.</p><h3>Setup</h3><p>First let’s create a small example to better illustrate the issue.</p><p>We create a dashboard application with the following architecture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/592/1*jOGpOhtvh7uqDjy8Vcjmug.png" /></figure><p>In the apps folder, we have our application entry point. AppComponent is the root component.</p><p>In the libs folder, we have one library called users containing two components:</p><ul><li>UsersDashboardComponent is a table listing all users. <em>(It uses Angular Material MatTable.)</em></li><li>UserComponent is a component displaying an icon and the user’s fullname. <em>(It uses MatIcon)</em></li><li>index.ts is our library entry point where both component are exposed.</li></ul><pre>// index.ts<br>export { UserComponent } from &#39;./lib/user.component&#39;;<br>export { default } from &#39;./lib/users-dashboard.component&#39;;</pre><p>AppComponent is eager referencing UserComponent and it’s lazy-loading UsersDashboardComponent as shown below:</p><pre>import {<br>  UserComponent,<br>  type User,<br>} from &#39;@angular-challenges/static-dynamic-import/users&#39;;<br>// ^Where the ESlint error is shown!!!<br><br><br>@Component({<br>  imports: [UserComponent, RouterOutlet],<br>  template: `<br>    Author:<br>    &lt;sdi-user [user]=&quot;author&quot; /&gt;<br>    &lt;router-outlet /&gt;<br>  `,<br>   ...<br>})<br>export class AppComponent {<br>  author: User = {...};<br>}</pre><p>Where the router is defined as follow:</p><pre>provideRouter([<br>  {<br>    path: &#39;&#39;,<br>    loadComponent: () =&gt;<br>      import(&#39;@angular-challenges/static-dynamic-import/users&#39;),<br>  },<br>]),</pre><p>We can now clearly see that users library is loaded statically and dynamically.</p><p>However at serve/build time, Typescript is not showing errors and the application is running correctly. So, why do we need to create an ESlint error to guard us from this issue and what is the side effect of importing a library statically and dynamically at the same time ?</p><h3>Side-Effect</h3><p>To better understand the issue, we will inspect the bundle of the application’s build. The tool source-map-explorer will be of great help.</p><p>Let’s build our application using the production configuration and setting sourceMap to true.</p><p>After the build has completed, we can see this summary printed in the console:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/598/1*weiUhQGseJ5hiRUym6mVXg.png" /><figcaption>build bundle size</figcaption></figure><p>Let’s explore what is inside the main bundle by opening the chunk inside source-map-explorer by re-running the following command:</p><pre>npx source-map-explorer [PATH_DIST_FOLDER]/*.js</pre><p>This command will give you the following visualization:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*473BdFpI0qgmWbhDmRM6tA.png" /><figcaption>main bundle exploration</figcaption></figure><p>By opening the source-map of the main chunk, we can see that MatTable and MatIcon are bundled in the same chunk, although MatTable is only used inside UserDashboardComponent which is lazy-loaded. We would have expected that this component would be bundled inside the lazy-loaded one.</p><p>This small example clearly shows that the compiler is bundling the entire library inside the main bundle despite using only a small portion of it.</p><h3>Solution</h3><p>The solution is straight forward. We only have to create a new library and move our UserComponent. We will now have two libraries. One which will be lazy loaded, and a shared one which can be eager loaded anywhere in our code.</p><p>Let’s rebuild our refactored application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/551/1*xpKV0AaHLT38PDnt1XMoNg.png" /><figcaption>build summary</figcaption></figure><p>and rerun source-map-explorer</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hBke0QCmiSHNZOzCbrZHgA.png" /><figcaption>main bundle containing only MatIcon</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wonfm1oNlLjFaii78nYdJQ.png" /><figcaption>Lazy-loaded bundle containing MatTable</figcaption></figure><p>The compiler divided our component successfully. MatTable is now located inside the lazy-loaded component and it will not increase the main bundle unnecessarily.</p><p>If you want to play with this example and try it on your own, I created a <a href="https://angular-challenges.vercel.app/challenges/nx/42-static-dynamic-import/">challenge</a> inside <a href="https://github.com/tomalaforge/angular-challenges">AngularChallenges</a>.</p><p>You can find me on <a href="https://medium.com/@thomas.laforge">Medium</a>, <a href="https://twitter.com/laforge_toma">Twitter</a> or <a href="https://github.com/tomalaforge">Github</a>. Don’t hesitate to reach out to me if you have any questions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8f965032bf03" width="1" height="1" alt=""><hr><p><a href="https://medium.com/ngconf/static-imports-of-lazy-loaded-libraries-are-forbidden-8f965032bf03">Static imports of lazy-loaded libraries are forbidden.</a> was originally published in <a href="https://medium.com/ngconf">ngconf</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Angular Dependency Injection works under the hood]]></title>
            <link>https://medium.com/ngconf/how-angular-dependency-injection-works-under-the-hood-cc210f7040bd?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/cc210f7040bd</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[angular-17]]></category>
            <category><![CDATA[ngconf2024]]></category>
            <category><![CDATA[dependency-injection]]></category>
            <category><![CDATA[ngconf]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Thu, 16 Nov 2023 13:16:01 GMT</pubDate>
            <atom:updated>2023-11-24T07:06:06.789Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*6VTqINbVq0j4HTW51DGDcw.png" /></figure><p>Dependency Injection (DI) is one of the most beloved and powerful features of Angular, and it happens to be my personal favorite as well. Understanding and mastering it can elevate your Angular skills and grant you superpowers.</p><p>In this article, I will explain what Dependency Injection is and delve into how it operates within Angular to provide a profound understanding.</p><h3>What is a Dependency Injection</h3><p>Let’s start by examining an example that doesn’t use Dependency Injection:</p><pre>@Component({<br>   //...<br>})<br>export class AppComponent {<br>  service = new RootService();<br>}</pre><p>In this example, we directly instantiate the RootService using the new keyword, resulting in a hardcoded dependency and a tight coupling between AppComponent and RootService. While this approach does work, it lacks flexibility, testability, and scalability in the long run, making it less maintainable.</p><p>Now, let’s consider the same example using Dependency Injection, where you’ll recognise a well-known Angular code snippet:</p><pre>@Component({<br>   //...<br>})<br>export class AppComponent {<br>  service = inject(RootService);<br>// constructor(private service: RootService) {}<br>}</pre><blockquote><strong>Notes:</strong> you can use either the constructor or the inject function, as both methods have the same underlying implementation.</blockquote><p>As we can see, AppComponent is no longer directly responsible for instantiating RootService. Instead, it delegates this task to an external source, which is responsible for either returning an existing instance or creating a new instance of the requested service.</p><p>We can simplify the code for this external source, which might look like this:</p><pre><br>export const inject = (searchClass: Class) =&gt; {<br>  const dependance = find(searchClass)<br>  if(dependance) {<br>    return dependance;<br>  } else {<br>    return new searchClass();<br>  }<br>}</pre><p>In this example, AppComponent doesn’t need to have knowledge about RootService. This reduces the coupling between classes and their dependencies, making the code more maintainable, testable, and reusable.</p><p>In Angular, this external source is referred to as an <strong>Injector. </strong>And its implementation can be compared to a dictionary of records. The structure of a record looks like this:</p><pre>record:{<br> //...<br> [index]:{<br>   key: class RootService,<br>   value: {<br>    factory: ƒ RootService_Factory(t),<br>    value: {}<br>   }<br> //...<br>}</pre><p>The Injector stores information about all injectable classes, which includes anything with a decorator such as @Injectable, @Component, @Pipe, and @Directive.</p><p>Returning to the previous example, when AppComponent requests RootService, the Injector iterates over its records to locate the requested token. Once found, the Injector returns the value if it&#39;s not undefined, indicating that the service has already been instantiated. Otherwise, the Injector creates a new instance using the factory function.</p><p>As you can observe, the record is simply an object, and the value can be easily overridden. For example, if we write the following code:</p><pre>@Component({<br>   //...<br>   providers: [{ provide: RootService, useClass: OtherService }]<br>})<br>export class AppComponent {<br>  service = inject(RootService);<br>}</pre><p>The Injector will override the value property within the RootService record:</p><pre>record:{<br> //...<br> [index]:{<br>   key: class RootService,<br>   value: {<br>    factory: ƒ OtherService_Factory(t),<br>    value: {}<br>   }<br> //...<br>}</pre><p>This means that when AppComponent requests RootService, the Injector will provide a new instance of OtherService.</p><blockquote><strong>Note:</strong> This example simplifies how Angular’s Dependency Injection works, but it illustrates the underlying DI principle.</blockquote><p>The next section delves into more advanced aspects, revealing the inner workings of Angular’s DI system.</p><h3>Angular Dependency Injection</h3><p>Angular has two categories of Injectors:</p><ol><li><strong>EnvironmentInjector:</strong> This category includes all global injectable classes provided through the router, modules, or using the providedIn: &#39;root&#39; keyword.</li><li><strong>NodeInjector:</strong> This category contains all local injectable classes found in each component or template.</li></ol><p>It’s important to note that each small piece of a view containing injectable classes (referred to as <strong>LView</strong>) has its own NodeInjector, and within this NodeInjector, we can locate all services provided within the component provider array or any directives used within that LView.</p><blockquote><strong>LView !== Component</strong></blockquote><h4>Creation of EnvironmentInjector Tree</h4><p>When we bootstrap the application, the bootstrapApplication function is called in our main.ts file. This function takes two parameters:</p><ul><li>The root Component</li><li>A list of providers</li></ul><pre>bootstrapApplication(AppComponent, {<br>  providers: [GlobalService],<br>})</pre><p>Under the hood, this function will create three EnvironmentInjectors chained together:</p><ul><li><strong>NullInjector:</strong> This is the end of the road. Its sole purpose is to throw an error: <em>“NullInjectorError: No provider for …!!!”</em></li><li><strong>PlatformInjector</strong>: It contains a list of tokens that inform Angular about the platform the application is running on, such as browser, server, web worker, etc.</li></ul><p><strong>Example:</strong> this is where the InjectionToken DOCUMENT is created. For instance, if you are on a browser, this token will return window.document, whereas on a server, Angular will build and provide a DOM using <a href="http://scripts/test/setupLocalMongoDbDocker.sh">Domino</a>. It&#39;s crucial to always work with the DOCUMENT token by injecting it instead of using window.document. This ensures compatibility if you ever need to render your application from a server.</p><pre>import { DOCUMENT } from &#39;@angular/common&#39;;<br><br>@Component()<br>export class FooComponent {<br>  document = inject(DOCUMENT) // ✅<br>  document = window.document // ❌<br>}</pre><ul><li><strong>RootInjector</strong>: This is the most well-known of the three. It’s where all our global services <em>(injectables set as root)</em> are stored.</li></ul><p><strong>Notes:</strong> If we refer back to the earlier example, the GlobalService instance will be located within this injector.</p><p>All three of these injectors are chained together.</p><h4>Creation of NodeInjector Tree</h4><p>In this section, we will explore examples that you likely encounter in your daily projects. The first part aims to provide a better understanding of how the NodeInjector tree is created. <em>(The NodeInjectorTree is similar to the ComponentTree but not strictly identical.)</em></p><p>We will then see how Angular determines which dependencies to retrieve or create.</p><blockquote><strong>Note:</strong> In this article, we will not discuss modules since most applications are expected to transition to standalone. Furthermore, all new Angular applications will be set to standalone by default starting from v17.</blockquote><p><strong>Tree Creation</strong></p><p>Let’s examine what a NodeInjectorTree looks like. We’ll begin with a very simple example: a Parent with one Child.</p><pre>@Component({<br>  template: `&lt;child /&gt;`,<br>  imports: [ChildComponent],<br>})<br>export class ParentComponent {}<br><br>@Component({})<br>export class ChildComponent {}</pre><p>This results in the following tree:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/507/1*Z2ScUudShf-g4xEi77eXfQ.png" /></figure><p>Since ParentComponent and ChildComponent are annotated with @Component, it means they are injectable. Thus, each component is stored within its own NodeInjector as follows. It&#39;s important to note that ChildComponent can inject ParentComponent, but it cannot inject itself, as this would create a circular dependency.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/509/1*_-nXiUX1qvt1jhbhmlbYHA.png" /></figure><p>Now, let’s add another child to the parent:</p><pre>@Component({<br>  template: `<br>    &lt;child /&gt;<br>    &lt;child /&gt;<br>   `,<br>  imports: [ChildComponent],<br>})<br>export class ParentComponent {}<br><br>@Component({})<br>export class ChildComponent {}</pre><p>The structure of both trees remains similar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/675/1*n7OCWRous1NqX2Ugke43OA.png" /></figure><p>However, let’s see what happens when we encapsulate one child into a div with a directive on it.</p><pre>@Directive({<br>  selector: &#39;[foo]&#39;,<br>  standalone: true,<br>})<br>export class FooDirective {}<br><br>@Component({<br>  selector: &#39;app-root&#39;,<br>  standalone: true,<br>  imports: [ChildComponent, FooDirective],<br>  template: `<br>    &lt;div foo&gt;<br>      &lt;child /&gt;<br>    &lt;/div&gt;<br>    &lt;child /&gt;<br>  `,<br>})<br>export class ParentComponent {}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/676/1*bFx_27rig8c0iSnnRPkWgg.png" /></figure><p>Now the InjectorTree begins to diverge from the ComponentTree. A new Injector has appeared. Since FooDirective is a type of @Directive, it means it&#39;s injectable, and the first ChildComponent can inject it.</p><p>From this example, we can see that a NodeInjector is not associated with a Component but with an LView (Logical View).</p><p>With these three examples, you have all you need to understand how the InjectorTree is built.</p><p><em>(</em><strong><em>Note:</em></strong><em> Routing and ActivatedRoute will be explained in a follow-up article.)</em></p><p>Now, let’s explore different ways of providing an injectable service and how Angular locates the instance you are injecting.</p><h4>Component provider</h4><p>Within the component decorator, you have a property called providers that allows you to provide an Injectable class, as illustrated below:</p><pre>@Component({<br>  template: `...`,<br>  providers: [MyComponentService],<br>})<br>export class MyComponent {}</pre><p>The service provided inside the decorator will be stored within the records of the NodeInjector of MyComponent. <strong>Please note that providing your service does not instantiate it. A service is instantiated only when it is injected.</strong></p><p>Let’s now examine which instance is returned with two concrete examples:</p><p><strong>Example 1:</strong></p><pre>@Component({<br>  template: `<br>    &lt;child /&gt;<br>    &lt;child /&gt;<br>   `,<br>  imports: [ChildComponent],<br>})<br>export class ParentComponent {}<br><br>@Component({<br>  providers: [MyService]<br>})<br>export class ChildComponent {<br>  myService = inject(MyService);<br>}</pre><p>This results in the following NodeInjectorTree:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/674/1*QMvkwADz2l8bGWKSEQHW_Q.png" /></figure><p>As we can see, MyService is present inside both ChildInjectors. When Angular creates the first ChildComponent class, it will request MyService from the DI system. The DI system will start by searching inside the record of ChildInjector, which looks like this:</p><pre>record:{<br> //...<br> [index]:{<br>   key: class MyService,<br>   value: {<br>    factory: ƒ MyService_Factory(t),<br>    value: undefined<br>   }<br> //...<br>}</pre><p>Angular will iterate over all dictionary entries of the Injector to check if the key MyService is present. Since MyService is present inside this NodeInjector, it will then check if it has already been instantiated, which is not the case since the value is undefined. In this case, a new instance of MyService will be created and returned.</p><p>If the key wasn’t present inside the record, the DI system will move to the next Injector until finding it or reaching the NullInjector, which will throw an error and terminate the application.</p><p>The same process will repeat for the second instance of ChildComponent. Angular will start searching inside its own NodeInjector, find the key inside the record, and since MyService has not been instantiated, a new instance will be created.</p><p><strong>Example 2:</strong></p><p>Now, let’s provide MyService inside ParentComponent instead of inside ChildComponent.</p><pre>@Component({<br>  providers: [MyService]<br>  template: `<br>    &lt;child /&gt;<br>    &lt;child /&gt;<br>   `,<br>  imports: [ChildComponent],<br>})<br>export class ParentComponent {}<br><br>@Component({})<br>export class ChildComponent {<br>  myService = inject(MyService);<br>}</pre><p>Now, MyService is located inside the record of ParentInjector.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/673/1*0IPUyxw-XwkBt8KEQmrVWg.png" /></figure><p>This time, when Angular creates the first ChildComponent, it won&#39;t find the key of MyService inside the record of ChildInjector. Angular will then move up to the next Injector, which is ParentInjector. The record of ParentInjector looks like this:</p><pre>record:{<br> //...<br> [index]:{<br>   key: class MyService,<br>   value: {<br>    factory: ƒ MyService_Factory(t),<br>    value: undefined<br>   }<br> //...<br>}</pre><p>Since MyService has not been instantiated yet, a new instance will be created and returned.</p><p>However, things are different when the second ChildComponent is created. Angular will traverse the NodeInjectorTree until reaching ParentInjector. But this time, the ParentInjector looks like this:</p><pre>record:{<br> //...<br> [index]:{<br>   key: class MyService,<br>   value: {<br>    factory: ƒ MyService_Factory(t),<br>    value: MyService {<br>      prop1: &#39;xxx&#39;<br>      // ...<br>    }<br>   }<br> //...<br>}</pre><p>The value of MyService is no longer undefined. The DI System will return this instance to the second ChildComponent. This means that both ChildComponents are sharing the same instance of MyService, unlike in the previous example.</p><p><strong>Note:</strong> If ParentComponent was injecting MyService, the same instance would be shared among all three components.</p><h4>ProvidedIn: ‘root’</h4><p>The providedIn: &#39;root&#39; is one of the most commonly used injectable designs within Angular applications, but not everyone fully understands the implications of these two words. This chapter aims to provide a clear explanation.</p><p>Let’s create a very basic application with a parent and a child:</p><pre>@Component({<br>  template: `&lt;child /&gt;`,<br>  imports: [ChildComponent],<br>})<br>export class ParentComponent {}<br><br>@Component({})<br>export class ChildComponent {<br>  service = inject(RootService);<br>}<br><br>@Injectable({ providedIn: &#39;root&#39; })<br>export class RootService {}</pre><p>When we examine the NodeInjectorTree, we find that RootService is not present in any of the records. This is because <strong>Angular does not include it in any Injector until a component actually injects it</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/509/1*_-nXiUX1qvt1jhbhmlbYHA.png" /></figure><p><strong>Note</strong>: In the context of lazy-loaded routes, RootService may get tree-shaken and bundled outside the main bundle. This topic is beyond the scope of this article, but you can read more about it below.</p><p><a href="https://itnext.io/mastering-injectable-services-a-comprehensive-guide-6c2c0f5f48a2">Mastering Injectable Services: A Comprehensive Guide</a></p><p>When Angular creates ChildComponent, it searches for RootService starting from the ChildInjector and moving up the tree, eventually reaching the EnvironmentInjectorTree and more precisely, the RootInjector.</p><blockquote><strong>Note:</strong> The exact implementation is more complex, but for the sake of simplicity, we’ll provide a high-level explanation here.</blockquote><p>When the DI system reaches the RootInjector, it searches for the RootService key, similar to any other NodeInjector. However, it doesn&#39;t find it there either. Unlike NodeInjectors, before moving to the next EnvironmentInjector, it compares the scope of the Injector with the scope of the service being injected.</p><p>The code below is a portion of the get function of the RootInjector: <em>(If you want to see the full function, you can go </em><a href="https://github.com/angular/angular/blob/fc9ba3978cc098b59c107371bbd5413044fbecda/packages/core/src/di/r3_injector.ts#L266"><em>here</em></a><em>)</em></p><pre>let record: Record&lt;T&gt;|undefined|null = this.records.get(token);<br>if (record === undefined) {<br>  // No record, but maybe the token is scoped to this injector. Look for an injectable<br>  // def with a scope matching this injector.<br>  const def = couldBeInjectableType(token) &amp;&amp; getInjectableDef(token);<br>  if (def &amp;&amp; this.injectableDefInScope(def)) {<br>    // Found an injectable def and it&#39;s scoped to this injector. Pretend as if it was here<br>    // all along.<br>    record = makeRecord(injectableDefOrInjectorDefFactory(token), NOT_YET);<br>  } else {<br>    record = null;<br>  }<br>  this.records.set(token, record);<br>}</pre><p>First, it attempts to retrieve the record of the searched token. If there is no record, it checks if the service has an InjectableDef <em>(the </em><em>providedIn property)</em>. If the service has one and if the scope matches the scope of the current EnvironmentInjector <em>(root in our case)</em>, a new record is created and added to the Injector, then a new instance is returned.</p><p>The next time a component requests RootService, the record will be present, and the same instance will be returned.</p><blockquote><strong>Note:</strong> While less common, if you want to provide your service inside the PlatformInjector, you can set your Injectable to providedIn: &#39;platform&#39;.</blockquote><p><strong>Warning: </strong>In practice, setting the providedIn: &#39;root&#39; property for your Injectable service signifies that your service will be a <strong>singleton</strong>. However, if you provide your service within the providers property of one of your components, this service will be added to the record of the NodeInjector of that component. Let&#39;s see an example to better understand this:</p><pre>@Component({})<br>export class ChildComponent {<br>  service = inject(RootService);<br>}<br><br>@Component({<br>  providers: [RootService]<br>})<br>export class FooComponent {<br>  service = inject(RootService);<br>}<br><br>@Component({<br>  template: `<br>    &lt;child /&gt;<br>    &lt;foo /&gt;<br>  `,<br>  imports: [ChildComponent, FooComponent],<br>})<br>export class ParentComponent {}<br><br>// injectable service<br>@Injectable({ providedIn: &#39;root&#39; })<br>export class RootService {}</pre><p>Here, we have a providedIn: &#39;root&#39; RootService, which is injected inside both FooComponent and ChildComponent. However, we provide RootService inside the NodeInjector of FooComponent. This gives us the following graph:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lHZk4SFGu6zTXyWKRrmiKg.png" /></figure><p>ChildComponent will have an instance of the service located inside the RootInjector, whereas FooComponent will have the one from its own Injector. This can be misleading because by observing the service, one might assume that both components share the same global instance, which is not the case in this example.</p><p>In summary, providedIn: &#39;root&#39; is only information for Angular to create a record inside RootInjector and only if the service reaches that point while searching for it inside the InjectorTree.</p><p>I really hope that the Dependency Injection System of Angular will no longer hold any secrets for you. You should now be able to harness its power to create exceptional applications and understand whether an instance of a service will be shared or unique.</p><p>You can expect me to write follow-up articles on the following subjects:</p><ul><li>Dependency Injection inside Routed Components</li><li>Injection Flags: Host, Self, SkipSelf, and Optional</li><li>All the options for overriding within the DI: useClass, useValue, useFactory, useExisting</li></ul><p>If you would like to learn about anything else, please don’t hesitate to leave a comment.</p><blockquote>If you want to improve your Angular skill, go check out <a href="https://angular-challenges.vercel.app/">Angular Challenges</a>. It groups a set of challenges about Angular and its ecosystem.</blockquote><p>You can find me on <a href="https://medium.com/@thomas.laforge">Medium</a>, <a href="https://twitter.com/laforge_toma">Twitter</a> or <a href="https://github.com/tomalaforge">Github</a>. Don’t hesitate to reach out to me if you have any questions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc210f7040bd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/ngconf/how-angular-dependency-injection-works-under-the-hood-cc210f7040bd">How Angular Dependency Injection works under the hood</a> was originally published in <a href="https://medium.com/ngconf">ngconf</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[On the road to Fine-Grained Change Detection]]></title>
            <link>https://medium.com/@thomas.laforge/on-the-road-to-fine-grained-change-detection-d61813aa9753?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/d61813aa9753</guid>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Wed, 01 Nov 2023 14:46:18 GMT</pubDate>
            <atom:updated>2023-11-17T11:54:18.664Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*nsvMy0VyXVwG7uoAj6dghQ.png" /></figure><p>A pull request was recently merged and will be available in the upcoming major version v17. This pull request represents the first step towards fine-grained reactivity and signal-based components.</p><p>If you want to read more, click the pull request below:</p><p><a href="https://github.com/angular/angular/pull/52302">perf(core): Update LView consumer to only mark component for check by atscott · Pull Request #52302 · angular/angular</a></p><p>The goal of this change is to refresh <strong>ONLY</strong> the component where the change occurred, but there are a couple of requirements:</p><ul><li>All your components need to use <strong>OnPush</strong>.</li><li>The change has to be triggered by a <strong>Signal</strong>.</li></ul><p>Let’s take a step back and explain why we need OnPush and Signals to benefit from local change detection.</p><h3>How Change Detection Works</h3><p>Angular employs <strong>Zone.js</strong> to detect changes and trigger a new render. Since Zone.js doesn’t track where a change occurred, Angular performs a top-to-bottom re-evaluate. This means that Angular refreshes the RootComponent down to all ChildComponents. This operation is fast but costly since Angular rerenders all components even if only a small change occurred.</p><p>Meanwhile, <strong>ChangeDetectionStrategy.OnPush</strong> was introduced to optimize the change detection cycle. When a component is marked for check, all its ancestors are also marked for check. Then Angular triggers a top-down cycle and <strong>only refreshes the marked components</strong>.</p><p>In v16, Angular introduced Signals, a new reactive API. This API allows Angular to precisely determine where a change is occurring. Angular can now identify which components need to be refreshed. A Signal marks the component as dirty but not its ancestors. When Angular performs its top-down change detection cycle, only that specific component will be refreshed.</p><h3>Notes:</h3><ul><li>Angular still needs to perform a top-down change detection to maintain backward compatibility with previous versions and with components not using Signals, relying on Zone.js instead.</li><li>Currently, Angular cannot achieve more granularity than per-component because it might disrupt how non-signal components are refreshed. In the future, the goal is to be more granular, but it will never be as granular as the binding. Refer to the <a href="https://github.com/angular/angular/discussions/49684">RFC</a> for more details.</li></ul><h3><strong>Conclusion:</strong></h3><p>Now you should understand why this change will benefit applications that leverage the use of Signals and OnPush.</p><ol><li>Signals allow Angular to precisely pinpoint where the change occurred and mark only the affected component as dirty.</li><li>OnPush is necessary because Angular still requires a top-down change detection, and it only refreshes OnPush components when marked for check. All Default components will still be rerefreshed.</li></ol><h3>Thomas 🅰️🇨🇵 on Twitter: &quot;A short video to better visualise how the new Local Change Detection can improve your application if you are using OnPush + Signals 🔥 pic.twitter.com/CLhCJRow7A / Twitter&quot;</h3><p>A short video to better visualise how the new Local Change Detection can improve your application if you are using OnPush + Signals 🔥 pic.twitter.com/CLhCJRow7A</p><p>Update your application to v17 and start using Signals, as this is just the beginning of how Signals can enhance your Angular application.</p><p>You can find me on <a href="https://medium.com/@thomas.laforge">Medium</a>, <a href="https://twitter.com/laforge_toma">Twitter</a> or <a href="https://github.com/tomalaforge">Github</a>. Don’t hesitate to reach out to me if you have any questions.</p><p><em>Thanks to </em><a href="https://twitter.com/Enea_Jahollari"><em>Enea</em></a><em> for reviewing it.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d61813aa9753" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Function calls inside template are dangerous!]]></title>
            <link>https://medium.com/ngconf/function-calls-inside-template-are-dangerous-15f9822a6629?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/15f9822a6629</guid>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[ngconf]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Thu, 31 Aug 2023 14:38:25 GMT</pubDate>
            <atom:updated>2023-10-26T19:34:33.598Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*I1JJGPvZWUnimL_tYCHQ5A.png" /></figure><p>The other day, one of my coworkers detected a strange behavior inside our application. When he added RouterLinkActive to a link, the application stopped rendering. However when the directive was removed, the application worked correctly.</p><p>Instead of immediately reading the solution, I created a <a href="https://angular-challenges.vercel.app/challenges/angular/32-bug-cd/">challenge</a> inside AngularChallenges for those who want to try resolving and understanding the source of the bug first. After that, you can come back to this article to compare your solution with mine and understand what went wrong.</p><p>To better understand the problem, below is a small reproduction of the issue:</p><pre>interface MenuItem {<br>  path: string;<br>  name: string;<br>}<br><br>@Component({<br>  selector: &#39;app-nav&#39;,<br>  standalone: true,<br>  imports: [RouterLink, NgFor, RouterLinkActive],<br>  template: `<br>    &lt;ng-container *ngFor=&quot;let menu of menus&quot;&gt;<br>      &lt;a<br>        [routerLink]=&quot;menu.path&quot;<br>        [routerLinkActive]=&quot;isSelected&quot;<br>          &gt;<br>        {{ menu.name }}<br>      &lt;/a&gt;<br>    &lt;/ng-container&gt;<br>  `,<br>})<br>export class NavigationComponent {<br>  @Input() menus!: MenuItem[];<br>}<br><br>@Component({<br>  standalone: true,<br>  imports: [NavigationComponent, NgIf, AsyncPipe],<br>  template: `<br>    &lt;ng-container *ngIf=&quot;info$ | async as info&quot;&gt;<br>      &lt;ng-container *ngIf=&quot;info !== null; else noInfo&quot;&gt;<br>        &lt;app-nav [menus]=&quot;getMenu(info)&quot; /&gt;<br>      &lt;/ng-container&gt;<br>    &lt;/ng-container&gt;<br><br>    &lt;ng-template #noInfo&gt;<br>      &lt;app-nav [menus]=&quot;getMenu(&#39;&#39;)&quot; /&gt;<br>    &lt;/ng-template&gt;<br>  `,<br>})<br>export class MainNavigationComponent {<br>  private fakeBackend = inject(FakeServiceService);<br><br>  readonly info$ = this.fakeBackend.getInfoFromBackend();<br><br>  getMenu(prop: string) {<br>    return [<br>      { path: &#39;/foo&#39;, name: `Foo ${prop}` },<br>      { path: &#39;/bar&#39;, name: `Bar ${prop}` },<br>    ];<br>  }<br>}</pre><p>MainNavigationComponent will display NavigationComponent and pass a list of MenuItem as an argument depending on the return of an HTTP request. When our HTTP request returns, we call the getMenu function with an empty string if there is no info, or with info if it’s not empty.</p><p>NavigationComponent will iterate over MenuItem and create a link for each item using RouterLink and RouterLinkActive for routing.</p><p>At first sight, this code seems correct, but applying RouterLinkActive to each link breaks the rendering with no errors in the console.</p><p>What could be happening? 🤯</p><p>To better understand the issue, let’s break down RouterLinkActive with the code that is causing the infinite rendering loop:</p><pre>import { Directive } from &#39;@angular/core&#39;;<br><br>@Directive({<br>  selector: &#39;[fake]&#39;,<br>  standalone: true,<br>})<br>export class FakeRouterLinkActiveDirective {<br>  constructor(private readonly cdr: ChangeDetectorRef) {<br>    queueMicrotask(() =&gt; {<br>      this.cdr.markForCheck();<br>    });<br>  }<br>}</pre><p>Inside RouterLinkActive, we call this.cdr.markForCheck() to mark our component as dirty. However, we make this function call within a different micro task. Once our current macro task ends, Angular will trigger a new change detection cycle within the following micro task.</p><p>Having this information, can you spot the issue now ?</p><p>Since Angular runs a new change detection cycle, the framework rechecks every binding, causing new function calls. This means that the getMenu function inside our MainNavigationComponent will be called again, and it returns a new instance of MenuItems.</p><p>But that’s not all.</p><p>NavigationComponent iterates over the array using the NgFor directive. As a new instance of MenuItem is passed as an Input to the component, NgFor recreates its list. NgFor destroys all DOM elements inside the list and recreates them. This causes the recreation of the RouterLinkActive instance, leading to another round of change detection, and this will be infinite.</p><p>We can avoid this by using the trackBy function inside the NgFor directive. This function tracks one property on the element, and checks if that property still exists within the new array. NgFor will only DESTROY or CREATE an element if the property no longer exists or did not exist previously. Adding the trackBy function in our case will correct the issue of infinite re-rendering.</p><p>If you are always forgetting the trackBy function, I invite you to read this article.</p><p><a href="https://medium.com/ngconf/make-trackby-easy-to-use-a3dd5f1f733b">Make TrackBy Easy to Use!</a></p><p>However, even if the trackBy function resolves this error, creating a new instance of MenuItem at each change detection cycle is bad practice.</p><p>One way of avoiding this would be to create a menuItem class property, but this would create imperative code and lead to spaghetti code.</p><p>The best way is to take a more declarative approach. Let’s see how to refactor the code in a more declarative way:</p><pre>@Component({<br>  standalone: true,<br>  imports: [NavigationComponent, AsyncPipe],<br>  template: ` &lt;app-nav [menus]=&quot;(menus$ | async) ?? []&quot; /&gt; `,<br>})<br>export class MainNavigationComponent {<br>  private fakeBackend = inject(FakeServiceService);<br><br>  readonly menus$ = this.fakeBackend<br>    .getInfoFromBackend()<br>    .pipe(map((info) =&gt; this.getMenu(info ?? &#39;&#39;)));<br><br>  getMenu(prop: string) {<br>    return [<br>      { path: &#39;/foo&#39;, name: `Foo ${prop}` },<br>      { path: &#39;/bar&#39;, name: `Bar ${prop}` },<br>    ];<br>  }<br>}</pre><p>Our menus$ property is now defined in a single place, and will update when getInfoFromBackend returns. menu$ will not get recomputed at each change detection cycle and only a single instance will be created during the entire lifetime of MainNavigationComponent. The code looks simpler, doesn’t it ?</p><p>You have probably heard that calling functions inside templates is a bad practice, and in most cases, it is. While you can call a function to access nested properties of an object, that should be one of the only exceptions. Try to avoid calling functions inside your template bindings, or be sure to really understand what you are doing and all the side effects that can be created by this function call. When attempting to mutate data through a function call, it should trigger a warning inside your head. Most of the time, there is a better declarative approach to your problem. Declarative programming is a different state of mind, but you should aim for it. Persist, and your code will become clearer and simpler, and both your coworkers and your future self will thank you for that.</p><p>I hope this article sheds some light on the consequences of calling functions within template bindings.</p><p><strong>Notes:</strong> In the future, Angular with “signal” will reduce that risk. With “signal” being memoized, it will save you from recreating new instances. 🔥</p><p>You can find me on <a href="https://medium.com/@thomas.laforge">Medium</a>, <a href="https://twitter.com/laforge_toma">Twitter</a> or <a href="https://github.com/tomalaforge">Github</a>. Don’t hesitate to reach out to me if you have any questions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=15f9822a6629" width="1" height="1" alt=""><hr><p><a href="https://medium.com/ngconf/function-calls-inside-template-are-dangerous-15f9822a6629">Function calls inside template are dangerous!</a> was originally published in <a href="https://medium.com/ngconf">ngconf</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[New input binding for NgComponentOutlet]]></title>
            <link>https://medium.com/ngconf/new-input-binding-for-ngcomponentoutlet-cb18a86a739d?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/cb18a86a739d</guid>
            <category><![CDATA[ngconf]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Thu, 24 Aug 2023 14:31:01 GMT</pubDate>
            <atom:updated>2023-08-24T14:31:01.444Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*gV1kJ3Z2dhNfUpkXK134Mg.png" /></figure><p>In Angular, you can create dynamic components using the NgComponentOutlet directive. However when your component has inputs, it was cumbersome to pass them through. In version 16.2.0-next.4, a new feature has been introduced, allowing you to bind your inputs much more easily.</p><p>In this article, we will explore how to achieve input binding in previous versions of Angular and the new approach introduced in version 16.2.0-next.4. Additionally, we will demonstrate another method to create dynamic components.</p><h3>Before v16.2.0-next.4</h3><p>Prior to version 16.2.0-next.4, you had to create an injector to set up your inputs and inject it into your dynamic component to access them.</p><p>Let’s look at an example to better understand this process.</p><p>First we need an InjectionToken for better type safety:</p><pre>interface TitleInputs {<br>  title: string;<br>  subTitle: string;<br>}<br><br>const INPUTS = new InjectionToken&lt;TitleInputs&gt;(&#39;title inputs&#39;);</pre><p>Now in our component we can create a custom injector to set up our inputs.</p><pre>@Component({<br>  selector: &#39;app-root&#39;,<br>  standalone: true,<br>  imports: [NgComponentOutlet],<br>  template: `<br>    &lt;ng-template *ngComponentOutlet=&quot;template; injector: customInjector&quot; /&gt;<br>  `,<br>})<br>export class AppComponent implements OnInit {<br>  template = OldTitleComponent;<br><br>  titleInputs = {<br>    title: &#39;Inputs for Component outlets&#39;,<br>    subTitle: `That&#39;s awesome`,<br>  };<br><br>  customInjector = Injector.create({<br>    providers: [{ provide: INPUTS, useValue: this.titleInputs }],<br>  });<br>}</pre><p>Finally, in OldTitleComponent , we can inject our token to retrieve our inputs.</p><pre>@Component({<br>  selector: &#39;app-title&#39;,<br>  standalone: true,<br>  template: `OldWay: {{ inputs.title }} {{ inputs.subTitle }}`,<br>})<br>export class OldTitleComponent {<br>  inputs = inject(INPUTS);<br>}</pre><p>This solution doesn’t feel very natural but there was no other way available at that time.</p><h3>After v16.2.0-next.4</h3><p>Now, since input binding has been implemented, we can simply pass our inputs object to our directive and retrieve our inputs using the @Input decorator, as we would expect it to be.</p><p>Let’s see this in action.</p><pre>@Component({<br>  selector: &#39;app-root&#39;,<br>  standalone: true,<br>  imports: [NgComponentOutlet],<br>  template: `<br>    &lt;ng-template *ngComponentOutlet=&quot;template; inputs: titleInputs&quot; /&gt;<br>  `,<br>})<br>export class AppComponent implements OnInit {<br>  template = TitleComponent;<br><br>  titleInputs = {<br>    title: &#39;Inputs for Component outlets&#39;,<br>    subTitle: `That&#39;s awesome`,<br>  };<br>}</pre><pre>@Component({<br>  selector: &#39;app-title&#39;,<br>  standalone: true,<br>  template: `NewWay: {{ title }} {{ subTitle }}`,<br>})<br>export class TitleComponent{<br>  @Input() title!: string;<br>  @Input() subTitle?: string;<br>}</pre><p>So much simpler, isn’t it?</p><p>Moreover, if any inputs change inside the titleInputs object, TitleComponent will be notified. 👍</p><p><strong>Note:</strong> Inputs binding is not typed. Anything can be pass to inputs property.</p><h3><strong>Using CreateComponent</strong></h3><p>In Angular, there’s another API to dynamically create components. Instead of doing it inside the template, you can achieve it in the TypeScript part using the createComponent function.</p><pre>@Component({<br>  selector: &#39;app-root&#39;,<br>  standalone: true,<br>  template: ``,<br>})<br>export class AppComponent implements OnInit {<br>  ref = inject(ViewContainerRef);<br>  envInjector = inject(EnvironmentInjector);<br><br>  ngOnInit(): void {<br>    const comp = this.ref.createComponent(TitleComponent, {<br>      environmentInjector: this.envInjector,<br>    });<br>    comp.setInput(&#39;title&#39;, &#39;Inputs with createComponent&#39;);<br>    comp.setInput(&#39;subTitle&#39;, &#39;Still works!&#39;);<br>  }<br>}</pre><p>To bind inputs, you need to use the setInput function. While you could do comp.instance.title = `...` , if your input change, TitleComponent will not be notified.</p><p><strong>Note: </strong>NgComponentOutlet is using createComponent and setInput under the hood. 😉</p><p>Enjoy creating dynamic component in an easier way !! 🚀</p><p>You can find me on <a href="https://medium.com/@thomas.laforge">Medium</a>, <a href="https://twitter.com/laforge_toma">Twitter</a> or <a href="https://github.com/tomalaforge">Github</a>. Don’t hesitate to reach out to me if you have any questions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cb18a86a739d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/ngconf/new-input-binding-for-ngcomponentoutlet-cb18a86a739d">New input binding for NgComponentOutlet</a> was originally published in <a href="https://medium.com/ngconf">ngconf</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Share / ShareReplay / RefCount]]></title>
            <link>https://itnext.io/share-sharereplay-refcount-a38ae29a19d?source=rss-c8632e31eb8c------2</link>
            <guid isPermaLink="false">https://medium.com/p/a38ae29a19d</guid>
            <category><![CDATA[rxjs]]></category>
            <category><![CDATA[angular]]></category>
            <dc:creator><![CDATA[Thomas Laforge]]></dc:creator>
            <pubDate>Tue, 01 Aug 2023 11:17:31 GMT</pubDate>
            <atom:updated>2023-08-01T14:26:09.788Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*DtWBnpSTX8CW7qjqnzIytw.png" /></figure><p>share and ShareReplay are two RxJs operators that we always struggle to use correctly. We know that we can reach for them when we want to multicast a costly observable or cache a value that will be used at multiple places. But what are the key differences between both, and what is the refCount flag and how can we leverage its behavior?</p><p>In this article, I will try to explain them for you so that you will not need to ask this question again.</p><h3>Example 1</h3><p>I posted the following question on Twitter which unfortunately received zero responses. This really highlights the lack of understanding regarding share and shareReplay.</p><h3>Thomas 🅰️🇨🇵 on Twitter: &quot;#angularQuizz:Do you know #Rxjs share / shareReplay: refcount ? Below are 3 snapshots: 👉 share👉 shareReplay: refcount=true👉 shareReplay: refcount=falseCan you guess what will be displayed on the screen after 10s ? Good luck, that&#39;s not easy 🤯 pic.twitter.com/UMwTsIHHRI / Twitter&quot;</h3><p>angularQuizz:Do you know #Rxjs share / shareReplay: refcount ? Below are 3 snapshots: 👉 share👉 shareReplay: refcount=true👉 shareReplay: refcount=falseCan you guess what will be displayed on the screen after 10s ? Good luck, that&#39;s not easy 🤯 pic.twitter.com/UMwTsIHHRI</p><p>The exercice looks like this:</p><pre>@Component({<br>  selector: &#39;app-count&#39;,<br>  standalone: true,<br>  imports: [NgIf, AsyncPipe],<br>  template: `<br>    &lt;ng-container *ngIf=&quot;flag&quot;&gt; {{ count1$ | async }} &lt;/ng-container&gt;<br>    &lt;ng-container *ngIf=&quot;!flag&quot;&gt; {{ count2$ | async }} &lt;/ng-container&gt;<br>  `,<br>})<br>export class CountComponent implements OnInit {<br>  flag = true;<br><br>  readonly count$ = interval(1000).pipe(<br>    take(7),<br>    shareReplay({ bufferSize: 1, refCount: false }) // 👈 line: 15<br>  );<br><br>  readonly count1$ = this.count$.pipe(<br>    take(3),<br>    map((c) =&gt; `count1: ${c}`)<br>  );<br>  readonly count2$ = this.count$.pipe(<br>    take(3),<br>    map((c) =&gt; `count2: ${c}`)<br>  );<br><br>  ngOnInit(): void {<br>    setTimeout(() =&gt; {<br>      this.flag = false;<br>    }, 5500);<br>  }<br>}</pre><p><strong>Note: </strong>This code snippet is written using Angular, but the behavior of both operator is the same outside the framework.</p><p>When the flag is true, we subscribe to count1$and after 3 emissions, the observable completes. Then after 5500ms, the flag is set to false and we subscribe to count2$ which also completes after 3 emissions. Both observables are chained with count$.</p><p>The goal is to predict the result displayed on the screen after 10s, depending on the operator used in line 15.</p><h4>Share</h4><p>Let’s start with the share operator.</p><p>The share operator will multicast each value emitted by the source observable, which means we won’t re-execute the source observable for each new subscription.</p><p>Moreover, when the <strong>count of subscribers drops to 0, the source observable will be unsubscribed.</strong></p><p>Inside this operator, we <strong>use a </strong><strong>Subject as a connector</strong> between the source observable and the subscribers. This means that <strong>every late subscriber will NOT have access to the previously emitted data</strong>.</p><p>You should use this operator if you know that you will not use previously emitted data and are only interested in upcoming ones.</p><h4><strong>Solution</strong></h4><p>If we go back to our example:</p><ol><li>count$ will be triggered when count1$ subscribes to it.</li><li>After count$ emits 3 values, count1$ completes due to the take(3) operator. Consequently count$ will also complete since the number of subscribers drops to 0 and the inner Subject will reset.</li><li>After 5500ms count2$ starts. It will subscribe to count$ and count$ will start emitting from the beginning.</li><li>Since we have a take(3) on the observable, <strong>the final answer is 3</strong>.</li></ol><h4>ShareReplay with refCount: true</h4><p>Both share and shareReplay operators behave almost the same: ShareReplay use share under the hood. The crucial difference lies in the connector: <strong>shareReplay uses a </strong><strong>ReplaySubject instead of a </strong><strong>Subject</strong>. This distinction becomes significant when dealing with late subscribers, as they will have access to previously emitted data.</p><p>Another difference is the ability to toggle the refCount flag. When refCount=true, it allows unsubscribing from the source observable when the subscriber count drops to 0. The share operator’s refCount flag is defaulted to true.</p><p>In this scenario with refCount set to true, the source observable will get unsubscribed when the subscriber count drops to 0.</p><h4>Solution</h4><p>If we go back to our exemple:</p><ol><li>count$ will be triggered when count1$ subscribes to it.</li><li>After count$ emits 3 times, count1$ completes due to the take(3) operator. Consequently count$ will also complete since the number of subscribers drops to 0 and the refCount flag is set to true.</li><li>After 5500ms count2$ starts, it will subscribe to count$ again and count$ will start emitting from the beginning.</li><li>Since we have a take(3) on the observable, <strong>the final answer is 3</strong>. In this example, both share and shareReplay behave exactly the same. However, we will see more examples below to understand the differences between them.</li></ol><h4>ShareReplay with refCount: false</h4><p>As explained above, <strong>setting the </strong><strong>refCount flag to false will keep the source observable alive even if the subscriber count drops to 0</strong>.</p><p>This is dangerous because <strong>if the source observable never completes, this can create memory leaks.</strong></p><p>However in some cases, you may not want to re-execute the source observable if a new subscriber subscribes, such as in the case of an HTTP request.</p><h4>Solution</h4><p>If we go back to our exemple:</p><ol><li>count$ will be triggered when count1$ subscribes to it.</li><li>After count$ emits 3 times, count1$ completes due to the take(3) operator, BUT count$ will NOT complete and continue to emit a value every 1s.</li><li>After 5500ms count2$ starts, it will subscribe to count$ and receive the last emitted value which is 4.</li><li>Since we have a take(3) on the observable, <strong>the final answer is 6.</strong></li></ol><p><strong>Note:</strong> Since all observables completes, we don’t have any memory leaks issues</p><h3>Example 2</h3><p>Let’s take another example to better understand the difference between share and shareReplay. This distinction becomes more evident when we apply the operators to an observable that never completes, such as a BehaviorSubject.</p><pre>@Component({<br>  selector: &#39;app-root&#39;,<br>  standalone: true,<br>  imports: [NgIf, AsyncPipe],<br>  template: `<br>    &lt;ng-container *ngIf=&quot;!flagFinalize&quot;&gt;<br>      &lt;ng-container&gt; {{ count1$ | async }} &lt;/ng-container&gt;<br>      &lt;ng-container *ngIf=&quot;flag&quot;&gt; {{ count2$ | async }} &lt;/ng-container&gt;<br>    &lt;/ng-container&gt;<br>    &lt;button (click)=&quot;flagFinalize = !flagFinalize&quot;&gt;FINALIZE&lt;/button&gt;<br>    &lt;button (click)=&quot;subject.next(subject.value + 1)&quot;&gt;INCREMENT&lt;/button&gt;<br>  `,<br>})<br>export class AppComponent implements OnInit {<br>  flag = false;<br>  flagFinalize = false;<br><br>  subject = new BehaviorSubject(0);<br><br>  readonly count$ = this.subject.pipe(<br>    tap({<br>      next: (t) =&gt; console.log(&#39;I get next value of count&#39;, t),<br>      complete: () =&gt; console.log(&#39;complete count&#39;),<br>      finalize: () =&gt; console.log(&#39;finalize count&#39;),<br>    }),<br>    share() // 👈<br>  );<br><br>  readonly count1$ = this.count$.pipe(<br>    tap({<br>      next: (t) =&gt; console.log(&#39;I get next value of count1&#39;, t),<br>      complete: () =&gt; console.log(&#39;complete count1&#39;),<br>      finalize: () =&gt; console.log(&#39;finalize count1&#39;),<br>    }),<br>    map((c) =&gt; `count1: ${c}`)<br>  );<br>  readonly count2$ = this.count$.pipe(<br>    tap({<br>      next: (t) =&gt; console.log(&#39;I get next value count2&#39;, t),<br>      complete: () =&gt; console.log(&#39;complete count2&#39;),<br>      finalize: () =&gt; console.log(&#39;finalize count2&#39;),<br>    }),<br>    map((c) =&gt; `count2: ${c}`)<br>  );<br><br>  ngOnInit(): void {<br>    setTimeout(() =&gt; {<br>      this.flag = true;<br>    }, 1000);<br>  }<br>}</pre><p>This time we are using a BehaviorSubject and we have an INCREMENT button to emit a value to the Subject.</p><p>Additionally, we have added a tap operator to log the next, complete and finalize events.</p><p>The FINALIZE button allows us to unsubscribe from the count1$ and count2$ observables.</p><h4>Scenario</h4><p>When the app is loaded, count1$ subscribes to count$, and after 1s count2$ also subscribes to count1$.</p><p>Next, we click once on the INCREMENT button, and finally we click on the FINALIZE button.</p><p>Before reading the article further, I invite you to try to guess what the behavior of this scenario will be with each operator. Once you are done, compare your ideas with the solution.</p><h4>Share</h4><ol><li>When count1$ subscribes to count$, the count$ observable will start emitting values and count1$ will receive the initial value 0.</li><li>In this scenario, count1$ doesn’t complete so the number of subscribers will not drop to 0, and count$ will not complete either.</li><li>After 1s, count2$ starts subscribing to count$, but since we are using the share operator, the inner observable is a Subject which will not replay the previous emitted value. Therefore count2$ will not receive any value.</li><li>We now click on the INCREMENT button, and both count1$ and count2$ will be notified with the value 1.</li><li>Finally we click on the FINALIZE button, and both count1$ and count2$ will get unsubscribed (<em>due to the </em><em>asyncPipe</em>) and finalized. Since the share operator unsubscribes the source when all subscribers drop to 0, count$ will finalize as well.</li></ol><h4>ShareReplay with refCount: true</h4><p>We replace the share operator with shareReplay({bufferSize: 1, refCount: true})</p><ol><li>same behavior as previously</li><li>same thing</li><li>After 1s, count2$ starts subscribing to count$. However this time, count2$ gets the previous emitted value (<em>0</em> <em>in our case</em>), because shareReplay uses a ReplaySubject as the connector. This is the significant difference between both operators</li></ol><p>4 and 5 are identical since the refCount flag is set to true.</p><h4>ShareReplay with refCount: false</h4><p>1 to 4 is identical to the previous scenario, and all differences are seen in point 5 when we unsubscribe from the subscribers.</p><p>5. When we click on the FINALIZE button, count1$ and count2$ will be unsubscribed correctly due to the asyncPipe. However count$ will not finalize because shareReplay will not unsubscribe the inner source and count$ will continue to exist indefinitely, which might cause a memory leak.</p><p><strong>Important note:</strong> If you have noticed, I said that this might cause a memory leak. If you lose the reference to your running observable, such as when destroying a component, a new count$ observable will be instantiated next time you initialize the particular component. This can lead to memory leaks.</p><p>In the above example, switching back the flag, count1$ and count2$ will resubscribe to the existing observable. shareReplay with refCount set to false is useful when you don’t want to re-execute a costly observable like an HTTP request. You can set your shared observable in a global service and inject it anywhere in your application. The instance will never be unsubscribed, but when new subscribers subscribe to the observable, they will use the existing instance.</p><p><strong>Note: </strong>We often see the shorthand synthax replaySubject(1), which is the shorthand for replaySubject({bufferSize: 1, refCount: false}). So you need to be careful about using this synthax. You will more often reach for refCount set to true to avoid memory leak issues.</p><h3>Example 3</h3><p>In the last example, we will use a source observable that completes, similar to an HTTP request. To simplify the example, we will use the of operator.</p><pre>@Component({<br>  selector: &#39;app-root&#39;,<br>  standalone: true,<br>  imports: [NgIf, AsyncPipe],<br>  template: `<br>    &lt;ng-container&gt; {{ request1$ | async }} &lt;/ng-container&gt;<br>    &lt;ng-container *ngIf=&quot;flag&quot;&gt; {{ request2$ | async }} &lt;/ng-container&gt;<br>  `,<br>})<br>export class AppComponent implements OnInit {<br>  flag = false;<br><br>  readonly http$ = of(&#39;trigger http request&#39;).pipe(<br>    tap({<br>      next: (t) =&gt; console.log(&#39;http response&#39;, t),<br>      complete: () =&gt; console.log(&#39;complete http&#39;),<br>      finalize: () =&gt; console.log(&#39;finalize http&#39;),<br>    }),<br>    share() // 👈<br>  );<br><br>  readonly request1$ = this.http$.pipe(<br>    tap({<br>      next: (t) =&gt; console.log(&#39;request1 response&#39;, t),<br>      complete: () =&gt; console.log(&#39;complete request1&#39;),<br>      finalize: () =&gt; console.log(&#39;finalize request1&#39;),<br>    }),<br>    map((c) =&gt; `request1: ${c}`)<br>  );<br>  readonly request2$ = this.http$.pipe(<br>    tap({<br>      next: (t) =&gt; console.log(&#39;request2 response&#39;, t),<br>      complete: () =&gt; console.log(&#39;complete request2&#39;),<br>      finalize: () =&gt; console.log(&#39;finalize request2&#39;),<br>    }),<br>    map((c) =&gt; `request2: ${c}`)<br>  );<br><br>  ngOnInit(): void {<br>    setTimeout(() =&gt; {<br>      this.flag = true;<br>    }, 1000);<br>  }<br>}</pre><h4>Scenario</h4><p>When we load the component, we trigger a first HTTP request using the observable http$. After 1s, we want to get the result of the same request. To achieve this, we consider using either share or shareReplay operator to cache the result.</p><p>Same exercice as previously, I encourage you to think first before reading the solution below.</p><h4>Share</h4><ol><li>request1$ subscribes to http$ which triggers an HTTP call. Once the response comes back, the HTTP call completes, causing http$ and request1$ to complete as well.</li><li>After 1s, request2$ subscribes to http$ hoping to get the result of the same HTTP call. However, since share uses a Subject under the hood, nothing is cached, and http$ is re-executed, resulting in a new HTTP call.</li></ol><h4>ShareReplay</h4><p>In this scenario, the refCount doesn’t change the behavior since the source observable (http$) completes on its own, irrespective of the number of subscribers.</p><ol><li>same as previously</li><li>After 1s, request2$ subscribes to http$ but this time, shareReplay uses a ReplaySubject as the connector, allowing it to store the last value emitted by http$. Therefore http$doesn’t need to be re-executed, and request2$ received the cached value without triggering a new HTTP call.</li></ol><p><strong>Note:</strong> Be careful when using shareReplay inside a <strong>global service</strong> behind a http call. Each new subscriber will receive the cached value and the HTTP request will NEVER fire again. As a result, your data will NEVER get refreshed.</p><h3>Conclusion</h3><p>In summary, shareReplay is useful in scenarios where you want to cache and replay the last emitted value of an observable, especially in situations like HTTP requests, to avoid unnecessary re-execution and improve performance. <strong>But be careful, this is useful inside a component scope, generally not within the global scope.</strong></p><p>You need to think carefully about the refCount flag when using shareReplay on observables that don’t complete on their own.</p><p>share is useful when you want to multicast a long-living observable and you don’t need to access previously emitted data.</p><p>As you can see, understanding exactly how this two operators work under the hood can help you improve your application’s performance.</p><p>I hope you now have a better understanding of the differences between share and shareReplay and the importance of the refCount flag. With this knowledge, you should be able to use them correctly and truly understand what is happening behind the scenes.</p><p>You can find me on <a href="https://medium.com/@thomas.laforge">Medium</a>, <a href="https://twitter.com/laforge_toma">Twitter</a> or <a href="https://github.com/tomalaforge">Github</a>. Don’t hesitate to reach out to me if you have any questions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a38ae29a19d" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/share-sharereplay-refcount-a38ae29a19d">Share / ShareReplay / RefCount</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>