<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anastasios Theodosiou</title>
    <description>The latest articles on DEV Community by Anastasios Theodosiou (@atheodosiou).</description>
    <link>https://dev.to/atheodosiou</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F241863%2F8de4b911-19d0-4526-aeb0-cd2cf41bf7f1.jpg</url>
      <title>DEV Community: Anastasios Theodosiou</title>
      <link>https://dev.to/atheodosiou</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atheodosiou"/>
    <language>en</language>
    <item>
      <title>The Missing Piece in Angular i18n</title>
      <dc:creator>Anastasios Theodosiou</dc:creator>
      <pubDate>Sun, 01 Feb 2026 00:42:04 +0000</pubDate>
      <link>https://dev.to/atheodosiou/the-missing-piece-in-angular-i18n-57m0</link>
      <guid>https://dev.to/atheodosiou/the-missing-piece-in-angular-i18n-57m0</guid>
      <description>&lt;h3&gt;
  
  
  How to stop breaking translations every time your app changes
&lt;/h3&gt;

&lt;p&gt;Angular’s i18n story looks complete at first glance.&lt;br&gt;
You mark strings, run &lt;code&gt;ng extract-i18n&lt;/code&gt;, and you get a &lt;code&gt;messages.xlf&lt;/code&gt; file that accurately represents your templates.&lt;/p&gt;

&lt;p&gt;That part works.&lt;/p&gt;

&lt;p&gt;What Angular never really addresses is what happens &lt;em&gt;after&lt;/em&gt; that first extraction — when the application keeps evolving and translations need to keep up &lt;strong&gt;without losing work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the gap that causes most Angular i18n pain in real projects.&lt;/p&gt;


&lt;h2&gt;
  
  
  Where things actually go wrong
&lt;/h2&gt;

&lt;p&gt;In production apps, translation files are not disposable build artifacts.&lt;br&gt;
They are long-lived documents edited by humans and external tools.&lt;/p&gt;

&lt;p&gt;A typical locale file contains far more than translated text:&lt;br&gt;
translator notes, context groups, approval flags, vendor-specific metadata — information that gives meaning and history to each string.&lt;/p&gt;

&lt;p&gt;Yet every time &lt;code&gt;messages.xlf&lt;/code&gt; changes, teams are forced into risky workflows:&lt;br&gt;
manual XML edits, copy–paste merges, regeneration of locale files, or fragile scripts that assume XLIFF is just key-value data.&lt;/p&gt;

&lt;p&gt;The result is almost always the same: &lt;strong&gt;silent data loss&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Notes disappear.&lt;br&gt;
Approved translations reset.&lt;br&gt;
Context is lost.&lt;br&gt;
And nobody notices until translators complain or builds fail.&lt;/p&gt;

&lt;p&gt;Angular does not solve this problem.&lt;br&gt;
Most third-party tools don’t either.&lt;/p&gt;


&lt;h2&gt;
  
  
  What &lt;a href="https://www.npmjs.com/package/xlf-sync" rel="noopener noreferrer"&gt;xlf-sync&lt;/a&gt; is designed to do
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvb3srfnm4xex0jlukp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvb3srfnm4xex0jlukp0.png" alt="xlf-sync cli tool" width="800" height="409"&gt;&lt;/a&gt;&lt;br&gt;
xlf-sync exists for a very specific purpose:&lt;/p&gt;

&lt;p&gt;Keep &lt;code&gt;messages.&amp;lt;locale&amp;gt;.xlf&lt;/code&gt; files structurally in sync with &lt;code&gt;messages.xlf&lt;/code&gt; &lt;strong&gt;without destroying anything that already exists&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It does not generate translations.&lt;br&gt;
It does not “clean up” XML.&lt;br&gt;
It does not assume locale files can be recreated safely.&lt;/p&gt;

&lt;p&gt;Instead, it treats them as &lt;strong&gt;valuable state&lt;/strong&gt; that must be preserved.&lt;/p&gt;

&lt;p&gt;The source file remains the authority for &lt;em&gt;what keys exist&lt;/em&gt;.&lt;br&gt;
Locale files remain the authority for &lt;em&gt;everything else&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Metadata is the real differentiator
&lt;/h2&gt;

&lt;p&gt;Most sync tools rebuild translation units from scratch.&lt;br&gt;
That inevitably strips information the tool doesn’t understand.&lt;/p&gt;

&lt;p&gt;xlf-sync takes the opposite approach.&lt;/p&gt;

&lt;p&gt;Existing translation units are never reconstructed.&lt;br&gt;
They are read, preserved byte-for-byte, and only extended when the source introduces something new.&lt;/p&gt;

&lt;p&gt;If a translator added a note two years ago, it stays.&lt;br&gt;
If a CAT tool added metadata, it survives.&lt;br&gt;
If an entry was marked as approved, it remains approved.&lt;/p&gt;

&lt;p&gt;This is not a convenience feature.&lt;br&gt;
It is the core guarantee of the tool.&lt;/p&gt;


&lt;h2&gt;
  
  
  Handling change without deleting history
&lt;/h2&gt;

&lt;p&gt;Applications change constantly.&lt;br&gt;
Strings are added, removed, and reorganized.&lt;/p&gt;

&lt;p&gt;xlf-sync makes one important assumption: &lt;strong&gt;removal does not imply deletion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a key disappears from the source file, you decide how to handle it.&lt;br&gt;
You can mark it obsolete, move it to a dedicated graveyard file, or delete it explicitly if that’s truly what you want.&lt;/p&gt;

&lt;p&gt;Nothing happens implicitly.&lt;br&gt;
Nothing disappears accidentally.&lt;/p&gt;

&lt;p&gt;This makes refactoring safer and preserves translation work even when features are temporarily removed.&lt;/p&gt;


&lt;h2&gt;
  
  
  Designed for real Angular codebases
&lt;/h2&gt;

&lt;p&gt;Many Angular repositories are not clean, greenfield projects.&lt;br&gt;
They contain legacy modules, migrations, and mixed formats.&lt;/p&gt;

&lt;p&gt;xlf-sync supports both XLIFF 1.2 and 2.0 and detects the version per file.&lt;br&gt;
It works even when both formats exist in the same project.&lt;/p&gt;

&lt;p&gt;That makes it usable not just for new apps, but for mature systems with history.&lt;/p&gt;


&lt;h2&gt;
  
  
  Visibility and automation matter
&lt;/h2&gt;

&lt;p&gt;Angular’s CLI gives you no clear picture of translation health.&lt;/p&gt;

&lt;p&gt;xlf-sync fills that gap by providing both quick console reports and a standalone HTML dashboard that visualizes translation coverage across locales. The dashboard is especially useful outside the engineering team — it makes translation progress visible without opening XML files.&lt;/p&gt;

&lt;p&gt;For automation, the &lt;code&gt;check&lt;/code&gt; command turns i18n into a real CI concern.&lt;br&gt;
You can fail builds when translations are missing, out of sync, or drifting from the source.&lt;/p&gt;

&lt;p&gt;This shifts i18n from an afterthought to a quality gate.&lt;/p&gt;


&lt;h2&gt;
  
  
  A missing piece Angular never shipped: a translation dashboard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zgd1i17kkresmt3kscd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zgd1i17kkresmt3kscd.png" alt="xlf-sync translations dashboard" width="800" height="395"&gt;&lt;/a&gt;&lt;br&gt;
Angular’s i18n tooling gives you files, but it gives you &lt;strong&gt;zero visibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You don’t know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which locales are behind&lt;/li&gt;
&lt;li&gt;how much is translated&lt;/li&gt;
&lt;li&gt;where the gaps actually are&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;xlf-sync addresses this with a standalone &lt;strong&gt;HTML dashboard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx xlf-sync dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you get a self-contained report that visualizes translation coverage across all locales. It shows how many keys exist, how many are translated, and which ones are missing — without requiring anyone to open an XLIFF file.&lt;/p&gt;

&lt;p&gt;This turns translation status from something hidden in XML into something &lt;strong&gt;observable and shareable&lt;/strong&gt;. Product managers, translators, and developers can all look at the same artifact and understand the state of i18n instantly.&lt;/p&gt;

&lt;p&gt;Because the dashboard is static HTML, it also fits naturally into CI pipelines. Teams often attach it as a build artifact, making translation progress visible alongside test reports and coverage metrics.&lt;/p&gt;

&lt;p&gt;This is not about eye candy.&lt;br&gt;
It’s about making i18n &lt;strong&gt;measurable&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this differs from other tools
&lt;/h2&gt;

&lt;p&gt;Most tools treat locale files as derived output.&lt;br&gt;
xlf-sync treats them as assets.&lt;/p&gt;

&lt;p&gt;That single distinction explains its behavior:&lt;br&gt;
no destructive syncs, no metadata loss, no silent rewrites.&lt;/p&gt;

&lt;p&gt;It prioritizes safety over cleverness — which is exactly what long-lived multilingual projects need.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub repository: &lt;a href="https://github.com/atheodosiou/xlf-sync" rel="noopener noreferrer"&gt;https://github.com/atheodosiou/xlf-sync&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Official documentation &amp;amp; landing page: &lt;a href="https://atheodosiou.github.io/xlf-sync/" rel="noopener noreferrer"&gt;https://atheodosiou.github.io/xlf-sync/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href="https://www.npmjs.com/package/xlf-sync" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/xlf-sync&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;Angular i18n handles extraction well.&lt;br&gt;
Everything after that is left to the developer.&lt;/p&gt;

&lt;p&gt;xlf-sync doesn’t replace Angular’s tooling — it completes it.&lt;/p&gt;

&lt;p&gt;If your application has real translations, real translators, and real change,&lt;br&gt;
this problem will surface sooner or later.&lt;/p&gt;

&lt;p&gt;xlf-sync simply removes the risk from that moment onward.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>i18n</category>
      <category>webdev</category>
      <category>cli</category>
    </item>
    <item>
      <title>How I Built a Plugin-Based Architecture in Angular 19+ 💉</title>
      <dc:creator>Anastasios Theodosiou</dc:creator>
      <pubDate>Wed, 05 Nov 2025 12:40:09 +0000</pubDate>
      <link>https://dev.to/atheodosiou/how-i-built-a-plugin-based-architecture-in-angular-19-1n6o</link>
      <guid>https://dev.to/atheodosiou/how-i-built-a-plugin-based-architecture-in-angular-19-1n6o</guid>
      <description>&lt;p&gt;&lt;strong&gt;A simple pattern that keeps your Angular app modular, scalable, and sane.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Why I Needed It
&lt;/h3&gt;

&lt;p&gt;A few months ago, I was working on a large Angular 20 project.&lt;br&gt;
It had everything: analytics, error monitoring, A/B testing, feature flags, and more integrations than I could count.&lt;/p&gt;

&lt;p&gt;Every new service wanted to “initialize” itself at startup.&lt;br&gt;
Soon, my &lt;code&gt;main.ts&lt;/code&gt; looked like a spaghetti monster of async calls and environment checks.&lt;br&gt;
I knew there had to be a cleaner way.&lt;/p&gt;

&lt;p&gt;That’s when I revisited something most Angular devs overlook: &lt;strong&gt;multi-providers&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  ⚙️ The Hidden Power of Multi-Providers
&lt;/h3&gt;

&lt;p&gt;Angular’s DI system can do more than inject single services.&lt;br&gt;
With a &lt;em&gt;multi provider&lt;/em&gt;, you can register multiple implementations under one &lt;code&gt;InjectionToken&lt;/code&gt;.&lt;br&gt;
When you inject that token, you get &lt;strong&gt;an array of all registered items&lt;/strong&gt;, perfect for a plugin system.&lt;/p&gt;

&lt;p&gt;This pattern lets you add or remove features without touching the core codebase.&lt;br&gt;
Each plugin is just a class with an &lt;code&gt;init()&lt;/code&gt; method and a unique ID.&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 1 – Define the Plugin Contract
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;em&gt;Minimum Angular version: 19+ (uses provideAppInitializer)&lt;/em&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// core/plugins/plugin.token.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;?():&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppPlugin&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.plugins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That’s it. A minimal interface.&lt;br&gt;
Each plugin knows how to initialize itself, and Angular will collect them all through this token.&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 2 – A Registry to Run Them All
&lt;/h3&gt;

&lt;p&gt;We need one lightweight service to coordinate everything at startup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// core/plugins/plugin-registry.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PLATFORM_ID&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isPlatformBrowser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./plugin.token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PluginRegistry&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;platformId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLATFORM_ID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;initAll&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isPlatformBrowser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platformId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// skip SSR&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eligible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;?.()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;eligible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Plugin] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; initialized`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Plugin] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; failed`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In one of my apps, this registry replaced nearly 200 lines of manual startup logic.&lt;br&gt;
Now, every integration just registers itself and runs automatically.&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 3 – Bootstrap Cleanly with &lt;code&gt;provideAppInitializer&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Angular 19 introduced &lt;code&gt;provideAppInitializer()&lt;/code&gt;, a small but powerful helper that replaces boilerplate &lt;code&gt;APP_INITIALIZER&lt;/code&gt; factories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bootstrapApplication&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideAppInitializer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app/app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PluginRegistry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./core/plugins/plugin-registry.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./core/plugins/plugin.token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SentryPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./core/plugins/sentry.plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleAnalyticsPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./core/plugins/ga.plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SentryPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GoogleAnalyticsPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;provideAppInitializer&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PluginRegistry&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;initAll&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Compatibility (Angular 15–18):
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;These versions don’t have provideAppInitializer(). Use the deprecated APP_INITIALIZER token instead; everything else stays the same.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Angular 15–18&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APP_INITIALIZER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PluginRegistry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./core/plugins/plugin-registry.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nl"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APP_INITIALIZER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PluginRegistry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line replaces all the “init this, then that” chaos, and it runs safely before your root component renders.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4 – Real Plugins in Action
&lt;/h3&gt;

&lt;p&gt;Here’s how the plugins look in practice.&lt;br&gt;
Each one is self-contained and only loads if it’s actually enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// core/plugins/ga.plugin.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./plugin.token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GoogleAnalyticsPlugin&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ga4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;GA_MEASUREMENT_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GA_MEASUREMENT_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://www.googletagmanager.com/gtag/js?id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;gtag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;anonymize_ip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;loadScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// core/plugins/sentry.plugin.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./plugin.token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SentryPlugin&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sentry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;SENTRY_DSN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SENTRY_DSN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sentry/browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tracesSampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, both run automatically, no imports, no conditionals, no spaghetti.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5 – Feature-Scoped Plugins
&lt;/h3&gt;

&lt;p&gt;This pattern scales nicely across domains.&lt;br&gt;
A payments library, for example, can register its own plugin without touching the core app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// libs/payments/payment.plugin.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@app/core/plugins/plugin.token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentsAuditPlugin&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;AppPlugin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payments-audit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* custom logic */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;providePaymentsPlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APP_PLUGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaymentsAuditPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach it right in the route config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;providePaymentsPlugins&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./payments.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PaymentsComponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every feature can extend global behavior independently. No central bottlenecks.&lt;/p&gt;




&lt;h3&gt;
  
  
  ⚡ What This Gives You
&lt;/h3&gt;

&lt;p&gt;From experience, this small pattern delivers huge wins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility:&lt;/strong&gt; add or remove integrations safely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stability:&lt;/strong&gt; a broken plugin can’t crash the app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR friendly:&lt;/strong&gt; browser-only code stays browser-side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testable:&lt;/strong&gt; mock any plugin easily in unit tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainable:&lt;/strong&gt; cross-cutting logic lives in one place&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;In one of our enterprise apps, we had six different analytics SDKs, all fighting for control of &lt;code&gt;window.dataLayer&lt;/code&gt;.&lt;br&gt;
After moving to this plugin registry, we bootstrapped them cleanly, logged failures, and never touched them again.&lt;/p&gt;

&lt;p&gt;Multi-providers are the unsung hero of Angular’s DI system.&lt;br&gt;
They turn a monolith into a composable frontend, with zero external libraries and full type safety.&lt;/p&gt;




&lt;h3&gt;
  
  
  Looking Ahead
&lt;/h3&gt;

&lt;p&gt;Angular’s DI has been rock-solid for years, and it keeps improving around developer experience and performance.&lt;br&gt;
The good news is: the multi-provider pattern isn’t going anywhere.&lt;br&gt;
It’s stable, fast, and perfectly aligned with Angular’s standalone architecture.&lt;/p&gt;




&lt;h3&gt;
  
  
  💬 Final Thoughts
&lt;/h3&gt;

&lt;p&gt;If you’ve ever scaled an Angular app across multiple teams, you know how startup logic can spiral out of control.&lt;br&gt;
This pattern won’t just clean it up, it’ll future-proof it.&lt;/p&gt;

&lt;p&gt;Give it a try in your next project.&lt;br&gt;
You’ll never go back to manual “init” scripts again.&lt;/p&gt;

&lt;p&gt;Looking back, this pattern didn’t just clean up our code, it changed how we think about scalability in Angular.  &lt;/p&gt;

&lt;p&gt;Sometimes, true architecture isn’t about adding more frameworks, but about using what Angular already gives us, properly.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;em&gt;Anastasios Theodosiou&lt;/em&gt;&lt;br&gt;
Senior Software Engineer | Angular Certified Developer | Building Scalable Frontend Systems&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you found this useful, follow for more deep dives into real-world Angular architecture.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angulararchitecture</category>
      <category>dependencyinjection</category>
      <category>typescripttips</category>
      <category>frontendengineering</category>
    </item>
    <item>
      <title>Async in JavaScript, Served Hot: The Chef’s guide to the Event Loop</title>
      <dc:creator>Anastasios Theodosiou</dc:creator>
      <pubDate>Thu, 24 Jul 2025 23:08:20 +0000</pubDate>
      <link>https://dev.to/atheodosiou/async-in-javascript-served-hot-the-chefs-guide-to-the-event-loop-536a</link>
      <guid>https://dev.to/atheodosiou/async-in-javascript-served-hot-the-chefs-guide-to-the-event-loop-536a</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is the JavaScript Event Loop?&lt;/strong&gt;&lt;br&gt;
The event loop is a mechanism provided by the JavaScript environment (like your browser or Node.js) that enables asynchronous programming. It manages how and when different tasks—such as timers, promises, and event callbacks—get executed, making sure that everything happens in the right order, even though JavaScript itself can only do one thing at a time.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Imagine you walk into a kitchen. There’s a chef, focused and busy. He’s great at his job, but here’s his one weird rule: &lt;strong&gt;he can only do one thing at a time&lt;/strong&gt;. If he’s stirring a pot, he can’t chop onions. If he’s answering the phone, he can’t check the oven. This chef is JavaScript (single-threaded), running step by step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4zbfa4egu3hpzsp0p9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4zbfa4egu3hpzsp0p9c.png" alt="Chef and the call stack" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, of course, a kitchen isn’t just a chef. There are timers on the oven, deliveries to track, and maybe a robot that watches for when the fridge is left open. All these “background jobs” are the &lt;strong&gt;Web APIs&lt;/strong&gt;, not part of the chef’s mind, but part of the kitchen he works in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkfbznci3nu408ywhf12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkfbznci3nu408ywhf12.png" alt="Chef reading the call stack" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, here’s the crucial part:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The event loop, the trays where jobs wait, and the Web APIs are all provided by the kitchen itself—the environment.&lt;/strong&gt;&lt;br&gt;
They’re &lt;em&gt;not&lt;/em&gt; part of the chef’s brain (the JavaScript engine, like V8 or SpiderMonkey). This means that when you write JavaScript, the chef can only follow instructions; he needs the kitchen (the browser, Node.js, etc.) to do all the real-world, slow, or event-based jobs.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;How do the Chef and Kitchen communicate? (The Real Story)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s make this concrete. When the chef encounters a task he can’t do himself—like setting a timer or making an HTTP request—he turns to his helpers. Here’s how the whole process actually unfolds:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chef hits an async task:&lt;/strong&gt;&lt;br&gt;
Suppose your code says &lt;code&gt;setTimeout&lt;/code&gt;. The chef (JS engine) can’t actually wait or time things. So, he asks the kitchen’s timer (Web API): “Please handle this, and let me know when time’s up.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The helper (Web API) takes over:&lt;/strong&gt;&lt;br&gt;
The kitchen’s timer starts counting, &lt;em&gt;completely independently&lt;/em&gt; from the chef, who just moves on. The chef doesn’t care about the timer anymore—he has other work to do.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When the timer is done:&lt;/strong&gt;&lt;br&gt;
The kitchen’s timer writes a note: “This task is ready!” But the kitchen can’t just barge in and interrupt the chef; that would mess up the recipe! So the kitchen puts the note on the &lt;strong&gt;callback queue&lt;/strong&gt; (the tray).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The event loop keeps watch:&lt;/strong&gt;&lt;br&gt;
There’s an invisible manager (the event loop) who keeps checking:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;“Is the chef busy with the main recipe (call stack)?”&lt;/li&gt;
&lt;li&gt;“If not, is there a note on any tray (macrotask or microtask queue)?”
 If yes, the manager gives the next task from the tray to the chef.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The chef never talks directly to the helpers again:&lt;/strong&gt;
The communication always goes through the trays and the event loop. The chef (JavaScript engine) and the kitchen (environment) are separate; they only “talk” via these shared trays.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same thing happens for things like HTTP requests, file reading, DOM events, or geolocation—the chef delegates to the kitchen’s specialized helper (Web API), and when they’re done, the callback goes on the tray, waiting its turn.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Code Analogy: The communication in action&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s see it step by step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step 1: Start Cooking&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Chef asks the kitchen to start a timer&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step 5: Cake is ready!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Chef asks the kitchen to fetch groceries&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://some.api/ingredients&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step 6: Groceries arrived!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step 2: Chopping veggies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Chef queues a promise microtask&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step 4: Finished a quick microtask!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Step 3: Making sauce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What really happens?
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The chef prints Step 1.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;He asks the kitchen to start a timer (&lt;code&gt;setTimeout&lt;/code&gt;). The timer runs in the kitchen (Web API land).&lt;/li&gt;
&lt;li&gt;He asks the kitchen to fetch groceries. The fetch starts in the kitchen, out of sight for the chef.&lt;/li&gt;
&lt;li&gt;He sees a Promise. That goes on a special “microtask” tray.&lt;/li&gt;
&lt;li&gt;He prints Step 2 and Step 3, continuing his work.&lt;/li&gt;
&lt;li&gt;Now, he’s done with the immediate recipe. The event loop checks the microtask tray first. The Promise callback is there, so he handles it: prints Step 4.&lt;/li&gt;
&lt;li&gt;Then, the chef looks to the regular tray. Maybe the timer is done (after 1s), so Step 5 gets printed.&lt;/li&gt;
&lt;li&gt;When the groceries arrive, their callback (from fetch) is also placed on the regular tray (macrotask queue), so Step 6 prints after everything else that’s already waiting.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Order of output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1: Start Cooking
Step 2: Chopping veggies
Step 3: Making sauce
Step 4: Finished a quick microtask!
Step 5: Cake is ready!         // after 1 second
Step 6: Groceries arrived!     // when the fetch completes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x92kbogwivww5lyvgfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x92kbogwivww5lyvgfn.png" alt="Hand-drawn chef analogy visualizing JavaScript’s event loop: shows a chef putting a task in the oven (setTimeout), a tray for macrotasks, and the event loop handing the task back to the chef for execution." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the chef (JS engine) and the kitchen helpers (Web APIs) coordinate everything &lt;em&gt;without&lt;/em&gt; ever stepping on each other’s toes, thanks to the trays and the event loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;A Peek behind the kitchen door: Microtasks vs Macrotasks&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94yv9i1z87knv5o40atq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94yv9i1z87knv5o40atq.png" alt="Hand-drawn chef analogy showing the JavaScript event loop’s microtask and macrotask queues, with callbacks waiting in trays." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might wonder: Why are there two trays?&lt;br&gt;
Well, in JavaScript, &lt;strong&gt;microtasks&lt;/strong&gt; (promises, mutation observers, queueMicrotask) have higher priority. After every batch of main recipe steps, the chef &lt;em&gt;always&lt;/em&gt; clears the microtask tray before he looks at the macrotask tray (timers, fetch, events). This guarantees that microtasks like resolved promises happen as soon as possible, before any new timers or events.&lt;/p&gt;
&lt;h4&gt;
  
  
  Common Gotchas: Where most developers get surprised
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;setTimeout(..., 0) doesn’t run immediately!&lt;/strong&gt;&lt;br&gt;
Even with a zero delay, setTimeout callbacks are always placed in the macrotask queue, so all microtasks (like Promise callbacks) will run first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Promise callbacks (“microtasks”) always run before timers (“macrotasks”).&lt;/strong&gt;&lt;br&gt;
If you have both in your code, the promise’s &lt;code&gt;.then()&lt;/code&gt; will execute before setTimeout, every time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Long chains of microtasks can block everything else.&lt;/strong&gt;&lt;br&gt;
If you keep queuing microtasks inside other microtasks, the event loop can get “stuck” processing them, delaying other tasks like timers or UI updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Async code doesn’t mean “in parallel.”&lt;/strong&gt;&lt;br&gt;
JavaScript is single-threaded. “Async” just means “wait your turn”—not “run at the same time.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event callbacks (like click or keydown) also wait their turn!&lt;/strong&gt;&lt;br&gt;
UI events are queued just like any other macrotask—they don’t interrupt code that’s already running.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;What about events like clicks or keyboard input?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;That’s the kitchen’s job too!&lt;br&gt;
When you click a button, the kitchen sees it and, when the chef is free, puts a note on the macrotask tray. The event loop delivers it to the chef, who then runs your click handler code.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;And If Things Get Crazy? (Infinite Microtasks, Starvation, etc)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s a secret:&lt;br&gt;
If you keep adding microtasks inside other microtasks, the chef could get stuck on the VIP tray forever, and never move on to macrotasks!&lt;br&gt;
That’s called &lt;strong&gt;starvation&lt;/strong&gt;. JavaScript tries to avoid it, but it’s up to you as a developer to be careful—don’t flood the microtask queue with endless callbacks.&lt;/p&gt;


&lt;h2&gt;
  
  
  Advanced Extras: Animation Frames, Node.js, and more complex examples
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;What about requestAnimationFrame?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Besides microtasks and macrotasks, browsers have a special queue for animation callbacks: the &lt;strong&gt;animation frame queue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you call &lt;code&gt;requestAnimationFrame(callback)&lt;/code&gt;, the callback runs &lt;em&gt;before&lt;/em&gt; the next browser repaint—after all microtasks and regular tasks for the frame are finished.&lt;/p&gt;

&lt;p&gt;This is perfect for smooth animations, because the browser chooses the best moment for your code to update the UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analogy:&lt;/strong&gt;&lt;br&gt;
The chef (JS engine) has finished his trays, but before starting new work, the kitchen manager (event loop) checks, "Anything for the animation?" If so, it gets handled right before serving the next meal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;D&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output order:&lt;/strong&gt;&lt;br&gt;
A&lt;br&gt;
D&lt;br&gt;
C&lt;br&gt;
B&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What about Node.js? (Event Loop Phases)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Node.js runs JavaScript outside the browser, and its event loop is split into phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Timers:&lt;/strong&gt; Executes &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt; callbacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pending Callbacks:&lt;/strong&gt; I/O callbacks deferred to the next loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poll:&lt;/strong&gt; Retrieves new I/O events and executes their callbacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check:&lt;/strong&gt; Executes &lt;code&gt;setImmediate&lt;/code&gt; callbacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close Callbacks:&lt;/strong&gt; For closed connections or resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Promise microtasks&lt;/strong&gt; always run between these phases, just like in the browser.&lt;/p&gt;

&lt;p&gt;If you’re digging into Node, check &lt;a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/" rel="noopener noreferrer"&gt;the official Node.js guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Complex Code Execution: Step-by-step Examples&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Example 1: Chained Promises vs setTimeout
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Order:&lt;/strong&gt;&lt;br&gt;
1&lt;br&gt;
5&lt;br&gt;
3&lt;br&gt;
4&lt;br&gt;
2&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 2: setTimeout inside a Promise
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Order:&lt;/strong&gt;&lt;br&gt;
a&lt;br&gt;
e&lt;br&gt;
b&lt;br&gt;
d&lt;br&gt;
c&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 3: Starvation with Endless Microtasks
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;recur&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Try count &amp;lt; 1_000_000 for real starvation!&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recur&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;recur&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Timer fired!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you remove the limit, the timer may never fire—the microtask queue "starves" the timer!&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 4: Animation, Microtasks, and Timers Together
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anim&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;timeout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Order:&lt;/strong&gt;&lt;br&gt;
start&lt;br&gt;
end&lt;br&gt;
promise&lt;br&gt;
timeout&lt;br&gt;
anim&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Tip:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When in doubt, remember the order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Synchronous code (call stack)&lt;/li&gt;
&lt;li&gt;Microtasks (promises)&lt;/li&gt;
&lt;li&gt;Animation frame queue (in browser)&lt;/li&gt;
&lt;li&gt;Macrotasks (timers, events)&lt;/li&gt;
&lt;li&gt;Repeat!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion: What should you take away?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The JavaScript event loop is what makes single-threaded JavaScript feel so much more powerful—like a chef that always keeps the kitchen running smoothly, no matter how many jobs are waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So next time you see async code, timers, or promises, remember:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The chef (JavaScript) never gets overwhelmed, because the kitchen (the environment) and the event loop keep things organized.&lt;/li&gt;
&lt;li&gt;If something doesn’t run when you expect, check which tray it’s waiting on!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to see this in action? Try out your own code examples in the browser console, or grab one of the above and play with the order. The best way to master the event loop is to experiment.&lt;/p&gt;

&lt;p&gt;If you have questions, favorite “gotchas,” or other analogies that helped you, share them in the comments below!&lt;br&gt;
Let’s help more developers see that behind every callback and promise, there’s just a chef and a kitchen doing their best work—one task at a time.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>eventloop</category>
    </item>
    <item>
      <title>Optimizing Angular Change Detection with OnPush: Skipping Subtrees for Performance</title>
      <dc:creator>Anastasios Theodosiou</dc:creator>
      <pubDate>Tue, 01 Apr 2025 21:40:21 +0000</pubDate>
      <link>https://dev.to/atheodosiou/optimizing-angular-change-detection-with-onpush-skipping-subtrees-for-performance-45md</link>
      <guid>https://dev.to/atheodosiou/optimizing-angular-change-detection-with-onpush-skipping-subtrees-for-performance-45md</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Master Angular’s OnPush strategy to build faster, more efficient apps. Learn how to skip unnecessary checks, use Signals for reactivity, and apply real-world best practices with minimal boilerplate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Originally published on: &lt;a href="https://anastasios.theodosiou.me/blog" rel="noopener noreferrer"&gt;anastasios.theodosiou.me&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Angular's default change detection checks every component on each cycle, which can be inefficient in large apps.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;OnPush&lt;/code&gt; strategy improves performance by skipping subtrees unless certain triggers (like new inputs or events) occur.&lt;/li&gt;
&lt;li&gt;Signals (introduced in Angular 16 and improved in Angular 17+) are reactive primitives that work seamlessly with OnPush.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;signal()&lt;/code&gt;, &lt;code&gt;input()&lt;/code&gt;, and the &lt;code&gt;async&lt;/code&gt; pipe enables highly performant and reactive components with minimal boilerplate.&lt;/li&gt;
&lt;li&gt;This article explores practical code examples, common pitfalls, and best practices for combining OnPush and Signals effectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For Angular developers aiming to boost performance and reduce unnecessary UI checks, mastering the OnPush change detection strategy is essential. This article guides you through how it works, why it matters, and how to leverage it effectively.&lt;/p&gt;

&lt;p&gt;Angular’s change detection is the mechanism that keeps the UI in sync with data. By default, Angular uses the &lt;strong&gt;Default&lt;/strong&gt; (or “CheckAlways”) change detection strategy, which means every time a change detection cycle runs (e.g. after a user event, timer, HTTP response, etc.), Angular will traverse &lt;em&gt;the entire component tree&lt;/em&gt; from the root, checking each component’s template bindings for changes. This default strategy is simple and ensures all changes are caught, but it can become inefficient in large applications because &lt;strong&gt;every component is checked on every cycle&lt;/strong&gt;, even if most of them haven’t changed (&lt;a href="https://mokkapps.de/blog/the-last-guide-for-angular-change-detection-you-will-ever-need" rel="noopener noreferrer"&gt;The Last Guide For Angular Change Detection You'll Ever Need - Michael Hoffmann | Michael Hoffmann&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To improve performance, Angular provides the &lt;strong&gt;OnPush&lt;/strong&gt; change detection strategy. The OnPush strategy allows Angular to &lt;strong&gt;skip entire subtrees&lt;/strong&gt; of the component tree during change detection if it knows those components didn’t change. In other words, with OnPush, Angular will not always check a component and its children – it will “opt-out” of checking that subtree unless certain conditions are met. This can greatly reduce the work done in each change detection cycle, making your application more efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does OnPush work?&lt;/strong&gt; A component set to &lt;code&gt;ChangeDetectionStrategy.OnPush&lt;/code&gt; tells Angular: &lt;em&gt;“Only check me for changes if you have a specific reason.”&lt;/em&gt; Those reasons include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New Input Reference:&lt;/strong&gt; The component receives new @Input() values (Angular compares the new value with the old value using &lt;code&gt;===&lt;/code&gt; equality) (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). If an input’s reference has changed (or primitive value changed), Angular will mark that OnPush component as needing check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An Event in the Component or Its Children:&lt;/strong&gt; Any user event or output emitted &lt;em&gt;within&lt;/em&gt; that component (or any of its child components) will mark the component for check (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). For example, a click event on one of its buttons will cause that OnPush component to run change detection for itself and its subtree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual Trigger:&lt;/strong&gt; A call to the component’s &lt;code&gt;ChangeDetectorRef&lt;/code&gt; methods, such as &lt;code&gt;markForCheck()&lt;/code&gt; or &lt;code&gt;detectChanges()&lt;/code&gt;, can explicitly mark the component for checking or trigger a check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async Pipe Emissions:&lt;/strong&gt; If the template uses the &lt;code&gt;async&lt;/code&gt; pipe to subscribe to an Observable/Promise, a new value emission will mark the component for check automatically (the async pipe internally calls &lt;code&gt;markForCheck&lt;/code&gt; when a new value arrives (&lt;a href="https://blog.lacolaco.net/posts/en/angular-app-reactiveness" rel="noopener noreferrer"&gt;Angular: Test Reactiveness with OnPush strategy | lacolaco's marginalia&lt;/a&gt;)).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If none of these conditions occur, an OnPush component (and its children) will simply be skipped during change detection. In contrast, the Default strategy (used unless specified otherwise) will check the component every time regardless. This makes OnPush a powerful tool to optimize performance by &lt;strong&gt;skipping unnecessary checks&lt;/strong&gt; when you know a component’s data updates are limited to specific triggers.&lt;/p&gt;

&lt;p&gt;In the following sections, we’ll dive deeper into how change detection works under Default vs OnPush, examine common scenarios with OnPush, and look at code examples, best practices, and pitfalls when using OnPush to skip component subtrees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Change Detection Scenarios
&lt;/h2&gt;

&lt;p&gt;To effectively use OnPush, it’s important to understand how Angular’s change detection behaves in different scenarios. Below we detail key scenarios and how the OnPush strategy affects what gets checked or skipped in each case.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Events in a Component with Default Change Detection
&lt;/h3&gt;

&lt;p&gt;When an event is handled in a component that uses the &lt;strong&gt;Default&lt;/strong&gt; change detection (the usual behavior), Angular will run change detection for &lt;strong&gt;the entire component tree&lt;/strong&gt; from the root down (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). It doesn’t assume anything about what might have changed, so it checks everything. However, even in this case, Angular is smart about OnPush descendants: any child subtree rooted at an OnPush component will be &lt;strong&gt;skipped&lt;/strong&gt; if that OnPush component has not received new inputs during this cycle (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does this mean?&lt;/strong&gt; Suppose you have a Default parent component that contains some OnPush child components. If a user clicks a button in the parent (triggering an event in the Default component), Angular will run through all components. The parent and other Default components will update as usual. For each OnPush child, Angular will check whether its inputs changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;strong&gt;no new inputs&lt;/strong&gt; were passed to that OnPush child, Angular will skip checking that child and its subtree to save time (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;If the parent’s event caused a new input value to flow into the OnPush child, then that child will be checked (since it meets the “new input” condition).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, an event in a Default component triggers a full change detection pass, but OnPush components down the line only update if they got new input. Otherwise, those subtrees remain unchanged (they are essentially “frozen” for that cycle, which is good for performance).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Example:&lt;/strong&gt; &lt;em&gt;Default parent with an OnPush child&lt;/em&gt;. In the example below, the parent component uses default change detection and has an OnPush child. The parent’s button click triggers change detection across the app. The OnPush child’s &lt;code&gt;ngDoCheck&lt;/code&gt; will &lt;strong&gt;not&lt;/strong&gt; run on click because its input (&lt;code&gt;data&lt;/code&gt;) isn’t changing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// OnPush child component&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Child Value: {{ data.value }}&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChildComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;ngDoCheck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ChildComponent checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Default change detection parent&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parent-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;child-comp [data]="staticData"&amp;gt;&amp;lt;/child-comp&amp;gt;
    &amp;lt;button (click)="onClick()"&amp;gt;Parent Click&amp;lt;/button&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParentComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;staticData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ParentComponent clicked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// No change to staticData input; just an event trigger&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, clicking the button logs “ParentComponent clicked”. Angular runs change detection. &lt;code&gt;ParentComponent&lt;/code&gt; is checked (because it’s default), but &lt;code&gt;ChildComponent&lt;/code&gt; is OnPush and its @Input &lt;code&gt;staticData&lt;/code&gt; still references the same object. Angular skips checking &lt;code&gt;ChildComponent&lt;/code&gt;’s view since &lt;strong&gt;no new input reference&lt;/strong&gt; was passed (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). The console will &lt;strong&gt;not&lt;/strong&gt; show “ChildComponent checked” on these clicks, indicating the OnPush child was skipped. (If &lt;code&gt;ParentComponent&lt;/code&gt; had changed the &lt;code&gt;staticData&lt;/code&gt; reference, then &lt;code&gt;ChildComponent&lt;/code&gt; would be checked and updated.)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Events in a Component with OnPush
&lt;/h3&gt;

&lt;p&gt;Now consider an event (like a click) happening inside a component that itself uses &lt;strong&gt;OnPush&lt;/strong&gt;. In this scenario, Angular will still trigger a change detection cycle for the whole application (Zone.js always initiates change detection for the entire tree on any event). The difference is in which components actually get updated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The OnPush component that had the event (and its children) will always be checked because the event marks it as “dirty” (an event counts as a change trigger within that component’s subtree) (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Other parts of the tree &lt;strong&gt;not in that component’s subtree&lt;/strong&gt; will be skipped &lt;em&gt;if&lt;/em&gt; they are OnPush and have no new inputs (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, Angular runs through the component tree, but &lt;strong&gt;ignores other OnPush subtrees that aren’t affected by this event&lt;/strong&gt;. Any Default components anywhere will still run (since default always runs), but OnPush components unrelated to the event remain unchanged.&lt;/p&gt;

&lt;p&gt;A common example: Suppose your root component contains two separate OnPush components (siblings). If an event happens in one of them, the other OnPush component (which didn’t receive any input and wasn’t part of the event) will be skipped during change detection (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). This way, Angular limits the work to the branch of the tree where the event occurred.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Example:&lt;/strong&gt; &lt;em&gt;Two OnPush siblings, event in one&lt;/em&gt;. In the example below, &lt;code&gt;MainComponent&lt;/code&gt; and &lt;code&gt;SideComponent&lt;/code&gt; are both OnPush and rendered by a parent. A button click inside &lt;code&gt;MainComponent&lt;/code&gt; triggers an event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;button (click)="onClick()"&amp;gt;Do Action&amp;lt;/button&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MainComponent button clicked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;ngDoCheck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MainComponent checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;side-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Side static content&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SideComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;ngDoCheck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SideComponent checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Parent template using both&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;main-comp&amp;gt;&amp;lt;/main-comp&amp;gt; &amp;lt;side-comp&amp;gt;&amp;lt;/side-comp&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user clicks the button in &lt;code&gt;MainComponent&lt;/code&gt;, we’ll see “MainComponent button clicked” and then “MainComponent checked” in the console. We &lt;strong&gt;will not&lt;/strong&gt; see “SideComponent checked” because &lt;code&gt;SideComponent&lt;/code&gt; (OnPush) had no reason to run. Angular runs change detection for the whole tree, but it &lt;strong&gt;ignores&lt;/strong&gt; the &lt;code&gt;SideComponent&lt;/code&gt; subtree since it’s OnPush with no new input and the event occurred outside of it (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). Thus, &lt;code&gt;SideComponent&lt;/code&gt; is skipped entirely in that cycle. (If &lt;code&gt;SideComponent&lt;/code&gt; were Default, it would run on every cycle regardless.)&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Events in a Descendant of an OnPush Component
&lt;/h3&gt;

&lt;p&gt;This scenario involves nested OnPush components: an OnPush parent with an OnPush (or Default) child, where an event originates in the child. For example, imagine &lt;code&gt;ParentComponent&lt;/code&gt; is OnPush and inside it is a &lt;code&gt;ChildComponent&lt;/code&gt; (which could also be OnPush). If an event (like a click) happens in the child, how does it affect the parent?&lt;/p&gt;

&lt;p&gt;In Angular, &lt;strong&gt;events bubble up through the component tree&lt;/strong&gt;, and Angular will mark the entire chain up to the root as needing change detection. So, if an event is handled in a descendant of an OnPush component, that ancestor OnPush component &lt;strong&gt;will be checked&lt;/strong&gt; as well, even if its inputs didn’t change (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). In our example, the event in the child causes Angular to check the child (obviously) &lt;em&gt;and&lt;/em&gt; the OnPush parent, because the child is part of the parent’s view.&lt;/p&gt;

&lt;p&gt;Thus, an event in an OnPush subtree ensures that subtree is not skipped – the OnPush boundary is effectively breached by the event. Angular will run change detection for the child, the OnPush parent, and upwards through any other ancestors (default or OnPush). This makes sense: if something happened in the child, the parent might also need to update (for instance, maybe the parent template also binds to some property that could change as a result of the child event).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Example:&lt;/strong&gt; &lt;em&gt;OnPush parent and child, event in child&lt;/em&gt;. Below, both parent and child use OnPush:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;button (click)="onChildClick()"&amp;gt;Child Click&amp;lt;/button&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChildComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;onChildClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ChildComponent button clicked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;ngDoCheck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ChildComponent checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parent-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;child-comp&amp;gt;&amp;lt;/child-comp&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParentComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;ngDoCheck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ParentComponent checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user clicks the button in &lt;code&gt;ChildComponent&lt;/code&gt;, Angular will process that event. Even though &lt;code&gt;ParentComponent&lt;/code&gt; is OnPush with no direct input change, the act of handling an event in its descendant marks it for checking. In the console, you’ll see both “ChildComponent checked” &lt;strong&gt;and&lt;/strong&gt; “ParentComponent checked”. The parent was not skipped – Angular checked it because the event happened in its view hierarchy (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). This confirms that an OnPush component is always checked when an event occurs anywhere in its subtree.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Receiving New Inputs in an OnPush Component
&lt;/h3&gt;

&lt;p&gt;The final common scenario is when an OnPush component receives a new value for one of its &lt;code&gt;@Input&lt;/code&gt; properties from its parent. This is one of the primary triggers for OnPush change detection. Angular will compare the new input value with the previous value; if it’s different (by reference or value for primitives), Angular will mark that OnPush component as needing to be checked (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;What happens then is that during the next change detection cycle, Angular will run change detection for that component (and its children). Importantly, this does &lt;strong&gt;not&lt;/strong&gt; force checks of other sibling OnPush components. It only affects the subtree rooted at the component that got the new input.&lt;/p&gt;

&lt;p&gt;For example, if &lt;code&gt;ParentComponent&lt;/code&gt; (could be Default or OnPush) passes a new object or new primitive value to &lt;code&gt;ChildComponent&lt;/code&gt; which is OnPush, Angular will check &lt;code&gt;ChildComponent&lt;/code&gt; (update its bindings in the view) on the next cycle. Other OnPush components that didn’t get new inputs remain untouched (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Example:&lt;/strong&gt; &lt;em&gt;Parent provides new input to OnPush child&lt;/em&gt;. In this example, the child is OnPush and simply displays an input value. The parent (could be default here) updates the input periodically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Counter: {{ counter }}&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChildComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;ngOnChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ChildComponent input changed to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parent-comp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;child-comp [counter]="count"&amp;gt;&amp;lt;/child-comp&amp;gt;
    &amp;lt;button (click)="increment()"&amp;gt;Increment&amp;lt;/button&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParentComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each time the parent’s button is clicked, &lt;code&gt;ParentComponent.increment()&lt;/code&gt; runs and changes the value of &lt;code&gt;count&lt;/code&gt;. This new value flows into the &lt;code&gt;ChildComponent&lt;/code&gt; via the binding &lt;code&gt;[counter]="count"&lt;/code&gt;. Since &lt;code&gt;ChildComponent&lt;/code&gt; is OnPush and it &lt;strong&gt;received a new input value&lt;/strong&gt;, Angular will run change detection for &lt;code&gt;ChildComponent&lt;/code&gt; on that cycle (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). You’ll see &lt;code&gt;ngOnChanges&lt;/code&gt; log the new counter value each time. Only the &lt;code&gt;ChildComponent&lt;/code&gt; subtree is checked in response to this input change; if there were other OnPush components elsewhere that didn’t get new inputs or events, they would not run. If &lt;code&gt;ChildComponent&lt;/code&gt; had its own OnPush children, they would likewise update only if their inputs changed or they had their own events.&lt;/p&gt;

&lt;p&gt;One thing to note: the check is triggered by changing the &lt;em&gt;reference or primitive value&lt;/em&gt;. If &lt;code&gt;count&lt;/code&gt; were an object and we mutated one of its properties without assigning a new object, Angular’s input comparison might not catch it. We’ll cover this pitfall later.&lt;/p&gt;

&lt;p&gt;With these scenarios in mind, you can see that using OnPush strategically “cuts off” parts of the component tree from needless checks. Next, we’ll look at how to leverage this for performance and what practices to follow when using OnPush in your Angular apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Optimizations and Best Practices
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;ChangeDetectionStrategy.OnPush&lt;/code&gt; can significantly improve performance by reducing the amount of work Angular does in each change detection cycle. Here are some best practices and tips to use OnPush effectively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adopt Immutability for Inputs:&lt;/strong&gt; When using OnPush, it’s highly recommended to treat input data as immutable. That means instead of mutating objects/arrays, create new instances when data changes. This way, when you pass new data to an OnPush component, the input’s reference changes and Angular knows to update the component. For example, if you have an &lt;code&gt;@Input() items: Item[]&lt;/code&gt; and you want to update it, prefer &lt;code&gt;this.items = [...this.items, newItem]&lt;/code&gt; over &lt;code&gt;this.items.push(newItem)&lt;/code&gt;. The former changes the reference, triggering OnPush detection, whereas the latter mutates in place (same reference) and would not trigger an update.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use OnPush for Pure/Presentational Components:&lt;/strong&gt; Components that act as presentational (dumb) components or primarily display data based on @Inputs are great candidates for OnPush. They don’t manage their own internal state much; they just render inputs and maybe emit outputs. Marking them OnPush ensures they only re-render when input data actually changes or in response to user interaction within them. This can greatly cut down unnecessary checks for large lists of components (like a list of item rows, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage the Async Pipe:&lt;/strong&gt; When dealing with Observables in Angular, using the &lt;code&gt;async&lt;/code&gt; pipe in the template is a best practice – especially so with OnPush. The async pipe will subscribe to an Observable and push values into the template, and &lt;strong&gt;crucially, it will call &lt;code&gt;markForCheck()&lt;/code&gt; on the component when a new value is emitted (&lt;a href="https://blog.lacolaco.net/posts/en/angular-app-reactiveness" rel="noopener noreferrer"&gt;Angular: Test Reactiveness with OnPush strategy | lacolaco's marginalia&lt;/a&gt;)&lt;/strong&gt;. This means your OnPush component will update correctly when the Observable emits, without any manual intervention. This is cleaner than subscribing in the component class and then having to call &lt;code&gt;changeDetectorRef.markForCheck()&lt;/code&gt; yourself. In short, &lt;strong&gt;async pipe + OnPush&lt;/strong&gt; is a potent combination for automatic, efficient UI updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Know When to Manually Trigger Change Detection:&lt;/strong&gt; Even with OnPush, there are times you might need to manually tell Angular to check a component. The two main methods are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ChangeDetectorRef.markForCheck()&lt;/code&gt;: This will mark the component (and its ancestors) as dirty, so that on the &lt;strong&gt;next&lt;/strong&gt; change detection cycle, Angular will include them in the check (&lt;a href="https://stackoverflow.com/questions/57380682/angular-markforcheck-vs-detectchanges" rel="noopener noreferrer"&gt;typescript - Angular markForCheck vs detectChanges - Stack Overflow&lt;/a&gt;). Use this when you’ve updated some state that Angular didn’t catch (e.g. you imperatively changed a component’s input or a bound data structure) and you want Angular to pick it up in the next round. Marking for check is &lt;strong&gt;asynchronous&lt;/strong&gt; with respect to the current code execution – it doesn’t immediately run change detection, it just schedules the component for checking soon.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ChangeDetectorRef.detectChanges()&lt;/code&gt;: This method &lt;strong&gt;immediately&lt;/strong&gt; triggers change detection for this component and its children, synchronously, at the moment you call it (&lt;a href="https://stackoverflow.com/questions/57380682/angular-markforcheck-vs-detectchanges" rel="noopener noreferrer"&gt;typescript - Angular markForCheck vs detectChanges - Stack Overflow&lt;/a&gt;). It’s like telling Angular “check this view right now.” This can be useful if you need to ensure the UI reflects some changes instantaneously (for example, after an event callback outside of Angular’s zone, or in some complex timing scenarios). However, you should use this sparingly – calling &lt;code&gt;detectChanges()&lt;/code&gt; frequently or in a loop can hurt performance, since it bypasses Angular’s usual batching. Also, if used inside an ongoing change detection cycle, it can lead to the infamous "expression has changed after it was checked" error if not careful (because you might be triggering nested checks).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Which one to use?&lt;/strong&gt; In general, prefer &lt;code&gt;markForCheck()&lt;/code&gt; for most cases when dealing with OnPush components. It works with Angular’s natural change detection schedule and ensures minimal checks (it will only re-check the needed branch on the next cycle, coalescing multiple changes if they happen). Use &lt;code&gt;detectChanges()&lt;/code&gt; if you have a specific need to force an immediate check or to isolate change detection to a small part of the component tree manually. For example, after an external callback (like a setTimeout outside Angular’s NgZone), you might call &lt;code&gt;detectChanges()&lt;/code&gt; to update the view right away. If you find yourself calling &lt;code&gt;detectChanges()&lt;/code&gt; very often, consider if you can restructure your code to rely on Angular’s normal mechanism or the async pipe instead – too many manual calls can be a code smell indicating you’re fighting the framework.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid ChangeDetectorRef in Most Cases:&lt;/strong&gt; Ideally, if you design your component inputs and data flow in an Angular-friendly way (using immutable data and async pipes), you will rarely need to inject &lt;code&gt;ChangeDetectorRef&lt;/code&gt; and call these methods manually. The framework will handle it. It’s not “wrong” to use them – they exist for valid cases – but if you rely heavily on &lt;code&gt;markForCheck()&lt;/code&gt; or &lt;code&gt;detectChanges()&lt;/code&gt;, double-check if there’s a more idiomatic approach. For instance, if you find you have to call &lt;code&gt;markForCheck()&lt;/code&gt; after an @Input property mutation, that’s a hint you should be changing the @Input by reference instead of mutating.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measure Performance Gains:&lt;/strong&gt; OnPush can reduce the amount of work on each tick, but it also adds some complexity to your code (you have to manage change detection triggers consciously). Use tools like Angular DevTools or the performance timeline to measure if using OnPush in certain parts of your app yields visible performance improvements. Focus OnPush where the gains are significant (large lists, frequent updates, etc.). There’s no need to use OnPush everywhere by default – many apps perform just fine with the default strategy, but OnPush is there when you need that extra boost.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;By following these practices – using immutable data patterns, the async pipe, and judicious manual change detection – you can harness OnPush to make your Angular application more performant. Next, we’ll discuss some tricky edge cases and pitfalls to be aware of when using OnPush.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge Cases and Common Pitfalls
&lt;/h2&gt;

&lt;p&gt;While OnPush can speed up your app, it may surprise you if you’re not aware of certain gotchas. Here are some common pitfalls and how to handle them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mutating Object Inputs Without Changing Reference:&lt;/strong&gt; If an OnPush component gets an object (or array) as an @Input and you mutate a property of that object &lt;strong&gt;without assigning a new object&lt;/strong&gt;, Angular will not detect any change. This is because the reference of the object remains the same (&lt;code&gt;===&lt;/code&gt; comparison sees no difference) (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manually Updating @Input Properties:&lt;/strong&gt; Sometimes you might grab a child component instance via &lt;code&gt;@ViewChild&lt;/code&gt; and set an @Input property on it directly in code, or otherwise set an input property outside of the normal template binding. If that child is OnPush, Angular won’t automatically know to run change detection for it, since it wasn’t updated through the usual binding mechanism. The OnPush child remains in its previous state (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). For example: &lt;code&gt;@ViewChild(ChildComp) child: ChildComp; ... this.child.someInput = newVal; this.childCdr.markForCheck();&lt;/code&gt;. Alternatively, redesign to pass data via Input binding or service so Angular is aware of the change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using Observables with OnPush (without async pipe):&lt;/strong&gt; If you subscribe to an Observable inside an OnPush component (in the component class) and update some state when it emits, that won’t automatically trigger change detection. For instance, you inject a service, subscribe to a stream in &lt;code&gt;ngOnInit&lt;/code&gt;, and set a component field. In OnPush, unless that subscription callback calls &lt;code&gt;markForCheck()&lt;/code&gt; or the value is passed in via Input, the view might not update. &lt;strong&gt;Solution:&lt;/strong&gt; Again, the async pipe is your friend – it handles subscription and marking for check for you. But if you must subscribe in code (maybe to combine streams or use a single subscription for multiple values), then ensure you call &lt;code&gt;this.cdr.markForCheck()&lt;/code&gt; inside the subscription handler to notify Angular of new data. This way the OnPush component will check its template on the next cycle when the Observable fires a value. (Alternatively, consider using &lt;code&gt;detectChanges()&lt;/code&gt; if you need the update immediately during that subscription callback, though usually markForCheck is sufficient.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Async Pipe Multiple Emissions:&lt;/strong&gt; A related note – the async pipe will mark OnPush for check on each new emission. If an Observable emits frequently (e.g., many times a second), each emission schedules a change detection. This is usually fine (Angular can handle a lot of checks quickly), but be mindful of extremely high-frequency streams, as they could still cause performance issues if the UI work is heavy each time. In such cases, consider throttling/debouncing the stream or using strategies to drop frames (if applicable).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Calling &lt;code&gt;detectChanges()&lt;/code&gt; at the Wrong Time:&lt;/strong&gt; If you call &lt;code&gt;changeDetectorRef.detectChanges()&lt;/code&gt; while Angular is already in the middle of a change detection cycle (for example, from within &lt;code&gt;ngOnInit&lt;/code&gt; of a child while the parent is still being checked), you can run into the &lt;em&gt;ExpressionChangedAfterItHasBeenChecked&lt;/em&gt; error. This is because you’re forcing an additional check in the middle of Angular’s normal check, confusing its before/after comparison. To avoid this, only call &lt;code&gt;detectChanges()&lt;/code&gt; in places Angular isn’t actively checking (such as in a &lt;code&gt;setTimeout&lt;/code&gt; callback, or in response to an event that Angular doesn’t know about). A safer approach if you need to trigger an extra check during initialization is to use &lt;code&gt;setTimeout(() =&amp;gt; cdr.detectChanges())&lt;/code&gt; or &lt;code&gt;Promise.resolve().then(() =&amp;gt; cdr.detectChanges())&lt;/code&gt; to postpone it to the next macrotask, after Angular’s cycle is done. Or simply design the component not to require this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Default Change Detection inside OnPush subtree:&lt;/strong&gt; If you have a mixture of strategies (some child components default, some OnPush), remember that a Default-strategy child inside an OnPush parent will still &lt;strong&gt;not run&lt;/strong&gt; if the OnPush parent was skipped. The whole subtree is skipped if the parent OnPush doesn’t have a reason to run. This can be a pitfall: you might expect a Default child to always check, but if its OnPush ancestor isn’t running, it won’t check either. Essentially, OnPush “trickles down” – if a parent is skipped, all its descendants are skipped regardless of their own strategy. &lt;strong&gt;Solution:&lt;/strong&gt; Ensure that if you rely on a Default child to update, the OnPush parent is being marked dirty appropriately (perhaps via &lt;code&gt;markForCheck()&lt;/code&gt; when the child needs to update). Alternatively, consider making that child also OnPush for consistency, and manage its updates via inputs or events.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Being aware of these edge cases will help you avoid frustrating bugs where the UI doesn’t update. Most of these boil down to a simple rule: &lt;strong&gt;with OnPush, always change object references when you want updates, or explicitly tell Angular when something changes.&lt;/strong&gt; If you follow that rule, you’ll rarely run into issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagrams and Visual Aids
&lt;/h2&gt;

&lt;p&gt;Let’s visualize how OnPush can skip subtrees in a component tree. Consider the following component tree structure (OnPush components marked with "(OnPush)"):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AppComponent
├── HeaderComponent
├── MainComponent (OnPush)
│   ├── SearchComponent
│   └── ButtonComponent
└── LoginComponent (OnPush)
    └── DetailsComponent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a normal change detection cycle (Default strategy everywhere), if an event happens anywhere, Angular would check every component from &lt;code&gt;AppComponent&lt;/code&gt; down to &lt;code&gt;DetailsComponent&lt;/code&gt;. Now, let’s illustrate how OnPush alters this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event in a Default component (e.g. HeaderComponent):&lt;/strong&gt; Angular starts at &lt;code&gt;AppComponent&lt;/code&gt; and goes down. It will check &lt;code&gt;HeaderComponent&lt;/code&gt; (where the event occurred) and continue. When it gets to &lt;code&gt;MainComponent (OnPush)&lt;/code&gt;, since &lt;code&gt;MainComponent&lt;/code&gt; did not receive any new @Input, Angular &lt;strong&gt;skips&lt;/strong&gt; the entire &lt;code&gt;MainComponent&lt;/code&gt; subtree (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). The result is better performance by avoiding needless checks in &lt;code&gt;MainComponent&lt;/code&gt; and its children.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event in an OnPush component (e.g. MainComponent):&lt;/strong&gt; Angular runs the cycle for the whole tree, but now the event happened in &lt;code&gt;MainComponent&lt;/code&gt;. So &lt;code&gt;MainComponent&lt;/code&gt; &lt;strong&gt;will be checked&lt;/strong&gt; (it’s marked dirty due to the event). Its children &lt;code&gt;SearchComponent&lt;/code&gt; and &lt;code&gt;ButtonComponent&lt;/code&gt; will also be checked (since the subtree is active). Meanwhile, &lt;code&gt;LoginComponent&lt;/code&gt; (the other OnPush branch) is not part of this event’s subtree and got no new input, so Angular &lt;strong&gt;skips&lt;/strong&gt; &lt;code&gt;LoginComponent&lt;/code&gt; and its child (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). In effect, the &lt;code&gt;MainComponent&lt;/code&gt; branch runs, the &lt;code&gt;LoginComponent&lt;/code&gt; branch is ignored this time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event in a descendant of an OnPush (e.g. a button inside LoginComponent):&lt;/strong&gt; Suppose the event is deep in &lt;code&gt;LoginComponent&lt;/code&gt;’s template (&lt;code&gt;LoginComponent&lt;/code&gt; is OnPush). Angular will mark &lt;code&gt;LoginComponent&lt;/code&gt; as well as its ancestors for check. So &lt;code&gt;LoginComponent&lt;/code&gt; runs, and since its parent (&lt;code&gt;AppComponent&lt;/code&gt;) is also part of the view hierarchy, it will be checked too. However, &lt;strong&gt;other OnPush siblings&lt;/strong&gt;, such as &lt;code&gt;MainComponent&lt;/code&gt;, will &lt;strong&gt;be skipped&lt;/strong&gt; unless they received new inputs or had their own events — this is the core idea behind &lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;skipping component subtrees&lt;/a&gt;. In this cycle, only the &lt;code&gt;LoginComponent&lt;/code&gt; branch and its ancestors are re-evaluated — &lt;code&gt;MainComponent&lt;/code&gt; remains untouched because it’s an independent OnPush subtree not involved in the event.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New input to OnPush (e.g. AppComponent passes new data to MainComponent):&lt;/strong&gt; When &lt;code&gt;AppComponent&lt;/code&gt; updates an @Input bound to &lt;code&gt;MainComponent&lt;/code&gt;, Angular will, on the next cycle, check &lt;code&gt;MainComponent&lt;/code&gt; and its children (&lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;Skipping component subtrees • Angular&lt;/a&gt;). If &lt;code&gt;LoginComponent&lt;/code&gt; didn’t get anything new, it will be skipped. Essentially, only the &lt;code&gt;MainComponent&lt;/code&gt; subtree refreshes. If &lt;code&gt;LoginComponent&lt;/code&gt; also got a new input (say AppComponent also passed something new to it), then it would also refresh. OnPush ensures each subtree refreshes only if its direct inputs tell it to.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These scenarios can be visualized as portions of the tree turning “on” or “off” for change detection based on triggers. By looking at this tree, you can get a sense of how events or input changes cause certain branches to update while others remain untouched. This visual model reinforces the mental model: &lt;strong&gt;OnPush = don’t bother checking here unless something specific changed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(In a live diagram or flowchart, we could highlight which components get checked (e.g., green) and which are skipped (red) for each scenario. You might imagine nodes lighting up when they’re checked. The key point is that OnPush prunes the tree of checks at the OnPush boundaries when appropriate.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals and OnPush (Angular 17+)
&lt;/h2&gt;

&lt;p&gt;Starting with Angular 16, and further enhanced in Angular 17 and above, Angular introduced &lt;strong&gt;Signals&lt;/strong&gt; — a reactive state management primitive that integrates tightly with the OnPush change detection strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are Signals?
&lt;/h3&gt;

&lt;p&gt;Signals are reactive variables that notify Angular when their value changes. When a signal is used in the template of an OnPush component, Angular automatically tracks its dependency and &lt;strong&gt;marks the component for check&lt;/strong&gt; when the signal changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Signals Work Well with OnPush
&lt;/h3&gt;

&lt;p&gt;With OnPush, Angular skips checking a component unless it has new inputs, an event, or manual detection. Signals &lt;strong&gt;add a fourth path&lt;/strong&gt;: reactive values embedded in the component itself.&lt;/p&gt;

&lt;p&gt;This allows components to be reactive &lt;strong&gt;without @Input bindings or manual &lt;code&gt;markForCheck()&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: OnPush Component with a Signal
&lt;/h3&gt;



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

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;counter-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;p&amp;gt;Counter: {{ counter() }}&amp;lt;/p&amp;gt;
    &amp;lt;button (click)="increment()"&amp;gt;Increment&amp;lt;/button&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterButtonComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔍 &lt;strong&gt;Note:&lt;/strong&gt; Even though &lt;code&gt;CounterButtonComponent&lt;/code&gt; is OnPush and has no inputs, Angular tracks usage of &lt;code&gt;counter()&lt;/code&gt; in the template and triggers a check when it updates. This avoids the need for &lt;code&gt;ChangeDetectorRef.markForCheck()&lt;/code&gt; entirely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Signals as Inputs (Angular 17+)
&lt;/h3&gt;

&lt;p&gt;Angular 17 introduced &lt;code&gt;input()&lt;/code&gt; to define reactive &lt;code&gt;@Input()&lt;/code&gt; properties as signals:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Received: {{ value() }}&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChildComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when the parent updates the input, Angular updates the signal — and OnPush change detection picks it up automatically without needing manual triggers.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Signals with OnPush
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When you manage local reactive state inside OnPush components.&lt;/li&gt;
&lt;li&gt;When building presentational components with reactive @Inputs.&lt;/li&gt;
&lt;li&gt;When combining Observables and Signals for fine-grained UI updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prefer &lt;code&gt;signal()&lt;/code&gt; over &lt;code&gt;BehaviorSubject&lt;/code&gt; or &lt;code&gt;manual markForCheck()&lt;/code&gt; for local UI state.&lt;/li&gt;
&lt;li&gt;Combine &lt;code&gt;input()&lt;/code&gt; + &lt;code&gt;effect()&lt;/code&gt; to build responsive components with clean data flow.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;computed()&lt;/code&gt; to derive state based on multiple signals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining Signals with OnPush, you get the &lt;strong&gt;best of both performance and reactivity&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Optimizing Angular’s change detection with the OnPush strategy is a powerful technique for improving application performance. By telling Angular to skip checking component subtrees unless necessary, you reduce the work done during each change detection cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default vs OnPush:&lt;/strong&gt; Default change detection checks everything each time (simple but potentially heavy), whereas OnPush allows you to cut off branches of the component tree from being checked when nothing relevant changed in them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OnPush Triggers:&lt;/strong&gt; Remember the conditions that reenable checking for an OnPush component – new input references, events in its template or children, manual marks, or async pipe emissions. If none of these happen, the component can sit out the change detection dance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signals (Angular 16+):&lt;/strong&gt; Signals offer a reactive, template-safe way to update UI in OnPush components without needing &lt;code&gt;ChangeDetectorRef&lt;/code&gt;. They provide a clean, ergonomic alternative to &lt;code&gt;BehaviorSubject&lt;/code&gt; for local state and input management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Practices:&lt;/strong&gt; Use OnPush on components that benefit from it (often presentational components, large lists, etc.), keep your inputs immutable, and prefer using the async pipe and output events to communicate changes. Reach for &lt;code&gt;ChangeDetectorRef.markForCheck()&lt;/code&gt; when you need to nudge Angular that something changed, and use &lt;code&gt;detectChanges()&lt;/code&gt; only when you absolutely need an immediate check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common Pitfalls:&lt;/strong&gt; Be mindful of mutated objects not causing updates – always change references or explicitly mark for check in those cases. And ensure that any manual input setting or external data injection is accompanied by the necessary change detection trigger.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the introduction of &lt;strong&gt;Signals&lt;/strong&gt;, Angular now enables a more reactive and declarative style of building components—especially powerful when combined with OnPush. Together, they give developers fine-grained control and high performance without excessive boilerplate.&lt;/p&gt;

&lt;p&gt;By following these techniques, you'll write more scalable, efficient, and maintainable Angular applications. &lt;/p&gt;

&lt;h3&gt;
  
  
  Happy coding!
&lt;/h3&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>signals</category>
      <category>performance</category>
    </item>
    <item>
      <title>Types vs Interfaces in TypeScript: Making the Right Choice</title>
      <dc:creator>Anastasios Theodosiou</dc:creator>
      <pubDate>Sat, 14 Oct 2023 17:05:41 +0000</pubDate>
      <link>https://dev.to/atheodosiou/types-vs-interfaces-in-typescript-making-the-right-choice-48a4</link>
      <guid>https://dev.to/atheodosiou/types-vs-interfaces-in-typescript-making-the-right-choice-48a4</guid>
      <description>&lt;h2&gt;
  
  
  Types vs Interfaces in TypeScript: Making the Right Choice
&lt;/h2&gt;



&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The TypeScript community has long debated the use of types and interfaces. Developers often wrestle with the decision of when to use one over the other. In this blog post, we'll explore the advantages and drawbacks of both, helping you make an informed choice that aligns with your coding style and project needs.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase One: Interfaces Are the Bomb
&lt;/h2&gt;

&lt;p&gt;In the early days, interfaces were the favored option. The TypeScript Performance Wiki even claimed that interfaces were faster than types. It was believed that interfaces could boost the speed of the TypeScript type checker, making them ideal for performance-critical projects. However, interfaces had their limitations, primarily being designed for objects and functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Considerations
&lt;/h3&gt;

&lt;p&gt;It's important to clarify that when we talk about "speed," we're referring to the efficiency of the TypeScript type checker, not the runtime performance of your code. For large projects, a slow type checker can be a significant issue, pushing developers towards interfaces for potential performance improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmarking
&lt;/h3&gt;

&lt;p&gt;To test this belief, a benchmark was conducted, comparing a thousand types to a thousand interfaces. The results were inconclusive and led to discussions with the TypeScript team, revealing new insights.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Using an interface&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Using a type&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Coordinates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h2&gt;
  
  
  Phase Two: Consistency Is Key
&lt;/h2&gt;

&lt;p&gt;Phase Two introduced a different perspective: the choice between types and interfaces doesn't matter as long as you maintain coding consistency. This viewpoint encouraged using interfaces for objects and types for other constructs, sparking debates in the TypeScript community.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface Inheritance
&lt;/h3&gt;

&lt;p&gt;One key advantage of interfaces is their ability to inherit from other interfaces, a feature not easily achieved with types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Circle&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h2&gt;
  
  
  Phase Three: The Need for Specific Features
&lt;/h2&gt;

&lt;p&gt;A turning point occurred when the inventor of AngularJS encountered issues with interfaces, especially regarding appending properties, which led to unpredictable behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declaration Merging
&lt;/h3&gt;

&lt;p&gt;Interfaces introduced "declaration merging," a valuable feature in some scenarios but one that could add complexity when different interfaces shared the same name and scope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h2&gt;
  
  
  Conclusion: Making the Right Choice
&lt;/h2&gt;

&lt;p&gt;In Phase Three, the ultimate recommendation is to use types unless you specifically need features provided by interfaces. Types offer predictability and are less likely to exhibit unexpected behavior. Importantly, there is no discernible performance difference between types and interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consider Your Needs
&lt;/h3&gt;

&lt;p&gt;Your choice between types and interfaces should align with your project's unique requirements and your personal coding style. If you need inheritance, want to extend another type, or come from an object-oriented programming background, interfaces may be your preferred choice. However, if predictability and control are your priorities, types are the more suitable option.&lt;/p&gt;

&lt;p&gt;In summary, there is no one-size-fits-all solution. Your choice should be guided by your project's demands and your familiarity with TypeScript's intricacies.&lt;/p&gt;

&lt;p&gt;So, the next time you face the decision of types vs. interfaces in TypeScript, remember this post. Let it guide you in making an informed choice that best fits your coding style.&lt;/p&gt;



&lt;p&gt;Happy coding! Like, subscribe, and stay tuned for more enlightening content.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Exploring Dynamic Component Creation in Angular 16</title>
      <dc:creator>Anastasios Theodosiou</dc:creator>
      <pubDate>Fri, 06 Oct 2023 12:57:31 +0000</pubDate>
      <link>https://dev.to/atheodosiou/exploring-dynamic-component-creation-in-angular-16-1lk8</link>
      <guid>https://dev.to/atheodosiou/exploring-dynamic-component-creation-in-angular-16-1lk8</guid>
      <description>&lt;p&gt;In the wake of &lt;strong&gt;Angular's&lt;/strong&gt; version 13 release, a fresh approach to dynamic component usage was unveiled, aiming to streamline the process. The question on everyone's mind: Did this update genuinely simplify dynamic component creation? To find out, we'll dive into the world of Angular version 16 and explore its newfound capabilities.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://anastasios.theodosiou.me/blog" rel="noopener noreferrer"&gt;Anastasios Theodosiou Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Before we delve into the details, it's worth noting that in 2023, there are some intriguing Angular development tools you might want to explore alongside this dynamic component journey.&lt;/p&gt;



&lt;p&gt;The core idea here is to harness Dynamic Parameters to determine which Angular Component should be dynamically generated. To achieve this, let's take a look at how our route configuration should be structured:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Root Routes with lazy loaded Services Component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// Other routes...&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;services/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./services/services.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ServicesModule&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;In the route configuration above, the focus lies on line 6, where the dynamic parameter, ":id," is declared. This parameter will become accessible using the ActivatedRoute class, which is injected into the constructor via dependency injection. Here's an example of how you can access these dynamic parameters:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Initial ServicesComponent implementation&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ActivatedRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;


&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActivatedRoute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Access dynamic parameter here using params['id']&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;To avoid memory leaks, always remember to unsubscribe when your component is being destroyed, as not doing so can lead to issues. Now, let's move on to the core of our topic.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The Angular Team recommends creating a separate directive containing an injected ViewContainerRef class. This directive can then be reused across different components without the need for additional dependency injection in each component where dynamic components might be required. Here's an example implementation of such a directive:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example usage of ComponentHostDirective&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/app-component-host&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;Angular 16 introduces a self-closing tag syntax similar to React's, which enhances code readability. Now that the directive is in place, you can use the @ViewChild decorator to access the publicly available viewContainerRef within your services.component class:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Accessing viewContainerRef in services.component.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentHostDirective&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../directives/component-host.directive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ViewChild&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServicesComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ViewChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ComponentHostDirective&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;componentHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentHostDirective&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;With the viewContainerRef in hand, you can proceed to create dynamic components. The method for handling component creation involves clearing any previously created component instances and then instantiating a new component based on the service id obtained from the servicesComponentFactory definition:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Method to handle dynamic component creation&lt;/span&gt;
&lt;span class="nf"&gt;createDynamicComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentHost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;componentFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;servicesComponentFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getServiceComponentFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;componentFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentHost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;It's important to note that this implementation serves as an example and may not cover all use cases. You can customize it further to ensure proper mapping between parameters and component types.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The servicesComponentFactory definition might look something like this:&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Definition of servicesComponentFactory&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ServiceTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/service-types.enum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServicesComponentFactory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getServiceComponentFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Implement logic to return the appropriate component factory based on serviceId&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;In summary, your services.component.ts class should look similar to the one depicted above. With this setup, you can dynamically create child components based on the provided service id. As you can see, Angular 16 has made dynamic component creation appear quite straightforward, thanks to its latest features.&lt;/p&gt;



&lt;p&gt;And there you have it! Dynamic component creation in Angular 16, with a fresh perspective and a cleaner approach. Kudos to the Angular Team for this update!&lt;/p&gt;

&lt;p&gt;Find more in my personal blog &lt;a href="https://anastasios.theodosiou.me/blog" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>version16</category>
      <category>programming</category>
    </item>
    <item>
      <title>Angular Reactive Typed Forms - Not just a dream</title>
      <dc:creator>Anastasios Theodosiou</dc:creator>
      <pubDate>Thu, 14 Jul 2022 18:01:12 +0000</pubDate>
      <link>https://dev.to/atheodosiou/angular-reactive-typed-forms-not-just-a-dream-3c4a</link>
      <guid>https://dev.to/atheodosiou/angular-reactive-typed-forms-not-just-a-dream-3c4a</guid>
      <description>&lt;p&gt;It's been a while since I last wrote an article. When the new Angular version 14 was released I was quite satisfied with two new features and I wanted to share it with you. The first is Typed Reactive Forms and the second is Standalone Components.&lt;/p&gt;

&lt;p&gt;Original Source: &lt;a href="https://anastasios.theodosiou.me/blog/angular-reactive-typed-forms-not-just-a-dream" rel="noopener noreferrer"&gt;Anastasios Theodosiou Blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After 6 years of the first release, and after months of &lt;a href="https://github.com/angular/angular/discussions/44513" rel="noopener noreferrer"&gt;discussion &lt;/a&gt; and feedback, the most needed feature and up-voted &lt;a href="https://github.com/angular/angular/issues/13721" rel="noopener noreferrer"&gt;issue &lt;/a&gt;in the Angular repository is now solved in Angular v14!&lt;/p&gt;

&lt;p&gt;Angular 14 was released 2nd of June with the most significant update since Ivy. It includes two long-awaited features, &lt;strong&gt;Typed Reactive Forms&lt;/strong&gt; and &lt;strong&gt;Standalone Components&lt;/strong&gt;, as well as several minor improvements.&lt;/p&gt;

&lt;p&gt;On this article we will focus on Typed Reactive Forms. As before Angular v14, Reactive Forms did not include type definitions in many of its classes, and TypeScript would not catch bugs like in the following example during compilation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const loginForm = new FormGroup({
    email: new FormControl(''),
    password: new FormControl(''),
  });

  console.log(login.value.username);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Angular 14, the FormGroup, formControl, and related classes include type definitions enabling TypeScript to catch many common errors.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;Migration to the new Typed Reactive Forms is not automatic.&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;The already existing code containing FormControls, FormGroups, etc.. will be prefixed as Untyped during the upgrade. It is important to mention that if developers would like to take advantage of the new Typed Reactive Forms, must manually remove the Untyped prefix and fix any errors that may arise.&lt;/p&gt;

&lt;p&gt;More details about this migration can be found at the official&lt;a href="https://angular.io/guide/typed-forms" rel="noopener noreferrer"&gt; Typed Reactive Forms&lt;/a&gt; documentation. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;A step by step migration example of an untyped reactive form&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;Let's say that we have the following register form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  export class RegisterComponent {
    registerForm: FormGroup;

    constructor() {
      this.registerForm = new FormGroup({
        login: new FormControl(null, Validators.required),
        passwordGroup: new FormGroup({
          password: new FormControl('', Validators.required),
          confirm: new FormControl('', Validators.required)
        }),
        rememberMe: new FormControl(false, Validators.required)
      });
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Angular also provided an automated migration to speed up the process. This migration will run when we as developers, run the following command.&lt;/p&gt;

&lt;p&gt;&lt;span&gt;ng update @angular/core&lt;/span&gt;  or on demand, if we already manually updated your project by running the next command.   &lt;span&gt;ng update @angular/core --migrate-only=migration-v14-typed-forms &lt;/span&gt;.&lt;/p&gt;

&lt;p&gt;In our example, if we use the automated migration, we end up with the above changed code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class RegisterComponent {
  registerForm: UntypedFormGroup;

  constructor() {
    this.registerForm = new UntypedFormGroup({
      login: new UntypedFormControl(null, Validators.required),
      passwordGroup: new UntypedFormGroup({
        password: new UntypedFormControl('', Validators.required),
        confirm: new UntypedFormControl('', Validators.required)
      }),
      rememberMe: new UntypedFormControl(false, Validators.required)
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step now is to remove all the Untyped* usage and adjust properly our form.&lt;/p&gt;

&lt;p&gt;Each UntypedFormControl must be converted to FormControl, with T the type of the value of the form control. Most of the time, TypeScript can infer this information based on the initial value given to the FormControl.&lt;/p&gt;

&lt;p&gt;For example, passwordGroup can be converted easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;passwordGroup: new FormGroup({
  password: new FormControl('', Validators.required), // inferred as `FormControl&amp;lt;string | null&amp;gt;`
  confirm: new FormControl('', Validators.required) // inferred as `FormControl&amp;lt;string | null&amp;gt;`
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the inferred type is string | null and not string. This is because calling .reset() on a control without specifying a reset value, resets the value to null. This behavior is here since the beginning of Angular, so the inferred type reflects it. We’ll come back to this possibly null value, in an example bellow, as it can be annoying (but there is always a way).&lt;/p&gt;

&lt;p&gt;Now let’s take the field registerForm. Unlike FormControl, the generic type expected by FormGroup is not the type of its value, but a description of its structure, in terms of form controls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;registerForm: FormGroup&amp;lt;{
  login: FormControl&amp;lt;string | null&amp;gt;;
  passwordGroup: FormGroup&amp;lt;{
    password: FormControl&amp;lt;string | null&amp;gt;;
    confirm: FormControl&amp;lt;string | null&amp;gt;;
  }&amp;gt;;
  rememberMe: FormControl&amp;lt;boolean | null&amp;gt;;
}&amp;gt;;

constructor() {
  this.registerForm = new FormGroup({
    login: new FormControl&amp;lt;string | null&amp;gt;(null, Validators.required),
    passwordGroup: new FormGroup({
      password: new FormControl('', Validators.required),
      confirm: new FormControl('', Validators.required)
    }),
    rememberMe: new FormControl&amp;lt;boolean | null&amp;gt;(false, Validators.required)
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Nullability in forms
&lt;/h4&gt;

&lt;p&gt;As we can see above, the types of the controls are string | null and boolean | null, and not string and boolean like we could expect. This is happening because if we call the .reset() method on a field, resets its value to null. Except if we give a value to reset, for example .reset(''), but as TypeScript doesn’t know if and how you are going to call .reset(), the inferred type is nullable.&lt;/p&gt;

&lt;p&gt;We can tweek behavior by passing the nonNullable options (which replaces the new option introduced in Angular v13.2 initialValueIsDefault). With this option, we can get rid of the null value if we want to! &lt;/p&gt;

&lt;p&gt;On one hand, this is very handy if your application uses strictNullChecks, but on the other hand, this is quite verbose, as we currently have to set this option on every field (hope this change in the future).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;registerForm = new FormGroup({
  login: new FormControl&amp;lt;string&amp;gt;('', { validators: Validators.required, nonNullable: true }),
  passwordGroup: new FormGroup({
    password: new FormControl('', { validators: Validators.required, nonNullable: true }),
    confirm: new FormControl('', { validators: Validators.required, nonNullable: true })
  }),
  rememberMe: new FormControl&amp;lt;boolean&amp;gt;(false, { validators: Validators.required, nonNullable: true })
}); // incredibly verbose version, that yields non-nullable types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One other way to achieve the same result, is to use the NonNullableFormBuilder. A new property introduced by Angular v14 called nonNullable, that returns a NonNullableFormBuilder which contains the usual as known control, group, array, etc. methods to build non-nullable controls.&lt;/p&gt;

&lt;p&gt;Example of creating a non-nullable form grop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constructor(private fb: NonNullableFormBuilder) {}

registerForm = this.fb.group({
  login: ['', Validators.required]
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  So, does this migration wort it? What do we gain with Typed Reactive Forms?
&lt;/h4&gt;

&lt;p&gt;Before Angular v14, the existing forms API does note performing very well with TypeScript because every form control value is typed as any. So, we could easily write something like &lt;span&gt;this.registerForm.value.something&lt;/span&gt; and the application would compile successfully.  &lt;/p&gt;

&lt;p&gt;This is no longer the case: the new forms API properly types value according to the types of the form controls. In my example above (with nonNullable), the type of this.registerForm.value is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// this.registerForm.value
{
  login?: string;
  passwordGroup?: {
    password?: string;
    confirm?: string;
  };
  rememberMe?: boolean;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can spot some ?  in the type of the form value. What does it mean?&lt;/p&gt;

&lt;p&gt;It is widely known that in Angular, we can disable any part of our form we wan to and if so, Angular will automatically remove the value of a disabled control from the value of the form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.registerForm.get('passwordGroup').disable();
console.log(this.registerForm.value); // logs '{ login: null, rememberMe: false }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above result is a bit strange but it explains sufficiently why the fields are marked as optional if they have been disabled. So, they are not part of the this.registerForm.value any more. TypeScript calls this feature Partial value.&lt;/p&gt;

&lt;p&gt;There is also a way to get the hole object even with the disabled fields, by running the .getRawValue() function on the form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  login: string;
  passwordGroup: {
    password: string;
    confirm: string;
  };
  rememberMe: boolean;
} // this.registerForm.getRawValue()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Even more strictly typed .get() function
&lt;/h4&gt;

&lt;p&gt;The get(key) method is also more strictly typed. This is great news, as we could previously call it with a key that did not exist, and the compiler would not see the issue.&lt;/p&gt;

&lt;p&gt;Thanks to some hardcore TypeScript magic, the key is now checked and the returned control is properly typed! It is also works with array syntax for the key as bellow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;his.registerForm.get('login') // AbstractControl&amp;lt;string&amp;gt; | null
this.registerForm.get('passwordGroup.password') // AbstractControl&amp;lt;string&amp;gt; | null

//Array Syntax
this.registerForm.get(['passwordGroup', '.password'] as const) // AbstractControl&amp;lt;string&amp;gt; | null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also works  with nested form arrays and groups and if we use a key that does not exist we can finally get an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.registerForm.get('hobbies.0.name') // AbstractControl&amp;lt;string&amp;gt; | null 

//Non existing key
this.registerForm.get('logon' /* typo */)!.setValue('cedric'); // does not compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, get() returns a potentially null value: this is because you have no guarantee that the control exists at runtime, so you have to check its existence or use ! like above.&lt;/p&gt;

&lt;p&gt;Note that the keys you use in your templates for formControlName, formGroupName, and formArrayName aren’t checked, so you can still have undetected issues in your templates.&lt;/p&gt;

&lt;h4&gt;
  
  
  Something fresh: FormRecord
&lt;/h4&gt;

&lt;p&gt;FormRecord is a new form entity that has been added to the API. A FormRecord is similar to a FormGroup but the controls must all be of the same type. This can help if you use a FormGroup as a map, to which you add and remove controls dynamically. In that case, properly typing the FormGroup is not really easy, and that’s where FormRecord can help.&lt;/p&gt;

&lt;p&gt;It can be handy when you want to represent a list of checkboxes for example, where your user can add or remove options. For example, our users can add and remove the language they understand (or don’t understand) when they register:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;languages: new FormRecord({
  english: new FormControl(true, { nonNullable: true }),
  french: new FormControl(false, { nonNullable: true })
});

// later 
this.registerForm.get('languages').addControl('spanish', new FormControl(false, { nonNullable: true }));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we try to add a control of a different type, TS throws a compilation error!&lt;/p&gt;

&lt;p&gt;But as the keys can be any string, there is no type-checking on the key in removeControl(key) or setControl(key). Whereas if you use a FormGroup, with well-defined keys, you do have type checking on these methods: setControl only allows a known key, and removeControl only allows a key marked as optional (with a ? in its type definition).&lt;/p&gt;

&lt;p&gt;If we have a FormGroup on which we want to add and remove control dynamically, we’re probably looking for the new FormRecord type.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;I’m very excited to see this new forms API in Angular! This is, by far, one of the biggest changes in recent years for developers. Ivy was big but didn’t need us to make a lot of changes in our applications. Typed forms are another story: the migration is likely to impact dozens, hundreds, or thousands of files in our applications! &lt;/p&gt;

&lt;p&gt;The TypeScript support in Angular has always been outstanding, but had a major blind spot with forms: this is no longer the case!&lt;/p&gt;

&lt;p&gt;So, yes. It is totally worth it!!&lt;/p&gt;

&lt;p&gt;Till next time,&lt;br&gt;
Happy coding.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typed</category>
      <category>reactive</category>
      <category>forms</category>
    </item>
  </channel>
</rss>
