<?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>Forem: Robin Goetz</title>
    <description>The latest articles on Forem by Robin Goetz (@goetzrobin).</description>
    <link>https://forem.com/goetzrobin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F961549%2F3c98ffe5-8d8b-4db1-827a-de8c63c25444.jpeg</url>
      <title>Forem: Robin Goetz</title>
      <link>https://forem.com/goetzrobin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/goetzrobin"/>
    <language>en</language>
    <item>
      <title>spartan enters a new era with Zerops</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Tue, 12 Nov 2024 18:21:31 +0000</pubDate>
      <link>https://forem.com/goetzrobin/spartan-enters-a-new-era-with-zerops-40f4</link>
      <guid>https://forem.com/goetzrobin/spartan-enters-a-new-era-with-zerops-40f4</guid>
      <description>&lt;h2&gt;
  
  
  A Game-Changing Partnership
&lt;/h2&gt;

&lt;p&gt;I’m beyond excited to share that &lt;a href="https://spartan.ng" rel="noopener noreferrer"&gt;spartan&lt;/a&gt; has partnered with &lt;a href="https://zerops.io/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt;, a developer-first cloud platform deeply rooted in Angular and committed to strengthening its ecosystem. This collaboration is a huge opportunity for spartan, giving us the momentum and sustainability we need to take the project to the next level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Growth and the Need for Sustainability
&lt;/h2&gt;

&lt;p&gt;Over the last few months, development has been slower than I’d hoped—balancing life and other commitments can do that—but the growth of the spartan community has been nothing short of incredible. We’re now approaching 1,500 GitHub stars and seeing 2,400+ weekly downloads, a clear signal that developers see the value in what we’re building. &lt;/p&gt;

&lt;p&gt;This partnership with Zerops enables us to meet that demand head-on. For the first time, we’re able to compensate our team of core developers, ensuring they can dedicate more time to creating, refining, and expanding the spartan/ui components. These are people who care deeply about what we’re building, and now, they’ll have the resources to deliver their best work sustainably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Zerops?
&lt;/h2&gt;

&lt;p&gt;If you’re familiar with Zerops, you know they’re no strangers to Angular. Their support of projects like &lt;a href="https://analogjs.org" rel="noopener noreferrer"&gt;AnalogJs&lt;/a&gt; demonstrates their commitment to the ecosystem, and their alignment with spartan’s values—modern, accessible, flexible tools to build Angular apps—makes them the perfect partner for this journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next: A Production-Ready Starter Template
&lt;/h2&gt;

&lt;p&gt;One of the first outcomes of this partnership will be a starter template that integrates spartan with the &lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; e-commerce platform. This isn’t just a proof of concept—it’s a production-ready starting point for building e-commerce applications, combining spartan/ui components, Analog's full-stack capabilities, Medusa’s powerful platform, and Zerops’ seamless deployment tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Celebrating the spartan Community
&lt;/h2&gt;

&lt;p&gt;Of course, none of this would be possible without the incredible spartan community. From day one, The 300—our contributors—have been at the heart of everything we do. Your contributions, feedback, and ideas have shaped spartan into what it is today. This partnership with Zerops isn’t just about expanding the project—it’s about honoring the work and support that’s come before.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Road Ahead
&lt;/h2&gt;

&lt;p&gt;Looking ahead, the future of spartan is bright. Whether you’re a long-time supporter or just discovering the project, now is the perfect time to get involved. Check out the &lt;a href="https://github.com/spartan-ng/spartan" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, explore the roadmap, or contribute and become part of The 300. Together, we’re continuing to build towards an even more sustainable, accessible future for Angular development.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>zerops</category>
    </item>
    <item>
      <title>This is your sign(al) to try TanStack Query &amp; Angular</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Tue, 05 Mar 2024 15:20:39 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/this-is-your-signal-to-try-tanstack-query-angular-35m9</link>
      <guid>https://forem.com/playfulprogramming-angular/this-is-your-signal-to-try-tanstack-query-angular-35m9</guid>
      <description>&lt;p&gt;In version 16, Angular released signals. A reactive primitive promised by the core team that would help take the framework to the next level. A primitive that allows for fine-grained rendering updates and a world where Angular is not reliant on ngZone for change detection anymore. A primitive that also provides the open source ecosystem with a new way to react to changes in values in components, directives, and services.&lt;/p&gt;

&lt;p&gt;Now about 7 months later, the Angular community has absolutely embraced the signals sent by the core team and is building incredible new tools and libraries on top of this new technology.&lt;/p&gt;

&lt;p&gt;Projects like &lt;code&gt;ngrx/signals&lt;/code&gt;, &lt;code&gt;ngxtension&lt;/code&gt; with &lt;code&gt;signalSlice&lt;/code&gt; or &lt;code&gt;combine&lt;/code&gt;, come to mind immediately.&lt;/p&gt;

&lt;p&gt;But not only do signals enable new innovation inside the existing ecosystem, they also enable developers to port incredibly powerful solutions to Angular. By leveraging signals and their new way of notifying and reacting to changes in values that is significantly less complex than RxJs.&lt;/p&gt;

&lt;p&gt;One of these projects, and the one that I am (currently) most excited about is &lt;a href="https://github.com/arnoud-dv" rel="noopener noreferrer"&gt;arnoud-dv's&lt;/a&gt; official adapter that brings TanStack Query to the Angular ecosystem.&lt;/p&gt;

&lt;p&gt;In this article, I'll introduce TanStack Query and try make this your sign(al) to give TanStack Query &amp;amp; Angular a try!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is TanStack Query?
&lt;/h2&gt;

&lt;p&gt;TanStack Query is a data-fetching library that originated in the React ecosystem. It is an opinionated tool that allows you to easily fetch, cache, synchronize and update server state in web applications.&lt;/p&gt;

&lt;p&gt;If you have worked on bigger Angular applications, you probably realized that while the framework provides its own HttpClient to fetch remote data, it does not come with an opinionated way of managing the state of those interactions. We might create our own solutions with global NgRx or NGXS stores that allow us to access server data, let us manage entity collections, and manually trigger requests to fetch new information whenever it's necessary. Maybe we even develop some sort of opinionated meta-server-state-management tool to bring some order to the chaos.&lt;/p&gt;

&lt;p&gt;However, while &lt;code&gt;NgRx&lt;/code&gt;, &lt;code&gt;NGXS&lt;/code&gt;, or &lt;code&gt;Elf&lt;/code&gt; are powerful state management libraries, they were often created for working with client state and not explicitly designed around managing asynchronous or server state.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is server state?
&lt;/h3&gt;

&lt;p&gt;Let's look at how the TanStack Query team defines it:&lt;/p&gt;

&lt;p&gt;For starters, server state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is persisted remotely in a location you do not control or own&lt;/li&gt;
&lt;li&gt;Requires asynchronous APIs for fetching and updating&lt;/li&gt;
&lt;li&gt;Implies shared ownership and can be changed by other people without your knowledge&lt;/li&gt;
&lt;li&gt;Can potentially become "out of date" in your applications if you're not careful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This (unsurprisingly) makes perfect sense. Our data often lives in a remote database, not in the browser. To access and alter this data we need to communicate with our servers through API calls. These calls are always asynchronous. Others often have access to the same data. This means that although we might have just fetched a local copy of the data from the API, someone else could have already changed the underlying record in the database.&lt;/p&gt;

&lt;p&gt;Bummer... Looks like managing server state is actually a lot more complex than calling &lt;code&gt;this.http.post&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As we grasp this nature of server state in our applications, the TanStack team points out that even more challenges will inevitably arise as we go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caching... (possibly the hardest thing to do in programming)&lt;/li&gt;
&lt;li&gt;Deduping multiple requests for the same data into a single request&lt;/li&gt;
&lt;li&gt;Updating "out of date" data in the background&lt;/li&gt;
&lt;li&gt;Knowing when data is "out of date"&lt;/li&gt;
&lt;li&gt;Reflecting updates to data as quickly as possible&lt;/li&gt;
&lt;li&gt;Performance optimizations like pagination and lazy loading data&lt;/li&gt;
&lt;li&gt;Managing memory and garbage collection of server state&lt;/li&gt;
&lt;li&gt;Memoizing query results with structural sharing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oof... &lt;/p&gt;

&lt;p&gt;Just reading these points makes all my alarm signals go off and scream: Complexity!! Don't get me wrong, every once in a while I like a technical challenge (like building &lt;a href="https://github.com/goetzrobin/spartan" rel="noopener noreferrer"&gt;spartan/ui&lt;/a&gt;), but this just seems like an absolute nightmare to deal with... &lt;/p&gt;

&lt;p&gt;Especially, if there already exists a battle tested server state-management solution that let's you "[t]oss out that granular state management, manual refetching and endless bowls of async-spaghetti code [and] gives you declarative, always-up-to-date auto-managed queries and mutations that directly improve both your developer and user experiences."&lt;/p&gt;

&lt;p&gt;I love declarative solutions to complex problems. Even more those that people that are a lot smarter than me built with all the different edge cases in mind. Those solutions that let us use the fruits of years of their hard work so we can focus on building OUR applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I love TanStack Query!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It's all about the Queries!
&lt;/h2&gt;

&lt;p&gt;As the name already implies TanStack Query is built around the concept of Queries. The library takes care of all the complexities of fetching the data for these queries, manages updating the data, and can notify you when the data becomes out of date, and so much more.&lt;/p&gt;

&lt;p&gt;Let's start with taking a look at an example of a simple query that fetches data about the TanStack Query GitHub repository:&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;ChangeDetectionStrategy&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="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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;injectQuery&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;@tanstack/angular-query-experimental&lt;/span&gt;&lt;span class="dl"&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;lastValueFrom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&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;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;simple-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;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;`
    @if (query.isPending()) {
      Loading...
    }
    @if (query.error()) {
      An error has occurred: {{ query.error().message }}
    }
    @if (query.data(); as data) {
      &amp;lt;h1&amp;gt;{{ data.name }}&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;{{ data.description }}&amp;lt;/p&amp;gt;
      &amp;lt;strong&amp;gt;👀 {{ data.subscribers_count }}&amp;lt;/strong&amp;gt;
      &amp;lt;strong&amp;gt;✨ {{ data.stargazers_count }}&amp;lt;/strong&amp;gt;
      &amp;lt;strong&amp;gt;🍴 {{ data.forks_count }}&amp;lt;/strong&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;SimpleExampleComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectQuery&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;queryKey&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;repoData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;lastValueFrom&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;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;Response&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;https://api.github.com/repos/tanstack/query&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 code is all that is needed to fetch the data, cache it effectively, and have all the challenges of managing server state we explored above to be taken care of for us.&lt;/p&gt;

&lt;p&gt;We only give TanStack Query, using the &lt;code&gt;injectQuery&lt;/code&gt; function, a unique identifier, the &lt;code&gt;queryKey&lt;/code&gt;, for the data we are interested in and then tell it how to fetch the data with a &lt;code&gt;queryFn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this in mind, let's dig a little deeper and try to understand what makes TanStack Query tick (no change detection pun intended.)&lt;/p&gt;

&lt;h3&gt;
  
  
  So what is a Query again?
&lt;/h3&gt;

&lt;p&gt;From the docs:&lt;br&gt;
&lt;em&gt;A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This explains the two options we see getting passed to &lt;code&gt;injectQuery&lt;/code&gt; in our 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;queryKey&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;repoData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="nx"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;lastValueFrom&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;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;Response&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;https://api.github.com/repos/tanstack/query&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 &lt;code&gt;['repoData']&lt;/code&gt; array is used as a unique query key. This key is associated with a "container" that holds all the data associated with the state &amp;amp; data of our query. If you are wondering why this key is an array and not a simple string, worry not. We will go into more detail on this in a second.&lt;/p&gt;

&lt;p&gt;To update our query's data we need a promised based method to fetch data from the server, the &lt;code&gt;queryFn&lt;/code&gt;. In Angular interactions with the server happen through the HttpClient. Because the HttpClient returns an Observable we are using &lt;code&gt;lastValueFrom&lt;/code&gt; to make our server client interaction promise based:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;() =&amp;gt; lastValueFrom(this.http.get&amp;lt;Response&amp;gt;('https://api.github.com/repos/tanstack/query'))&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The query result, which is returned by the &lt;code&gt;injectQuery&lt;/code&gt; function, contains all of the information about the query that you'll need for templating and any other usage of the data as we can see in our example's template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@if (query.isPending()) {
  Loading...
}
@if (query.error()) {
  An error has occurred: {{ query.error().message }}
}
@if (query.data(); as data) {
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{ data.name }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ data.description }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;👀 {{ data.subscribers_count }}&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;✨ {{ data.stargazers_count }}&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;🍴 {{ data.forks_count }}&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;query&lt;/code&gt; object contains a few very important states you'll need to be aware of to be productive. A query can only be in one of the following states at any given moment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;isPending&lt;/code&gt; or &lt;code&gt;status === 'pending'&lt;/code&gt; - The query has no data yet&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isError&lt;/code&gt; or &lt;code&gt;status === 'error'&lt;/code&gt; - The query encountered an error&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isSuccess&lt;/code&gt; or &lt;code&gt;status === 'success'&lt;/code&gt; - The query was successful and data is available&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Beyond those primary states, more information is available depending on the state of the query:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;error&lt;/code&gt; - If the query is in an isError state, the error is available via the error property.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; - If the query is in an isSuccess state, the data is available via the data property.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isFetching&lt;/code&gt; - In any state, if the query is fetching at any time (including background refetching) isFetching will be true.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why is our queryKey an array?
&lt;/h3&gt;

&lt;p&gt;So far so good, we know that injectQuery gives us an optimized way to store server-state for a query key and an associated promised based server-client interaction, but why do we use an array as a key? Wouldn't a string make more sense?&lt;/p&gt;

&lt;p&gt;When in doubt, take a look at TanStack Query's incredible docs. They state the following:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;At its core, TanStack Query manages query caching for you based on query keys. Query keys have to be an Array at the top level, and can be as simple as an Array with a single string, or as complex as an array of many strings and nested objects. As long as the query key is serializable, and unique to the query's data, you can use it!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While there might be some hints on why you'd want unique and serializable keys, this alone does not yet tell me why they decided to go with an Array instead of using strings. Let's look at a couple examples to understand this decision.&lt;/p&gt;

&lt;p&gt;For the simplest of use cases such as generic lists or non-hierarchical resources a simple string would probably work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A list of todos&lt;/span&gt;
&lt;span class="nf"&gt;injectQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&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;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Something else, whatever!&lt;/span&gt;
&lt;span class="nf"&gt;injectQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&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;something&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;special&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;However, keeping them as an array will be well worth it! In reality our queries often need more information to uniquely describe their data. That is the reason we are using an array for our query keys. We can now combine strings and any number of serializable objects to describe our data:&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;// An individual todo&lt;/span&gt;
&lt;span class="nf"&gt;injectQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&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;todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// An individual todo in a "preview" format&lt;/span&gt;
&lt;span class="nf"&gt;injectQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&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;todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;preview&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="c1"&gt;// A list of todos that are "done"&lt;/span&gt;
&lt;span class="nf"&gt;injectQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&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;todos&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;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;done&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;As you see, when we deal with hierarchical or nested resources, and pass an ID, index, or other primitive to uniquely identify the item, or deal with&lt;br&gt;
queries that rely on additional search parameters, the array approach and its intrinsic hierarchy shines.&lt;/p&gt;

&lt;p&gt;Important to remember: Because query keys uniquely describe the data they are fetching, they always should include any variables you use in your query function that change.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why do we need a function to return our options instead of passing them in directly?
&lt;/h3&gt;

&lt;p&gt;The tl;dr is that we can think of the function we are using to pass in our options as an &lt;code&gt;effect&lt;/code&gt; for Angular's signals. That means the options are now updated whenever one of the signals used to construct them changes. Our query starts to react to the signals we use to declare it.&lt;/p&gt;

&lt;p&gt;Let's take a closer look how we ended up with this signal powered approach.&lt;/p&gt;
&lt;h3&gt;
  
  
  Origins of TanStack Query in React
&lt;/h3&gt;

&lt;p&gt;TanStack Query originated as a React data fetching library. &lt;/p&gt;

&lt;p&gt;In React world everything is a component. There is no concept of services or directives. Your application is ultimately composed of only components. React re-renders a component whenever one of it's props, which are values passed directly to the component, or some local state, declared with useState, change. &lt;/p&gt;

&lt;p&gt;React does not have a dependency injection mechanism in the same way Angular does. Instead it relies on a concept called hooks, e.g. useQuery. These hooks are functions that are re-executed whenever the component they are called from is re-rendered.&lt;/p&gt;

&lt;p&gt;TanStack Query manages all of it's server state outside of the React application.&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;QueryClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;QueryClientProvider&lt;/span&gt;&lt;span class="p"&gt;,&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;@tanstack/react-query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;// Create a client outside of the React App&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&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;QueryClient&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Provide the client to your App&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Todos&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also does the same in Angular. To make our example from earlier work, we actually need to create the QueryClient as a global object and provide it when bootstrapping our Angular application:&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;provideHttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provideAngularQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;QueryClient&lt;/span&gt;&lt;span class="p"&gt;,&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;@tanstack/angular-query-experimental&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&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;QueryClient&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;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;provideAngularQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryClient&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 to setup TanStack Query with React and Angular actually look pretty similar. Both times TanStack's QueryClient is instantiated outside the actual application. The core of it is framework agnostic.&lt;/p&gt;

&lt;p&gt;However, to take advantage of all the benefits TanStack provides for server-state management we need to connect this &lt;code&gt;queryClient&lt;/code&gt; with our application. &lt;/p&gt;

&lt;p&gt;In React we do this with the &lt;code&gt;useQuery&lt;/code&gt; hook to which we pass our query keys. Hooks are re-rendered when components input props or state changes. If a query key depends on props or state, the query key changes, which in return propagates the change to the &lt;code&gt;queryClient&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Todos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPage&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="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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&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;todos&lt;/span&gt;&lt;span class="dl"&gt;'&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="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getTodos&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;query&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;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;todo&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reactive Angular
&lt;/h3&gt;

&lt;p&gt;In Angular on the other hand, not everything is a component and there is no concept of a component tree re-rendering. Instead Angular comes with it's own Change Detection Mechanism based on &lt;code&gt;ngZone&lt;/code&gt;. For TanStack Query this means that the simple paradigm of input change or (non reactive) state change triggers function call with new param, which changes query key which causes TanStack Query to do its magic, does not work anymore. &lt;/p&gt;

&lt;p&gt;However, this reaction to an input or state change does sound suspiciously familiar to what &lt;code&gt;effect&lt;/code&gt;'s do with signals. And while the specifics are more complicated this is actually a good first intuition of how signals allow such an intuitive port to Angular:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="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;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;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;todos&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;ul&amp;gt;
    @for(todo of query.data() ?? []; track todo.id) {
      &amp;lt;li&amp;gt;{todo.title}&amp;lt;/li&amp;gt;
    }
  &amp;lt;/ul&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodosComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;page&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="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectQuery&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;queryKey&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;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getTodos&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;page&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;Whenever the &lt;code&gt;page&lt;/code&gt; variable, a "simple" state value in React or a signal containing state in Angular, changes. The TanStack Query client is made aware of this &lt;code&gt;page&lt;/code&gt; change. The query key that is composed of the &lt;code&gt;page&lt;/code&gt; value is updated, and the associated data is refetched accordingly.&lt;/p&gt;

&lt;p&gt;As &lt;a class="mentioned-user" href="https://dev.to/arnouddv"&gt;@arnouddv&lt;/a&gt; pointed out, there is another reason to return the options from a function:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Returning options from a functions] allows to preserve expressions. JavaScript - like most languages, is eagerly evaluated. The only practical way to preserve expressions is to wrap them in a function.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Which allows code like this:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;postQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectQuery&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;// ...&lt;/span&gt;
    &lt;span class="na"&gt;enabled&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;postId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Which means the query will be automatically enabled for you whenever the postId signal value is greater than 0. No need to separately define and pass a computed signal. This works for all the query properties.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If it becomes more complex than a simple comparison or maybe a ternary I would recommend a separate computed for code readability though.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutations: Update the server, invalidate the client
&lt;/h2&gt;

&lt;p&gt;Now that we have a better understanding of how we can leverage queries to fetch and manage async server state, we take a closer look and updating server state, and how we can ensure that these updates are also reflected in our client side data.&lt;/p&gt;

&lt;p&gt;To create/update/delete data or perform server side-effects, TanStack Query introduces the concept of mutations and exports a &lt;code&gt;injectMutation&lt;/code&gt; function to carry them out.&lt;/p&gt;

&lt;p&gt;For example if you wanted to create a new todo on the server you could use a mutation 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;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div&amp;gt;
      @if (mutation.isPending()) {
        &amp;lt;span&amp;gt;Adding todo...&amp;lt;/span&amp;gt;
      } @else if (mutation.isError()) {
        &amp;lt;div&amp;gt;An error occurred: {{ mutation.error()?.message }}&amp;lt;/div&amp;gt;
      } @else if (mutation.isSuccess()) {
        &amp;lt;div&amp;gt;Todo added!&amp;lt;/div&amp;gt;
      }
      &amp;lt;button (click)="mutation.mutate(1)"&amp;gt;Create Todo&amp;lt;/button&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;TodosComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;todoService&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;TodoService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;mutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectMutation&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;mutationFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;todoId&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="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;lastValueFrom&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;todoService&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="nx"&gt;todoId&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 &lt;code&gt;mutation&lt;/code&gt; object contains the same state indicators as a &lt;code&gt;query&lt;/code&gt;. &lt;code&gt;isIdle&lt;/code&gt;, &lt;code&gt;isPending&lt;/code&gt;, &lt;code&gt;isError&lt;/code&gt;, &lt;code&gt;isSuccess&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;data&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;It also has a &lt;code&gt;mutate&lt;/code&gt; function, which triggers the &lt;code&gt;mutationFn&lt;/code&gt; that we pass into the &lt;code&gt;injectMutation&lt;/code&gt;'s options object. In the example above, a click on the &lt;em&gt;Create Todo&lt;/em&gt; button starts executing our mutation. You can also see that variables can be passed to our mutations function by calling the &lt;code&gt;mutate&lt;/code&gt; function with a single variable or object.&lt;/p&gt;

&lt;p&gt;It is sometimes the case that you need to clear the error or data of a mutation request. To do this, each mutation has a &lt;code&gt;reset&lt;/code&gt; function, which can be called to handle this.&lt;/p&gt;

&lt;p&gt;Overall, mutations are actually a lot more straightforward than queries. However, it is often the case that after mutating some state on the server, we need to make sure that our queries reflect those changes on the frontend. To do that, we can pass an &lt;code&gt;onSuccess&lt;/code&gt; function to the &lt;code&gt;injectMutation&lt;/code&gt; options object. This function is called after the mutation successfully completed, which means it is the perfect place to ensure that your frontend data is up to date.&lt;/p&gt;

&lt;p&gt;There are two ways we can make this happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We invalidate the query keys that we know are affected by our mutation and let TanStack Query handle the refetching of the data. This shows the true power of declarative server-state management that is completely driven by query keys.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;injectAddComment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="kd"&gt;const&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectQueryClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;injectMutation&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mutationFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newComment&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="nf"&gt;lastValueFrom&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/comments`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newComment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// Invalidate and refetch by using the client directly&lt;/span&gt;
    &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ✅ refetch the comments list for our blog post&lt;/span&gt;
      &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidateQueries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;queryKey&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;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;comments&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;p&gt;Internally, TanStack Query uses fuzzy matching on the query key. That means that if you have multiple keys for your comments list, they will all be invalidated. Only currently active keys will be refetched, others are marked as stale and refetched the next time they are used.&lt;/p&gt;

&lt;p&gt;Let's say we have the option to sort our comments. When the new comment was added, two queries with comments are in our cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['posts', 5, 'comments', { sortBy: ['date', 'asc'] }
['posts', 5, 'comments', { sortBy: ['author', 'desc'] }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're only displaying one of them on the screen. &lt;code&gt;invalidateQueries&lt;/code&gt; refetches that one and marks the other one as stale. The user will always see the latest data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We know exactly what data on the frontend needs to be updated as a result of our mutation and set the new data directly inside our query client. This should be used with caution, but does avoid another (background) API call to our server.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;injectUpdateTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="kd"&gt;const&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectQueryClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;injectMutation&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mutationFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newTitle&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="nf"&gt;lastValueFrom&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newTitle&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;// Invalidate and refetch by using the client directly&lt;/span&gt;
    &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newPost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&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;// ✅ update detail view directly&lt;/span&gt;
      &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setQueryData&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;newPost&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;Putting data into the cache directly via &lt;code&gt;setQueryData&lt;/code&gt; will act as if this data was returned from the backend. All components using that query will update accordingly.&lt;/p&gt;

&lt;p&gt;Invalidation should be preferred and while it depends on the use-case, for direct updates to work reliably, you need more code on the frontend, and to some extent duplicate logic from the backend. For example, sorted lists are pretty hard to update directly. The position of the entry could've potentially changed due to the update. Invalidating the whole list is most often the "safer" approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is TanStack Query for components only or do directives and services work too?
&lt;/h2&gt;

&lt;p&gt;Since directives are just components without templates, the same code, for queries and mutations, that we used in our examples above would also work in any directive.&lt;/p&gt;

&lt;p&gt;Even more important to understand is that TanStack Query actually does not care where it is used. As we saw above, TanStack Query manages our server-state outside of our Angular application. The only limitation on where to use Angular Query is that the &lt;code&gt;injectQuery&lt;/code&gt; function needs to be called from an injection context. &lt;/p&gt;

&lt;p&gt;This is actually an Angular dependency injection limitation. However, since the instantiation of a class is run in an injection context it feels very natural to declare the query variable as a class variable. However, if you want to learn more about Angular's injection context I recommend &lt;a href="https://netbasal.com/understanding-angular-injection-context-18a0780ede2d" rel="noopener noreferrer"&gt;this article.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;injectQuery&lt;/code&gt; function provides all it's internal state and data as signals. We can use those signals to integrate with other code of any of our components, including the template, directives or services.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I use TanStack Query with modern Angular
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Write Reusable Queries with Custom Injection Functions
&lt;/h3&gt;

&lt;p&gt;When I started using TanStack Query for my Angular applications, I quickly realized that I do not always want to have to rewrite the same &lt;code&gt;queryKey&lt;/code&gt; and &lt;code&gt;queryFn&lt;/code&gt; everywhere. This seems like a great way to introduce inconsistencies and bugs that are hard to debug. What I wanted is a way to reuse queries.&lt;/p&gt;

&lt;p&gt;This is where we can leverage the power of custom injection functions (CIF) and inject reusable queries. To these CIFs I like to pass in a &lt;code&gt;params&lt;/code&gt; signal that drives the query, and an optional injector, which allows users to leverage the CIF is places where no injection context is available. I highly recommend reading &lt;a href="https://nartc.me/blog/inject-function-the-right-way" rel="noopener noreferrer"&gt;this article&lt;/a&gt; by Chau, if you want to learn more about the reasoning behind this.&lt;/p&gt;

&lt;p&gt;Putting these ideas together, the source code for a reusable query looks 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;inject&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="nx"&gt;runInInjectionContext&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;assertInjector&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;ngxtension/assert-injector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;injectQuery&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;@tanstack/angular-query-experimental&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;lastValueFrom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;todoKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ToDo&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;./todos.keys&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;injectTodosQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="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="nl"&gt;injector&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="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="nx"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;assertInjector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;injectTodosQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;runInInjectionContext&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="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;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;return&lt;/span&gt; &lt;span class="nf"&gt;injectQuery&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;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todoKeys&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="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;lastValueFrom&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;ToDo&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`todos?done=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;done&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the &lt;code&gt;assertInjector&lt;/code&gt; function from &lt;code&gt;ngxtension&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;assertInjector&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="nb"&gt;Function&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="nx"&gt;Injector&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="c1"&gt;// we only call assertInInjectionContext if there is no custom injector&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;assertInInjectionContext&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="c1"&gt;// we return the custom injector OR try get the default Injector&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;injector&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;Injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can be consumed in any component, directive, or service. With the optional &lt;code&gt;Injector&lt;/code&gt; you can now delay injecting the query until you need it, e.g. after Inputs become available in &lt;code&gt;ngOnInit&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read the docs &amp;amp; TkDodo's blog
&lt;/h3&gt;

&lt;p&gt;Besides these "Angular specific" challenges, I try to study and emulate all the pattern's that the TanStack documentation lays out or find myself reading the incredible blog posts &lt;a class="mentioned-user" href="https://dev.to/tkdodo"&gt;@tkdodo&lt;/a&gt; has published on &lt;a href="https://tkdodo.eu/blog" rel="noopener noreferrer"&gt;his personal blog!&lt;/a&gt; So far they have an answer to any question I could ever have about TanStack Query.&lt;/p&gt;

&lt;p&gt;Here are some insights I consider absolutely essential from his blog: &lt;/p&gt;

&lt;h4&gt;
  
  
  Always keep good Query Keys hygiene
&lt;/h4&gt;

&lt;p&gt;Keep your Query Keys next to their respective queries, co-located in a feature directory or Nx library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- src
  - app
    - features
      - todos
        - todos.keys.ts
        - todos.mutations.ts
        - todos.query.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Structure your Query Keys from most generic to most specific, with as many levels of granularity as you see fit in between. Here's an example structure for a todos list that allows for filterable lists as well as detail views:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['todos', 'list', { filters: 'all' }]
['todos', 'list', { filters: 'done' }]
['todos', 'detail', 1]
['todos', 'detail', 2]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far we have been manually declaring the Query Keys a lot. This error-prone and makes changes harder in the future if you wanted to add another level of granularity to your keys.&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/tkdodo"&gt;@tkdodo&lt;/a&gt; recommends to use Query Key factory per feature: A simple object with entries and functions that will create query keys. For the above structure, it would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;all&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;todos&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lists&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;todoKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;filters&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;todoKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lists&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;details&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;todoKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detail&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;detail&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="kr"&gt;number&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;todoKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;details&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="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But don't take my word for this and read &lt;a href="https://tkdodo.eu/blog/effective-react-query-keys" rel="noopener noreferrer"&gt;Dominik's article&lt;/a&gt; that goes into much more detail.&lt;/p&gt;

&lt;h4&gt;
  
  
  Keep this in mind to master mutations
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;injectQuery is declarative, injectMutation is imperative.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TanStack's Queries mostly run automatically. We define the dependencies, but TanStack Query takes care of running the query immediately. It also performs smart background updates when necessary. This works great for queries because we want to keep what we see on the screen in sync with the actual data on the backend.&lt;/p&gt;

&lt;p&gt;For mutations, this doesn't make sense: Imagine a new todo is created every time the user focuses their browser window... Thus, instead of running a mutation instantly, TanStack Query gives us a function that can be invoked whenever we want to make the mutation.&lt;/p&gt;

&lt;p&gt;For this reason, mutations also do not share state like &lt;code&gt;injectQuery&lt;/code&gt; does. You can use &lt;code&gt;injectQuery&lt;/code&gt; multiple times in different components and will get the same, cached results. This is not the same for &lt;code&gt;injectMutation&lt;/code&gt;. Each time you will get a new mutation, which keeps track of its own state and can be triggered with it's &lt;code&gt;mutate&lt;/code&gt;-method.&lt;/p&gt;

&lt;h5&gt;
  
  
  Be aware of &lt;code&gt;await&lt;/code&gt;-ed Promises
&lt;/h5&gt;

&lt;p&gt;Promises returned from the mutation callbacks are awaited by TanStack Query. &lt;code&gt;invalidateQueries&lt;/code&gt; returns a Promise. If you want your mutation to stay in loading state while your related queries update, you have to return the result of &lt;code&gt;invalidateQueries&lt;/code&gt; from the callback!&lt;/p&gt;

&lt;h5&gt;
  
  
  Know when to use &lt;code&gt;mutate&lt;/code&gt; or &lt;code&gt;injectMutation&lt;/code&gt; callbacks
&lt;/h5&gt;

&lt;p&gt;You can have callbacks on &lt;code&gt;injectMutation&lt;/code&gt; as well as on &lt;code&gt;mutate&lt;/code&gt; itself. Know that the callbacks on &lt;code&gt;injectMutation&lt;/code&gt; fire before the callbacks on &lt;code&gt;mutate&lt;/code&gt;. Callbacks on &lt;code&gt;mutate&lt;/code&gt; might not fire at all if the component/directive/service is destroyed before the mutation has finished.&lt;/p&gt;

&lt;p&gt;It's good practice to separate concerns in your callbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do things that are absolutely necessary and logic related (like query invalidation) in the &lt;code&gt;injectMutation&lt;/code&gt; callbacks.&lt;/li&gt;
&lt;li&gt;Do UI related things like redirects or showing toast notifications in &lt;code&gt;mutate&lt;/code&gt; callbacks. If the user navigates away from the current screen before the mutation finishes, those will purposefully not fire.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// always, declared in the Custom Injection Function (CIF)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;injectUpdateTodo&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectQueryClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;injectMutation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mutationFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ✅ always invalidate the todo list&lt;/span&gt;
    &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidateQueries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;queryKey&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;todos&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;list&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;span class="c1"&gt;// in the component&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;updateTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectUpdateTodo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&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;newTitle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// ✅ only redirect if we're still on the detail page&lt;/span&gt;
  &lt;span class="c1"&gt;// when the mutation finishes&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;todos&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 separation is especially neat if &lt;code&gt;injectMutation&lt;/code&gt; is part of a Custom Injection Function, CIF. The CIF contains all query related logic while UI related actions come from the component driving the UI. This also makes the CIF more reusable, because how you interact with the UI might vary on a case by case basis - but the invalidation logic will likely always be the same! &lt;/p&gt;

&lt;p&gt;Again, do yourself a favor and read this &lt;a href="https://tkdodo.eu/blog/mastering-mutations-in-react-query" rel="noopener noreferrer"&gt;this incredible article&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/tkdodo"&gt;@tkdodo&lt;/a&gt;. &lt;/p&gt;

&lt;h4&gt;
  
  
  Got a question? Read the FAQs!
&lt;/h4&gt;

&lt;p&gt;As you dive deeper into TanStack Query and are starting to use it for more advanced use cases you will inevitably end up with a bunch of questions. It's new technology after all. However, us Angular developers have a huge advantage, because TanStack Query has been used in the React community for years most, if not all, of your questions have already been asked, and answered! Dominik again, did us a huge favor and consolidated answers to the most frequent questions in &lt;a href="https://tkdodo.eu/blog/react-query-fa-qs" rel="noopener noreferrer"&gt;this great article.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why introduce another dependency?
&lt;/h2&gt;

&lt;p&gt;Yes, TanStack Query does come with a learning curve. However, I would describe this learning curve more as a period of unlearning async state micro-management and replacing it with a straightforward concept of hierarchical query keys, declarative data fetching in components, and knowing when to invalidate queries of which we know the server data has changed.&lt;/p&gt;

&lt;p&gt;If you decide to add TanStack Query to your application, it will very likely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Help you remove many lines of complicated and misunderstood code from your application and replace with just a handful of lines of TanStack Query logic.&lt;/li&gt;
&lt;li&gt;Make your application more maintainable and easier to build new features without worrying about wiring up new server state data sources&lt;/li&gt;
&lt;li&gt;Have a direct impact on your end-users by making your application feel faster and more responsive than ever before.&lt;/li&gt;
&lt;li&gt;Potentially help you save on bandwidth and increase memory performance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So it's up to you. Is it worth adding another dependency?&lt;/p&gt;

&lt;h2&gt;
  
  
  So this replaces NgRx right?
&lt;/h2&gt;

&lt;p&gt;No, but it will greatly reduce code you have to write yourself to manage server state. I went into great detail in the beginning describing all the requirements one has to consider when developing a solution that adequately and declaratively manages server state. Imagine yourself (re-)implementing an NgRx store that takes care of everything above.&lt;/p&gt;

&lt;p&gt;Don't do that to yourself. The creators of TanStack Query already built an amazing solution that deals with complexities of managing server state for you. It works amazingly well out-of-the-box, with zero-config, and can be customized to your liking as your application grows.&lt;/p&gt;

&lt;p&gt;What TanStack Query does not do for you is manage your application's global client state. It is simply not built to keep track of a complex chain of UI interactions, which we so often deal with in enterprise applications, or knows how to keep track of changes to an in memory list of elements that we are reordering with drag and drop, whose names and descriptions we can edit, all before hitting save. That's where NgRx and all the other client state libraries of the Angular ecosystem shine and will continue to be our trusted companions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I highly recommend all of you to check out the TanStack Query documentation. It is incredibly good and gives you everything to get up and running in no time. &lt;/p&gt;

&lt;p&gt;Even better, if there is any question left unanswered by the official documentation you can bet that &lt;a class="mentioned-user" href="https://dev.to/tkdodo"&gt;@tkdodo&lt;/a&gt; has written post on &lt;a href="https://tkdodo.eu/blog/all" rel="noopener noreferrer"&gt;his blog&lt;/a&gt; about the topic that explains and demonstrates the concept in a way that is absolutely incredible!&lt;/p&gt;

&lt;p&gt;If you want to get a more thorough Angular specific introduction, &lt;a href="https://ducin.dev/angular-query-core-concepts" rel="noopener noreferrer"&gt;this article&lt;/a&gt; by Tomasz Ducin is well worth the read!&lt;/p&gt;

&lt;p&gt;Keep in mind that the Angular port is currently in an experimental stage. Breaking changes can still happen in any release and we are using this incredible piece of code at our own risk. &lt;/p&gt;

&lt;p&gt;However, the core of TanStack Query is in it's 5th major version already. It's battle tested in millions of production apps and finally making its way to the Angular ecosystem.&lt;/p&gt;

&lt;p&gt;To ensure that we get the best TanStack Query experience possible we are also encouraged to share feedback and participate in the discussion on GitHub, which you can check out &lt;a href="https://github.com/TanStack/query/discussions/6293" rel="noopener noreferrer"&gt;here!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what are you waiting for? Give TanStack Query a try and never look back! I have been using it for a few weeks now and am already absolutely hooked!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is your sign(al) to try TanStack Query &amp;amp; Angular!!!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As always, do you have any further questions? What do you think of TanStack Query? Could you see yourself adding it to your project? Do you think you'd need to see an example app where queries and mutations are used in components, directives, or services? Do you have any other topics you'd like me to write about? I am curious to hear your thoughts. Please don't hesitate to leave a comment or send me a message.&lt;/p&gt;

&lt;p&gt;Finally, if you liked this article feel free to like and share it with others. If you enjoy my content follow me on &lt;a href="https://twitter.com/goetzrobin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/goetzrobin" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tanstack</category>
      <category>angular</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Getting Started with spartan/ui - Shadcn-like UI Components for Angular</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Thu, 24 Aug 2023 16:04:02 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/getting-started-with-spartanui-shadcn-like-ui-components-for-angular-8df</link>
      <guid>https://forem.com/playfulprogramming-angular/getting-started-with-spartanui-shadcn-like-ui-components-for-angular-8df</guid>
      <description>&lt;p&gt;We're all familiar with this: We are starting a new project and are looking for some beautiful UI components. While we could technically build these from scratch, we want to start building instead of reinventing the wheel. We need a solution to hit the ground running without sacrificing quality or accessibility (a11y).&lt;/p&gt;

&lt;p&gt;So we venture into the world of Angular component libraries.&lt;br&gt;
While they all provide incredible variety and most of them come with solid accessibility features, it seems that most Angular UI libraries come with a strong corporate branding that often doesn't quite align with the project's needs. More importantly, they mostly lack an easy way to customize or extend components and do not allow us to let them make them our own.&lt;/p&gt;

&lt;p&gt;Then we look at the React ecosystem and all the incredible projects built on &lt;a href="https://www.radix-ui.com/" rel="noopener noreferrer"&gt;RadixUI&lt;/a&gt; and &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn.&lt;/a&gt; I don't know about you, but when I do that I always get a little jealous.&lt;/p&gt;
&lt;h2&gt;
  
  
  shadcn - A game-changing UI library (for React)
&lt;/h2&gt;

&lt;p&gt;Why? &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; comes with all the components you could ever need for a project, and all of them come in beautiful styles by default. However, it still allows you to adjust and customize every single UI primitive as you please.&lt;/p&gt;

&lt;p&gt;How does it do that? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It builds on &lt;strong&gt;RadixUI&lt;/strong&gt;, a UI library that is completely &lt;strong&gt;un-styled&lt;/strong&gt; by default, and comes with an intuitive and extensible API, as well as great. &lt;/li&gt;
&lt;li&gt;It is styled using &lt;strong&gt;TailwindCSS&lt;/strong&gt; classes and &lt;strong&gt;CSS variables&lt;/strong&gt; that give you the perfect amount of flexibility while pushing you towards using solid design principles.&lt;/li&gt;
&lt;li&gt;Instead of making you install the styles through a npm package, it allows you to copy its beautifully crafted primitives directly into your code base, which means you &lt;strong&gt;own the code&lt;/strong&gt; and can adjust everything to fit your needs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem with shadcn for Angular developer's is that it is built on top of React... &lt;/p&gt;

&lt;p&gt;Now imagine an accessible, open-source Angular UI library that doesn't come pre-styled, allowing you to have full creative control over its appearance. Angular' shadcn implementation so to say.&lt;/p&gt;
&lt;h2&gt;
  
  
  spartan/ui - shadcn for Angular
&lt;/h2&gt;

&lt;p&gt;Enter &lt;a href="https://spartan.ng" rel="noopener noreferrer"&gt;spartan/ui&lt;/a&gt; – an innovative collection of Angular UI primitives that are un-styled and accessible by default.&lt;/p&gt;
&lt;h3&gt;
  
  
  brain &amp;amp; helm - The building blocks of spartan/ui
&lt;/h3&gt;

&lt;p&gt;To achieve our goal of a shadcn-like development experience, spartan/ui comes in two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Through &lt;strong&gt;spartan/ui/brain&lt;/strong&gt;, our goal is to make this process more straightforward and efficient. We offer a versatile collection of &lt;strong&gt;un-styled&lt;/strong&gt; UI building blocks that can be easily tailored to match your project's distinct visual and functional preferences.&lt;/li&gt;
&lt;li&gt;With &lt;strong&gt;spartan/ui/helm&lt;/strong&gt;, we provide pre-designed styles built on &lt;strong&gt;TailwindCSS&lt;/strong&gt; and &lt;strong&gt;CSS variables&lt;/strong&gt;. Just like with shadcn, you can copy them into your project so you retain full control over their code, appearance, and overall experience.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  @spartan-ng/cli - one command to rule them all
&lt;/h3&gt;

&lt;p&gt;To make this as easy as possible, &lt;strong&gt;spartan/ui&lt;/strong&gt; comes equipped with a CLI that allows you to effortlessly integrate our components into your Nx or Angular workspaces. With a single command, you can add any of its 30+ spartan/ui primitives to your projects.&lt;/p&gt;

&lt;p&gt;But that's not all – the CLI's capabilities extend beyond just adding components. You can also leverage it to incorporate one of 12 custom themes into your Angular or Nx applications, letting you truly own the visual appearance of your projects.&lt;/p&gt;
&lt;h2&gt;
  
  
  Your first spartan app
&lt;/h2&gt;

&lt;p&gt;So let's see what getting up and running with &lt;strong&gt;spartan/ui&lt;/strong&gt; looks like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you would rather follow along to a video version of this article, check it out on &lt;a href="https://www.youtube.com/watch?v=lfmR6U-P8t4&amp;amp;ab_channel=RobinGoetz" rel="noopener noreferrer"&gt;YouTube.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up an Nx' Angular workspace
&lt;/h3&gt;

&lt;p&gt;As mentioned above, &lt;strong&gt;spartan/ui&lt;/strong&gt; follows the same paradigm as &lt;strong&gt;shadcn&lt;/strong&gt;, that you should own the code that allows you to style, extend, and compose your UI components.&lt;/p&gt;

&lt;p&gt;While we are working on a standalone API, Nx provides incredible tooling for exactly this use case. Therefore, the initial version of &lt;strong&gt;spartan/ui&lt;/strong&gt;'s CLI is an Nx plugin. &lt;/p&gt;

&lt;p&gt;Hence, for this tutorial, we will create a new Angular project inside an Nx workspace.&lt;/p&gt;
&lt;h4&gt;
  
  
  Running create-nx-workspace
&lt;/h4&gt;

&lt;p&gt;Again, Nx makes this incredibly easy. Simply run the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-nx-workspace@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose a meaningful name, I chose&lt;/li&gt;
&lt;li&gt;Select Angular as your stack.&lt;/li&gt;
&lt;li&gt;Opt for a standalone project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Important:&lt;/strong&gt; Pick CSS for your styles.&lt;/li&gt;
&lt;li&gt;Add an (optional) end-to-end test runner of your choice.&lt;/li&gt;
&lt;li&gt;Select standalone components.&lt;/li&gt;
&lt;li&gt;Only add routing if you want to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, wait for Nx to work its magic, install all necessary dependencies, and set up your Angular workspace.&lt;/p&gt;

&lt;h4&gt;
  
  
  Removing boilerplate
&lt;/h4&gt;

&lt;p&gt;I am a big proponent of having your template and styles in the same file as your Component class. Therefore, I deleted the &lt;code&gt;src/app/app.component.html&lt;/code&gt; and &lt;code&gt;src/app/app.component.css&lt;/code&gt; files created by the workspace generator. I also got rid of the &lt;code&gt;src/app/nx-welcome.component.ts&lt;/code&gt; and changed the contents of my &lt;code&gt;src/app/app.component.ts&lt;/code&gt; to 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="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="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;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;imports&lt;/span&gt;&lt;span class="p"&gt;:&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;button&amp;gt;Hello from {{title}}&amp;lt;/button&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;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sparta&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;One more thing before we are ready to start adding &lt;strong&gt;spartan/ui.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding TailwindCSS
&lt;/h4&gt;

&lt;p&gt;As &lt;strong&gt;spartan/ui&lt;/strong&gt; is built on top of TailwindCSS, we need a working setup of it for our project.&lt;/p&gt;

&lt;p&gt;Thankfully, Nx again makes this incredibly easy for us. Simply run the following command and select your application when prompted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx g @nx/angular:setup-tailwind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;tailwind.config.ts&lt;/code&gt; file and install all the necessary dependencies. Let's continue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing @spartan-ng/cli
&lt;/h3&gt;

&lt;p&gt;We are now ready to add &lt;strong&gt;spartan/ui&lt;/strong&gt; to our project. To make our lives much easier, we will use the Nx plugin, which we install like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @spartan-ng/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing @spartan-ng/ui-core
&lt;/h3&gt;

&lt;p&gt;Then we add the &lt;code&gt;@spartan-ng/ui-core&lt;/code&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @spartan-ng/ui-core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It contains a bunch of helpers, like the &lt;code&gt;hlm&lt;/code&gt; function, which powers our tailwind class merging, and most importantly the &lt;code&gt;@spartan-ng/ui-core/hlm-tailwind-preset&lt;/code&gt;, which contains all the necessary extensions to tailwind, which make our &lt;strong&gt;spartan/ui/helm&lt;/strong&gt; directives and components work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up tailwind.config.js
&lt;/h3&gt;

&lt;p&gt;We now have to add this spartan-specific configuration to your TailwindCSS setup. Simply add &lt;code&gt;@spartan-ng/ui-core/hlm-tailwind-preset&lt;/code&gt; to the presets array of your config file:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createGlobPatternsForDependencies&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nx/angular/tailwind&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;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/** @type {import('tailwindcss').Config} */&lt;/span&gt;
&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@spartan-ng/ui-core/hlm-tailwind-preset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/**/!(*.stories|*.spec).{ts,html}&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;createGlobPatternsForDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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;plugins&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;
  
  
  Adding CSS variables
&lt;/h3&gt;

&lt;p&gt;To complete your TailwindCSS setup, we need to add our spartan-specific CSS variables to your style entry point. This is most likely a &lt;code&gt;styles.css&lt;/code&gt; in the &lt;code&gt;src&lt;/code&gt; folder of your application.&lt;/p&gt;

&lt;p&gt;Again, we are using Nx, so our plugin will take care of the heavy lifting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx g @spartan-ng/cli:ui-theme
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select the only application in the project&lt;/li&gt;
&lt;li&gt;Choose a theme you want to try&lt;/li&gt;
&lt;li&gt;Select a border-radius you like&lt;/li&gt;
&lt;li&gt;Leave the path empty (the plugin should be smart enough to figure out the correct one for most setups)&lt;/li&gt;
&lt;li&gt;Leave the prefix empty as we add a default theme&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, check out your &lt;code&gt;styles.css&lt;/code&gt; and see the following &lt;strong&gt;spartan/ui&lt;/strong&gt;-specific variables being added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="py"&gt;--font-sans&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="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="py"&gt;--background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt; &lt;span class="m"&gt;3.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--card-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt; &lt;span class="m"&gt;3.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--popover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--popover-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt; &lt;span class="m"&gt;3.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5.9%&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--primary-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;4.8%&lt;/span&gt; &lt;span class="m"&gt;95.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--secondary-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5.9%&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;4.8%&lt;/span&gt; &lt;span class="m"&gt;95.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--muted-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;3.8%&lt;/span&gt; &lt;span class="m"&gt;46.1%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;4.8%&lt;/span&gt; &lt;span class="m"&gt;95.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--accent-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5.9%&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--destructive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;84.2%&lt;/span&gt; &lt;span class="m"&gt;60.2%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--destructive-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5.9%&lt;/span&gt; &lt;span class="m"&gt;90%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5.9%&lt;/span&gt; &lt;span class="m"&gt;90%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--ring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5.9%&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="py"&gt;--background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt; &lt;span class="m"&gt;3.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt; &lt;span class="m"&gt;3.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--card-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--popover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt; &lt;span class="m"&gt;3.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--popover-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--primary-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5.9%&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;3.7%&lt;/span&gt; &lt;span class="m"&gt;15.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--secondary-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;3.7%&lt;/span&gt; &lt;span class="m"&gt;15.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--muted-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;5%&lt;/span&gt; &lt;span class="m"&gt;64.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;3.7%&lt;/span&gt; &lt;span class="m"&gt;15.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--accent-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--destructive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;62.8%&lt;/span&gt; &lt;span class="m"&gt;30.6%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--destructive-foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;3.7%&lt;/span&gt; &lt;span class="m"&gt;15.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;3.7%&lt;/span&gt; &lt;span class="m"&gt;15.9%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="py"&gt;--ring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;4.9%&lt;/span&gt; &lt;span class="m"&gt;83.9%&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;
  
  
  Adding our first primitive
&lt;/h3&gt;

&lt;p&gt;Awesome! We are now all set up to use &lt;strong&gt;spartan/ui&lt;/strong&gt; in our project. Let's leverage our Nx plugin one more time and add the button primitive to our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx g @spartan-ng/cli:ui button
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select an appropriate directory to put your components, e.g. libs/spartan&lt;/li&gt;
&lt;li&gt;Choose the default false, when prompted if you want to skip installing the necessary dependencies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the plugin finishes, you will see that a new buildable library was added in your &lt;code&gt;libs/spartan/button-helm&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;It contains the source code of the &lt;code&gt;HlmButtonDirective&lt;/code&gt;, which comes with a bunch of different styles that are applied through a &lt;code&gt;HostBinding&lt;/code&gt; based on the different inputs of the directive.&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;HostBinding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="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;cva&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VariantProps&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;class-variance-authority&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;hlm&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;@spartan-ng/ui-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;ClassValue&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;clsx&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;buttonVariants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cva&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background&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;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-primary text-primary-foreground hover:bg-primary/90&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;destructive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-destructive text-destructive-foreground hover:bg-destructive/90&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border border-input hover:bg-accent hover:text-accent-foreground&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-secondary text-secondary-foreground hover:bg-secondary/80&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ghost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hover:bg-accent hover:text-accent-foreground&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;underline-offset-4 hover:underline text-primary&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;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h-10 py-2 px-4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h-9 px-3 rounded-md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h-11 px-8 rounded-md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h-10 w-10&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="na"&gt;defaultVariants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonVariants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VariantProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;buttonVariants&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="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;[hlmBtn]&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="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;HlmButtonDirective&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;_variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;variant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;default&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;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;variant&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;return&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;_variant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;variant&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;_variant&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="nx"&gt;_class&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;generateClasses&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="nx"&gt;_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;default&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;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;size&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;return&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;_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonVariants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;size&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;_size&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="nx"&gt;_class&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;generateClasses&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="nx"&gt;_inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ClassValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ClassValue&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;_inputs&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_class&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;generateClasses&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;HostBinding&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&lt;/span&gt;&lt;span class="dl"&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;_class&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;generateClasses&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;generateClasses&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;hlm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buttonVariants&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;variant&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;_variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&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;_size&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;_inputs&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;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: Currently the plugin adds dependencies correctly, but their peer dependencies are not installed by Nx&lt;br&gt;
Simply run &lt;code&gt;npm i&lt;/code&gt; after the &lt;code&gt;@spartan-ng/cli:ui&lt;/code&gt; call to make sure everything is installed correctly.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Using our first primitive
&lt;/h3&gt;

&lt;p&gt;To use our new directive we simply add the directive to our &lt;code&gt;button&lt;/code&gt; in our &lt;code&gt;src/app/app.component/ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&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;HlmButtonDirective&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;@spartan-ng/button-helm&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;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;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;HlmButtonDirective&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;button hlmBtn variant="outline"&amp;gt;Hello from {{title}}&amp;lt;/button&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;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sparta&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;Then we start our development server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and see our beautifully styled &lt;strong&gt;spartan/ui&lt;/strong&gt; button:&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%2Fylbypwya9nq2rqb1p24o.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%2Fylbypwya9nq2rqb1p24o.png" alt="Spartan Button in dark gray with rounded corners and white text saying Hello from sparta" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To change the appearance to another variant we simply add a &lt;code&gt;variant&lt;/code&gt; input to our &lt;code&gt;&amp;lt;button hlmBtn&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&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;HlmButtonDirective&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;@spartan-ng/button-helm&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;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;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;HlmButtonDirective&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;button hlmBtn variant="outline"&amp;gt;Hello from {{title}}&amp;lt;/button&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;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sparta&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;Our styles will be updated accordingly and we see our outlined button:&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%2Fyna58he36f0fvziamb36.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%2Fyna58he36f0fvziamb36.png" alt="Spartan Button in light gray with rounded corners and dark text and dark border saying Hello from sparta" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other available components
&lt;/h2&gt;

&lt;p&gt;With the release of the initial alpha version, there are 30 components available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accordion&lt;/li&gt;
&lt;li&gt;Alert&lt;/li&gt;
&lt;li&gt;Alert Dialog&lt;/li&gt;
&lt;li&gt;Aspect Ratio&lt;/li&gt;
&lt;li&gt;Avatar&lt;/li&gt;
&lt;li&gt;Badge&lt;/li&gt;
&lt;li&gt;Button&lt;/li&gt;
&lt;li&gt;Card&lt;/li&gt;
&lt;li&gt;Collapsible&lt;/li&gt;
&lt;li&gt;Combobox&lt;/li&gt;
&lt;li&gt;Command&lt;/li&gt;
&lt;li&gt;Context Menu&lt;/li&gt;
&lt;li&gt;Dialog&lt;/li&gt;
&lt;li&gt;Dropdown Menu&lt;/li&gt;
&lt;li&gt;Input&lt;/li&gt;
&lt;li&gt;Icon&lt;/li&gt;
&lt;li&gt;Label&lt;/li&gt;
&lt;li&gt;Menubar&lt;/li&gt;
&lt;li&gt;Popover&lt;/li&gt;
&lt;li&gt;Progress&lt;/li&gt;
&lt;li&gt;Radio Group&lt;/li&gt;
&lt;li&gt;Scroll Area&lt;/li&gt;
&lt;li&gt;Separator&lt;/li&gt;
&lt;li&gt;Sheet&lt;/li&gt;
&lt;li&gt;Skeleton&lt;/li&gt;
&lt;li&gt;Switch&lt;/li&gt;
&lt;li&gt;Tabs&lt;/li&gt;
&lt;li&gt;Textarea (covered by hlmInput directive)&lt;/li&gt;
&lt;li&gt;Toggle&lt;/li&gt;
&lt;li&gt;Typography&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can add new components the same way as we did for the &lt;code&gt;button.&lt;/code&gt; I also plan to create more blog posts and videos, which show how to use &lt;strong&gt;spartan/ui&lt;/strong&gt; to build your user interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;spartan/ui&lt;/strong&gt; is still in alpha, so there is a long way (a marathon some history nerds might suggest) ahead of us. However, I am super excited that this project is finally getting off the ground and you get to try it out and provide me with incredibly valuable feedback. I hope spartan/ui becomes the shadcn of the Angular ecosystem and together with incredible projects like &lt;a href="https://analogjs.org/" rel="noopener noreferrer"&gt;AnalogJs&lt;/a&gt; can bring a similar innovation explosion to all of us.&lt;/p&gt;

&lt;p&gt;As always, do you have any further questions or suggestions for blog posts? What do you think of spartan? Could you see yourself adding it to your project? I am curious to hear your thoughts. Please don't hesitate to leave a comment or send me a message.&lt;/p&gt;

&lt;p&gt;Finally, if you liked this article feel free to like and share it with others. If you enjoy my content follow me on &lt;a href="https://twitter.com/goetzrobin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/goetzrobin" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>ui</category>
    </item>
    <item>
      <title>SPARTAN. Type-safe Angular full-stack development powered by Analog.</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Tue, 25 Apr 2023 12:07:26 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/spartan-type-safe-angular-full-stack-development-powered-by-analog-17dm</link>
      <guid>https://forem.com/playfulprogramming-angular/spartan-type-safe-angular-full-stack-development-powered-by-analog-17dm</guid>
      <description>&lt;p&gt;In 2023, Server-Side Rendering (SSR) is an essential part of modern web applications. In the React ecosystem NextJs has become so incredibly popular that the official React documentation now recommends to use it when creating a new project. The Vue docs point to Nuxt as the recommended way to take a Vue application "from the example to a production-ready SSR app."&lt;/p&gt;

&lt;p&gt;So what about Angular? We all know Angular as a popular single page application framework (SPA) by Google. It's strength has always been in enterprise applications, who require developers to build and manage complex systems. But indeed Angular has had support for server side rendering (SSR) through Angular Universal for years. However, in developer surveys Angular's SSR capabilities has been the top area Angular users want to see improvements in. To put it short, SSR is currently one of the weaknesses of this amazing framework. The good news, the team is working hard to bring improvements to Universal and the core framework that will soon allow us to leverage the full power of Angular combined with modern SSR solutions. I invite you to read &lt;a href="https://blog.angular.io/whats-next-for-server-side-rendering-in-angular-2a6f27662b67" rel="noopener noreferrer"&gt;this exciting article&lt;/a&gt; by Jessica Janiuk from the Angular team to learn more.&lt;/p&gt;

&lt;p&gt;The even better news is that SSR with Angular today might be a lot further than you think already. &lt;a class="mentioned-user" href="https://dev.to/brandontroberts"&gt;@brandontroberts&lt;/a&gt; has created an amazing community project called Analog. A metaframework a la NextJs or Nuxt built on top of Angular, Vite, and Nitro. There will be a whole article dedicated to getting started with Analog that will go into much more detail. I also recommend to check out the official documentation at &lt;a href="https://analogjs.org/" rel="noopener noreferrer"&gt;analogjs.org&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;In this article, we will go a step further and augment Analog with some other tools to create a complete, incredibly powerful application stack that is typesafe from the database to the DOM rendering template. Thankfully, Angular's TypeScript support is unrivaled as the framework has been developed entirely in TypeScript since its initial release became available more than ten years ago. To maintain this high focus on typesafety throughout the entire application is only natural. With both client and server written in TypeScript, Analog puts us in an exceptional position to fully capitalize on this, especially as more and more fantastic server side capabilities become available in the Typescript ecosystem.&lt;/p&gt;

&lt;p&gt;Once we augment these two fundamental pillars with other amazing (mostly) typesafe technologies we end up with the following stack: &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;, &lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt;, &lt;a href="https://analogjs.org/" rel="noopener noreferrer"&gt;Analog&lt;/a&gt;, &lt;a href="https://trpc.io/" rel="noopener noreferrer"&gt;tRPC&lt;/a&gt;, &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;, &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;, and &lt;a href="https://nx.dev/" rel="noopener noreferrer"&gt;Nx&lt;/a&gt;. Or short: SPARTAN. &lt;/p&gt;

&lt;p&gt;Of course, all the source code is available to you and if you are already excited about this and cannot wait to get your hands dirty I invite you to check out the repository &lt;a href="https://github.com/goetzrobin/spartan" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will do my best to present compelling arguments for each of the underlying technologies. Continue reading to learn why I think they deserve to power your upcoming full-stack application.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SPARTAN Stack
&lt;/h2&gt;

&lt;p&gt;Let’s get our hands dirty and see the different components that make up the SPARTAN stack:&lt;/p&gt;

&lt;h3&gt;
  
  
  Nx
&lt;/h3&gt;

&lt;p&gt;Let's start with the workspace. The location where your code will reside and the features that meet the needs of your users will be implemented. It reminds me of a craftsman's workshop, if you will. There is only a workbench at first, and as you move along and continue working on your projects, you gradually fill it with all the tools you need. Our beloved uncle Nx has been doing woodworking for years and arrives on the first day with a toolbox filled with every tool one could ever need. But let’s get back to software:&lt;/p&gt;

&lt;p&gt;Nx is a next-generation build system that offers  first-class monorepo support and strong integrations for all well-known javascript frameworks. It enables you to use  frontend frameworks like Angular or backend frameworks like NestJs to architect, test, and build your project at any size.&lt;/p&gt;

&lt;p&gt;It comes with a fantastic collection of tools that make managing monorepos much easier. A monorepo is a multi-project and multi-library version-controlled code repository. By enabling us to reuse components and types not just between multiple front-end libraries but also between the frontend and the backend, monorepos make it simple to increase the DRYness of our code. The ability to share code across the client and server in a s∂ingle repository is a game-changer for us. There are many more advantages to monorepos, and far better papers have been written about them.&lt;/p&gt;

&lt;p&gt;To get started with Nx you can run the following command and select the following options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-nx-workspace@latest

✔ Choose your style                     · integrated
✔ What to create &lt;span class="k"&gt;in &lt;/span&gt;the new workspace   · angular
✔ Repository name                       · spartan
✔ Application name                      · app
✔ Default stylesheet format             · css
✔ Enable distributed caching to make your CI faster · No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Please note that all the commands are supposed to illustrate a basic setup. There’s a lot of fine tuning necessary to get the application up and running. Cloning the GitHub repository is the fastest way to get started.&lt;/p&gt;
&lt;h3&gt;
  
  
  Angular
&lt;/h3&gt;

&lt;p&gt;The aforementioned command will create for you a Nx workspace that is already set up to be used with Angular. Well done! Your SPARTAN project's foundation has just been established!&lt;br&gt;
Let me briefly explain why Angular is my preferred framework before we go on:&lt;/p&gt;

&lt;p&gt;Angular has opinionated routing solutions, a strong HttpClient for making asynchronous web queries, and, my personal favorite, an outstanding dependency injection system. Angular provides programmers with all the resources they need to create outstanding applications right out of the box. It is more than simply a library; it is a full-fledged framework.&lt;/p&gt;

&lt;p&gt;Also, Angular is written in TypeScript. Static type checking is now possible with our IDE, helping in identifying mistakes early on. When both the frontend and the backend are created using TypeScript, this is extremely powerful. This enables us to be typesafe from the database all the way through to the template that renders our data to the DOM!&lt;/p&gt;

&lt;p&gt;When creating something from scratch, it's easy to overlook that applications on the internet are utilized by individuals of all abilities. It's possible that some users will need assistive technology to use our applications. Good accessibility (a11y) features have been baked into the Angular framework. By default, the Angular development team prioritizes accessibility. Look at the Angular Component Development Kit, for example. Its accessibility features are fantastic. It offers all the tools necessary to build your own extendable, aesthetically pleasing, and easily accessible components and is maintained by the framework's creators. Built with Angular, for everyone.&lt;/p&gt;

&lt;p&gt;Building with Angular, also means building with everyone. The incredible community surrounding Angular is perhaps its best feature. They are an amazing group of folks from all different backgrounds. The amazing Angular core team, which cares about users, developers, and ensures that everyone has a say when they plan and build new features, is the community's spearhead (pardon the pun). Angular truly supports excellent community projects, and the community takes full advantage by providing tremendous tools and features on top of an amazing framework.&lt;/p&gt;
&lt;h3&gt;
  
  
  Analog
&lt;/h3&gt;

&lt;p&gt;We are off to a strong start and have a full-fledged, complete framework as the essential component of our stack. So let's add Analog to Angular and give it some superpowers. Adding Analog to your workspace is quite simple with the brand-new Nx plugin:&lt;/p&gt;

&lt;p&gt;Install the &lt;code&gt;@analogjs/platform&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @analogjs/platform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And add a preconfigured Analog application:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nx g @analogjs/platform:app analog-trpc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Analog is a metaframework for Angular. It provides you with all the benefits of Angular, but extends its capabilities with even more:&lt;/p&gt;

&lt;p&gt;Analog uses Vite for serving and building as well as Vitest for testing. Vite is  a next generation JavaScript  bundler that is super fast. It also gives us access to the Vite ecosystem  with hundreds of very powerful plugins. A mature frontend framework like Angular combined with access to all the benefits of Vite is truly incredible. It seems like the Angular team shares this opinion as it is also working to move the built in dev server for &lt;a href="https://twitter.com/mgechev/status/1644018247748943872" rel="noopener noreferrer"&gt;Angular to Vite.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1644018247748943872-879" src="https://platform.twitter.com/embed/Tweet.html?id=1644018247748943872"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1644018247748943872-879');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1644018247748943872&amp;amp;theme=dark"
  }



 &lt;/p&gt;

&lt;p&gt;Another incredible advantage of Analog is that it comes equipped to handle both Server-Side Rendering (SSR) and Static Site Generation (SSG) for Angular applications. With Analog, Angular can easily power marketing websites, blog pages, and more! As Analog is SSR by default, no further configuration is necessary.&lt;br&gt;
Analog comes with SSG support. It makes it simple for you to export your website as basic static assets that you can host on GitHub Pages, S3, or any other static file server! Whether you choose to go fully static or leverage the benefits of server side rendering, Analog offers all of the SSR benefits for SEO and performance today. Still, there is even more. With Angular 16, we already get non-destructive hydration and significantly improved web vitals enhancements. You can expect even greater performance once zoneless applications powered by signals are released in version 17. Building on Analog puts you in a great position to take full advantage of those features.&lt;/p&gt;

&lt;p&gt;Last but not least, Analog supports API (server) routes and file-based routing for Angular apps. In an analog application, the folder hierarchy and filenames determine the routing. You won't ever need to declare a route array again. Don't worry, you can still use the full power of the Angular router with its route guards and resolvers by defining route metadata within your files. &lt;/p&gt;
&lt;h3&gt;
  
  
  TailwindCSS
&lt;/h3&gt;

&lt;p&gt;Every good application needs a great UI. Although it's easier said than done, there are tools that help us do our best work. Tailwind is one of those essential tools for me.&lt;br&gt;
Tailwind is "a utility-first CSS framework packed with classes like &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;pt-4&lt;/code&gt;, &lt;code&gt;text-center&lt;/code&gt; and &lt;code&gt;rotate-90&lt;/code&gt;."&lt;br&gt;
These utility classes let you to modify the layout, color, spacing, font, shadows, and more to create a distinctive component design without leaving your HTML or adding any additional CSS code. To me, there are several key factors that earn Tailwind it’s place in the SPARTAN stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Its set of utility classes have good design principles built in.&lt;/li&gt;
&lt;li&gt;Building your reusable UI components with tailwind gives you consistency and flexibility out of the box.&lt;/li&gt;
&lt;li&gt;Tailwind plays together perfectly with Angular. The framework is built around components which encapsulate part of the UI and can be assembled to achieve the user experience you desire.&lt;/li&gt;
&lt;li&gt;I would even claim that Angular makes working with tailwind especially fun: Angular’s directives are perfect to hide the often complained about ugly markup. I wrote &lt;a href="https://dev.to/goetzrobin/reusable-buttons-with-angular-tailwind-ki9"&gt;a whole article&lt;/a&gt; about this directive and tailwind love affair.  Also, if you dare you can something like this to extract your tw classes and keep your Angular templates clean:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1644820404861583361-882" src="https://platform.twitter.com/embed/Tweet.html?id=1644820404861583361"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1644820404861583361-882');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1644820404861583361&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: This seems to be highly controversial so use at your own risk...&lt;/p&gt;

&lt;p&gt;Since we are using Vite and Nx adding tailwind will also be super straightforward. We install the necessary dependencies&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss postcss autoprefixer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, we add a tailwind config to the root of our application:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createGlobPatternsForDependencies&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@nrwl/angular/tailwind&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;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/** @type {import("tailwindcss").Config} */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/**/!(*.stories|*.spec).{ts,html}&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;createGlobPatternsForDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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;Finally, we tell Vite to run PostCSS and create the tailwind classes by adding a postcss.config.js file to our application:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tailwindcss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwind.config.js&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;autoprefixer&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;
  
  
  tRPC
&lt;/h3&gt;

&lt;p&gt;At this point our frontend is ready to go! However, it turns out that Analog sneakily turned us into a full stack developer. Code that we put inside the server folder is actually executed on the server 🤯 It’s that easy! Well let’s take full advantage of our API being built in the same language and the same repository. Let’s make our server client interaction super smooth and super typesafe with tRPC.&lt;/p&gt;

&lt;p&gt;tRPC stands for TypeScript Remote Procedure Call, and is a lightweight library for remotely calling backend functions on the client side. It makes communication between the backend and frontend incredibly easy taking advantage of TypeScript inference to automatically warn you of errors on your client before you even save the change on your server file! Using tRPC will feel like using an SDK for your API's server code, giving you confidence in your endpoints.&lt;/p&gt;

&lt;p&gt;On the server, you create a tRPC API by defining your procedures. Procedures are the composable functions used to build your backend. They're composable and can be queries, mutations, or subscriptions. Routers contain multiple procedures. Often, we use a the popular &lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; validator library to ensure the input from the client has exactly the shape that our procedure expects. Finally, we always export the type of our API's tRPC router so we can use it in our frontend code.&lt;/p&gt;

&lt;p&gt;In the SPARTAN repository I have already implemented tRPC adapters for both the client inside our Angular application and the Nitro server, which Analog provides to us by default. This makes using tRPC and Analog super easy! If you are interested in the underlying source code feel free to check out the &lt;code&gt;@spartan/trpc&lt;/code&gt; package, which is located in the &lt;code&gt;libs/trpc&lt;/code&gt; folder. For more information on tRPC I recommend checking out their amazing documentation site &lt;a href="https://trpc.io" rel="noopener noreferrer"&gt;trpc.io&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Prisma
&lt;/h3&gt;

&lt;p&gt;Now, our front-end code is typesafe, as is our server code, and our front-end and server interaction is both very smooth and typesafe. We're just a few steps away from having complete typesafety from our database to the template generating the DOM. Prisma is the tool that will bring us there.&lt;/p&gt;

&lt;p&gt;Prisma is a Node.js and TypeScript ORM. An ORM is an Object Relational Mapper, which is a piece of code that wraps the code required to manipulate the data, allowing you to avoid using SQL and instead interact directly with an object in the same language you're using. Hibernate is a popular ORM for Java and the Spring framework, .NET has the entity framework, and Typescript has Prisma. Prisma, however, goes beyond just a simple ORM. It has an intuitive data model, automated migrations (I can't emphasize how valuable this is), typesafety, and auto-completion!&lt;/p&gt;

&lt;p&gt;With Prisma, you include a definition of your database schema within your code repository.&lt;br&gt;
Prisma then creates a properly typed Client using this information. The Prisma client is query builder that is specific to your database schema and is aware of every one of your tables, their fields, and their relationships! With Prisma, we are literally typesafe from the database to the template.&lt;/p&gt;

&lt;p&gt;Additionally, it creates SQL migrations automatically using the Prisma schema, monitors the execution of migrations, and offers tools for identifying and resolving conflicts and drifts between migrations and the database schema. This completely changes the game as it enables your database and all of its types to evolve alongside your application code.&lt;/p&gt;

&lt;p&gt;We can add Prisma to our stack like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;prisma &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, we initialize the client with:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will create a &lt;code&gt;prisma&lt;/code&gt; folder in our Nx workspace's root directory. Inside there is a file called &lt;code&gt;schema.prisma&lt;/code&gt;. I added the following contents:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model note {
  id         BigInt    @id(map: "notes_pkey") @default(autoincrement())
  created_at DateTime? @default(now()) @db.Timestamptz(6)
  note       String?
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will tell Prisma to create a JavaScript/TypeScript client, connect to a Postgres SQL database using the &lt;code&gt;DATABASE_URL&lt;/code&gt; connection url with a schema that has a simple table called &lt;code&gt;note&lt;/code&gt; for our example. Of course, you will adjust the schema to fit your project.&lt;/p&gt;

&lt;p&gt;At this point our typesafe stack is ready for development. The only thing that is missing is persistent data storage, our PostgresSQL database.&lt;/p&gt;
&lt;h3&gt;
  
  
  Supabase
&lt;/h3&gt;

&lt;p&gt;PostgreSQL is one of the worlds most scalable databases. It is a sophisticated object-relational system applying SQL. Postgres allows you to securely store vast quantities of complex data. It enables developers to build the most sophisticated programs, carry out administrative operations, and establish integral environments. It is an open source technology trusted by millions of developers. &lt;/p&gt;

&lt;p&gt;It turns out that every Supabase project is powered by a dedicated PostgreSQL database. Supabase is a fantastic open source Firebase replacement that is powered by the aforementioned Postgres database. It also provides a lot more features as your project expands and becomes more involved. Supabase provides solutions for common needs like as authentication, instant APIs, edge functions, real-time subscriptions, and storage, making it a robust platform on which to develop your application.&lt;/p&gt;

&lt;p&gt;Even better, Supabase is open source at heart. You can look through the &lt;a href="https://github.com/supabase/supabase" rel="noopener noreferrer"&gt;source code.&lt;/a&gt; You may self-host it, utilize their free plan to get started, or commit to one of their premium subscriptions to receive a fully managed production quality environment that scales with your needs. &lt;/p&gt;

&lt;p&gt;Supabase just wrapped up their &lt;a href="https://supabase.com/launch-week" rel="noopener noreferrer"&gt;Launch Week 7&lt;/a&gt; with a ton of incredible features. This includes an AI assistant that was added to their premium platform. It is aware of your database design and can help you when creating more complex SQL queries. I'm even more thrilled by their most recent releases of outstanding open source projects, such as the PostgreSQL package manager &lt;a href="https://database.dev" rel="noopener noreferrer"&gt;database.dev.&lt;/a&gt; dbdev serves the same purpose for PostgreSQL that npm does for JavaScript. It gives your PostgresSQL database incredibly easy access to packages that give your DB superpowers like full-text search or one-time-only-read data access à la Snapchat.  The best thing is that dbdev can load any PostgreSQL instance that has the required fundamental extensions, independent of the Supabase platform. Again, the code is &lt;a href="https://github.com/supabase/dbdev" rel="noopener noreferrer"&gt;open source.&lt;/a&gt; I can't wait to see where this project goes in the future, and I have no doubt that we will soon be able to utilize a lot more fantastic libraries. Like Brandon Roberts puts it:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1645436665677193217-318" src="https://platform.twitter.com/embed/Tweet.html?id=1645436665677193217"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1645436665677193217-318');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1645436665677193217&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Let's complete this last step and finish our typesafe, fullstack SPARTAN stack:&lt;/p&gt;

&lt;p&gt;We will set up our persistent data storage and then use Prisma to sync our database schema to the Postgres database that our project will run on. There are two ways to get up and running with Supabase:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connecting directly to your managed instance on &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;supabase.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Locally using &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Option 1: Connecting to supabase.com instance
&lt;/h4&gt;

&lt;p&gt;This way is super easy! Simply by creating your account, you will also have set up your first project.&lt;br&gt;
This means that you are ready to connect to your projects database already!&lt;/p&gt;

&lt;p&gt;Let's connect our application to our Supabase Postgres instance:&lt;/p&gt;

&lt;p&gt;Add a  &lt;code&gt;.env&lt;/code&gt; file at the root of your Nx workspace and add the following code snippet&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-SUPABASE-REFERENCE-ID].supabase.co:5432/postgres?schema=public"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It's really this easy!! Prisma can now take over the DB management and make sure that our schema is migrated to our Supabase instance.&lt;/p&gt;
&lt;h4&gt;
  
  
  Option 2: Connecting to local supabase instance
&lt;/h4&gt;

&lt;p&gt;Supabase also allows you to run a version of their system locally!&lt;/p&gt;

&lt;p&gt;To get up and running you can follow &lt;a href="https://supabase.com/docs/guides/cli/local-development" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;! They do a great job explaining how to get started and there is plenty of resources to help you if you get stuck.&lt;/p&gt;

&lt;p&gt;If you want the quick and dirty way and are on a Mac. Here is what I did to get up and running:&lt;/p&gt;
&lt;h5&gt;
  
  
  Install supabase CLI
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;supabase/tap/supabase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  Log into CLI
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;supabase login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create your access token from &lt;a href="https://app.supabase.com/account/tokens" rel="noopener noreferrer"&gt;https://app.supabase.com/account/tokens&lt;/a&gt; and paste it into your terminal window.&lt;/p&gt;
&lt;h5&gt;
  
  
  Create Supabase project
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# if you are in the spartan directory move UP!!!&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="c"&gt;# create your project folder&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;spartan-supabase

&lt;span class="c"&gt;# move into the new folder&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;spartan-supabase

&lt;span class="c"&gt;# start a new git repository — important, don't skip this step&lt;/span&gt;
git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  Start Supabase services
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;supabase init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;supabase start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h6&gt;
  
  
  Important: Make sure Docker is running and configured correctly!
&lt;/h6&gt;

&lt;p&gt;I had Docker already installed and running. However, my setup is not compatible with the config Supabase expects by&lt;br&gt;
default.&lt;br&gt;
I ran the following command to get it to work for now:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unix:///Users/[YOUR_USER_ACCOUNT_NAME]/.docker/run/docker.sock supabase start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For more info see &lt;a href="https://github.com/supabase/cli/issues/167" rel="noopener noreferrer"&gt;this issue on GitHub.&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Connect to local DB
&lt;/h5&gt;

&lt;p&gt;The previous step can take a while as all the docker images have to be downloaded first.&lt;br&gt;
However, once everything completes you will see a console output that looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Started supabase local development setup.

         API URL: http://localhost:54321
          DB URL: postgresql://postgres:postgres@localhost:54322/postgres
      Studio URL: http://localhost:54323
    Inbucket URL: http://localhost:54324
        anon key: eyJh......
service_role key: eyJh......
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Take your cyber-security hat off for a minute (we are working locally after all) and copy the connection string:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgresql://postgres:postgres@localhost:54322/postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Add a &lt;code&gt;.env&lt;/code&gt; file at the root of your Nx workspace and add the connection string like so:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://postgres:postgres@localhost:54322/postgres?schema=public"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Perfect! You should be able to connect to your local Supabase Postgres instance now!&lt;/p&gt;
&lt;h4&gt;
  
  
  Initializing the DB
&lt;/h4&gt;

&lt;p&gt;Now that we have successfully set up our DB we need to set up our database schema.&lt;br&gt;
Primsa makes this super easy!!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can push the schema defined in our &lt;code&gt;schema.prisma&lt;/code&gt; file to our DB running
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn prisma db push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Finally, we create our prisma client by running
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it! Now our DB should be all set up and ready to go!&lt;/p&gt;
&lt;h2&gt;
  
  
  Bringing it all together
&lt;/h2&gt;

&lt;p&gt;While the instructions should get you pretty close to running your own SPARTAN application, the easiest way to get started with the full stack is to clone/fork the repository. It has all the little tweaks needed to get things running done already. You can start making changes right away:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/goetzrobin/spartan.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The files that will be most interesting to you are:&lt;br&gt;
&lt;code&gt;index.page.ts&lt;/code&gt; inside the analog-trpc application:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&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;injectTRPCClient&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;../../trpc-client&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;AsyncPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DatePipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JsonPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NgFor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NgIf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FormsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NgForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/forms&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;note&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;@prisma/client&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;waitFor&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;../../wait-for&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;inputTw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:outline-0 block w-full appearance-none rounded-lg px-3 py-2 transition-colors text-base leading-tight md:text-sm bg-black/[.05] dark:bg-zinc-50/10 focus:bg-white dark:focus:bg-dark placeholder:text-zinc-500 dark:placeholder:text-zinc-400 contrast-more:border contrast-more:border-current&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;btnTw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;focus-visible:ring-2 focus-visible:ring-zinc-50 focus-visible:outline-0 flex items-center justify-center rounded-lg px-2 py-1.5 text-sm font-bold tracking-tight shadow-xl shadow-red-500/20 bg-[#DD0031] hover:bg-opacity-70 text-zinc-800 hover:text-primary-darker&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;analog-trpc-home&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;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;AsyncPipe&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="nx"&gt;NgFor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DatePipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NgIf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JsonPipe&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="na"&gt;class&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 h-full p-4&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 class='justify-center flex mt-20 mb-8 items-center'&amp;gt;
      &amp;lt;h1 class='font-medium italic text-6xl text-[#DD0031] font-bold'&amp;gt;SPARTAN&amp;lt;/h1&amp;gt;
      &amp;lt;img class='ml-2 block w-32' alt='Spartan Logo' src='/assets/spartan.svg' /&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;form class='py-2 flex items-center' #f='ngForm' (ngSubmit)='addPost(f)'&amp;gt;
      &amp;lt;input required autocomplete='off' class='&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputTw&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' name='newTitle' [(ngModel)]='newTitle' /&amp;gt;
      &amp;lt;button class='ml-2 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;btnTw&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'&amp;gt;+
      &amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div class='mt-4'&amp;gt;
      &amp;lt;div class='mb-4 p-4 font-normal border border-zinc-500/40 rounded-md'
           *ngFor='let note of notes ?? []; trackBy: noteTrackBy'&amp;gt;
        &amp;lt;div class='flex items-center justify-between'&amp;gt;
          &amp;lt;p class='text-sm text-zinc-400'&amp;gt;{{note.created_at | date}}&amp;lt;/p&amp;gt;
          &amp;lt;button class='!text-xs h-6 !bg-opacity-10 hover:!bg-opacity-50 !text-zinc-50 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;btnTw&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'
                  (click)='removePost(note.id)'&amp;gt;x
          &amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;p class='mb-4'&amp;gt;{{ note.note }}&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;

      &amp;lt;div class='text-center rounded-xl p-20 bg-zinc-950/40' *ngIf='!loadingPosts &amp;amp;&amp;amp; notes.length === 0'&amp;gt;
        &amp;lt;h3 class='text-xl font-medium'&amp;gt;No notes yet!&amp;lt;/h3&amp;gt;
        &amp;lt;p class='text-zinc-400'&amp;gt;Add a new one and see them appear here...&amp;lt;/p&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&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;_trpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectTRPCClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;loadingPosts&lt;/span&gt; &lt;span class="o"&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;public&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;note&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;public&lt;/span&gt; &lt;span class="nx"&gt;newTitle&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="k"&gt;public&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;waitFor&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;_trpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&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="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notes&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;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;noteTrackBy&lt;/span&gt; &lt;span class="o"&gt;=&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;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;note&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;return&lt;/span&gt; &lt;span class="nx"&gt;note&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="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;addPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NgForm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markAllAsTouched&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;_trpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&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;newTitle&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchPosts&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;newTitle&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="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;removePost&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="nx"&gt;bigint&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;_trpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutate&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchPosts&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;fetchPosts&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;loadingPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_trpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&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="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notes&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;loadingPosts&lt;/span&gt; &lt;span class="o"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notes&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;It shows you how to query, update and delete records in our DB using the tRPC client.&lt;/p&gt;

&lt;p&gt;The client interacts with a trpc endpoint defined in the &lt;code&gt;[trpc].ts&lt;/code&gt; file inside the &lt;code&gt;server/routes/trpc&lt;/code&gt; folder of the same &lt;code&gt;analog-trpc&lt;/code&gt; application:&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;appRouter&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;../../trpc/routers&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;createContext&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;../../trpc/context&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;createTrpcNitroHandler&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;@spartan/trpc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// export API handler&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;createTrpcNitroHandler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createContext&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The appRouter comes from &lt;code&gt;server/trpc/routers/index.ts&lt;/code&gt; and consolidates all other routers of your application. In our case that is the &lt;code&gt;notesRouter&lt;/code&gt; that is in the notes file of the same folder. I suggest you should make changes to the source code of that file, e.g. remove the list procedure, and see how typescript immediately notifies you of the breaking change in the &lt;code&gt;index.page.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&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="nx"&gt;publicProcedure&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;../trpc&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;PrismaClient&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;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&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;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;noteRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;router&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;publicProcedure&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mutation&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;note&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;title&lt;/span&gt;
      &lt;span class="p"&gt;}})),&lt;/span&gt;
  &lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;publicProcedure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;publicProcedure&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;mutation&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&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="nx"&gt;input&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 is it! You now have the full power of fullstack typesafety right at your finger tips! This is SPARTAN!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/2BG086WOP2Xfi/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img alt="Spartan yelling this is SPARTAN" src="https://i.giphy.com/media/2BG086WOP2Xfi/giphy.gif" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Allowing NestJs as a backend provider is one of the most frequently requested features for Analog. Nest is a powerful framework for building efficient, scalable Node.js server-side applications. It provides an application architecture which allows us to create highly testable, scalable, loosely coupled, and easily maintainable applications. The architecture is heavily inspired by Angular, which in theory makes it a perfect fit for Analog and SPARTAN. &lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/analogjs/analog/issues/317" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Feature: Use NestJS integration as SSR/http server
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#317&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/brandonroberts" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F42211%3Fv%3D4" alt="brandonroberts avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/brandonroberts" rel="noopener noreferrer"&gt;brandonroberts&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/analogjs/analog/issues/317" rel="noopener noreferrer"&gt;&lt;time&gt;Apr 03, 2023&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Which scope/s are relevant/related to the feature request?&lt;/h3&gt;
&lt;p&gt;platform&lt;/p&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Information&lt;/h3&gt;
&lt;p&gt;NestJS is very popular Node.js framework for building APIs and is widely used in the Angular ecosystem. This feature would allow NestJS to be used as the server for the &lt;code&gt;/api&lt;/code&gt; routes, and provided integration as the backend for SSR.&lt;/p&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/vite-plugin-node" rel="nofollow noopener noreferrer"&gt;https://www.npmjs.com/package/vite-plugin-node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.nestjs.com/" rel="nofollow noopener noreferrer"&gt;https://docs.nestjs.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In development, NestJS would be configured under the &lt;code&gt;/api&lt;/code&gt; path. During the build it would host the Analog application and provide additional configured API routes.&lt;/p&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Describe any alternatives/workarounds you're currently using&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;No response&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;I would be willing to submit a PR to fix this issue&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Yes&lt;/li&gt;
&lt;li&gt;[ ] No&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/analogjs/analog/issues/317" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Unfortunately, tRPC and NestJs do not play well together by default, and the tRPC team does not intend to integrate the two through an official plugin &lt;a href="https://github.com/trpc/trpc/discussions/1504" rel="noopener noreferrer"&gt;for the time being.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, there is other tools out there that offer a similar type-safe experience built for REST-API's, which is where NestJs shines. Specifically, I am talking about &lt;a href="https://ts-rest.com/" rel="noopener noreferrer"&gt;ts-rest,&lt;/a&gt; which has an official NestJs integration. I am excited to explore those other options and see how SPARTAN can be the best possible starter for your enterprise full stack Angular projects.&lt;/p&gt;

&lt;p&gt;With signals landing in Angular 16, I also plan to make them an integral part of the SPARTAN stack. &lt;a class="mentioned-user" href="https://dev.to/timdeschryver"&gt;@timdeschryver&lt;/a&gt; has created an amazing form library called &lt;a href="https://github.com/timdeschryver/ng-signal-forms" rel="noopener noreferrer"&gt;ng-signal-forms&lt;/a&gt; that is completely driven by signals and already provides an absolutely amazing developer experience dealing with even the more complex scenarios we need to tackle in our real world applications. My goal is to integrate it into the current application to show how signals will transform Angular development in the future.&lt;/p&gt;

&lt;p&gt;Finally, there is an amazing repository called &lt;a href="https://github.com/shadcn/taxonomy" rel="noopener noreferrer"&gt;Taxonomy,&lt;/a&gt; which serves as a great resource to the NextJs community showing off how a real world application can be built with version 13 of the framework. In an ideal world, the SPARTAN repository could become such a beautifully crafted resource for the Angular community. That is the ambitious goal I have for SPARTAN and if this article got you excited about what is possible, I invite you to reach out with your ideas and even consider contributing yourself! &lt;/p&gt;

&lt;h2&gt;
  
  
  The future is bright
&lt;/h2&gt;

&lt;p&gt;With Analog, Angular finally has it's own meta-framework that enables us to built on top of all the amazing features that Angular provides, which gives us access to an amazing Vite ecosystem, and will enable all of us to build better, faster, and more amazing full stack Angular applications. &lt;/p&gt;

&lt;p&gt;Together with a powerful tools like TailwindCSS, tRPC, databases like Postgres, and infrastructure providers like Supabase, who are open source at heart, I couldn't be more excited for more and more people and companies to go all in on Analog (and maybe even the SPARTAN stack) in the future.&lt;/p&gt;

&lt;p&gt;As always, do you have any further questions or suggestions for blog posts? What do you think of this project? Is this something you could see yourself working with? I am curious to hear your thoughts. Please don't hesitate to leave a comment or send me a message.&lt;/p&gt;

&lt;p&gt;Finally, if you liked this article feel free to like and share it with others. If you enjoy my content follow me on &lt;a href="https://twitter.com/goetzrobin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/goetzrobin" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>analogjs</category>
      <category>angular</category>
      <category>trpc</category>
      <category>supabase</category>
    </item>
    <item>
      <title>Angular &amp; signals. Everything you need to know.</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Thu, 02 Mar 2023 21:47:10 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/angular-signals-everything-you-need-to-know-2b7g</link>
      <guid>https://forem.com/playfulprogramming-angular/angular-signals-everything-you-need-to-know-2b7g</guid>
      <description>&lt;p&gt;On February 15th, the Angular team opened the &lt;a href="https://github.com/angular/angular/pull/49091" rel="noopener noreferrer"&gt;first PR&lt;/a&gt; that introduces signals to the framework. This early PR has gained huge momentum and sent the Angular community in a frenzy. Everyone is talking about signals, everyone is posting about signals, everyone is prototyping with signals. &lt;/p&gt;

&lt;p&gt;The Angular team is doing an excellent job of pushing and explaining this new reactive primitive. They are making sure that developers have access to the tools they need to grasp this novel idea that will fundamentally alter how Angular apps function in the future. In their official &lt;a href="https://github.com/angular/angular/discussions/49090" rel="noopener noreferrer"&gt;Github conversation&lt;/a&gt;, they are diligently responding to questions and address community concerns. On twitter, they are providing the public with incredible bits of &lt;a href="https://twitter.com/pkozlowski_os/status/1628745655052079105?s=20" rel="noopener noreferrer"&gt;information.&lt;/a&gt; On live broadcasts, they engage in in-depth, &lt;a href="https://www.youtube.com/watch?v=HstDoVQeP9g" rel="noopener noreferrer"&gt;hour-long technical discussions&lt;/a&gt; with signal thought leaders. They are doing everything they can to share their enthusiasm for and knowledge about this amazing new addition to the framework.&lt;/p&gt;

&lt;p&gt;The only thing I have been missing so far is a compilation of all of this incredible information: One resource that digests all the materials available into a coherent story. A single article that teaches me what signals are, how they fit into the Angular story, what problems they address for the framework, and what it all means for me as an Angular developer ultimately using them to build my applications.&lt;/p&gt;

&lt;p&gt;This article is my attempt to create such a resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals. A short introduction
&lt;/h2&gt;

&lt;p&gt;So what are signals? A good starting point is to look at how Solid, a relatively new and increasingly popular framework that is built around signals describes them. Especially, the Angular team worked closely with its creator and CEO of Signals Ryan Carniato to develop Angular's version of them:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Signals are the cornerstone of reactivity in Solid. They contain values that change over time; when you change a signal's value, it automatically updates anything that uses it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That seems pretty straight forward. A signal is a wrapper around a simple value that registers what depends on that value and notifies those dependents whenever its value changes.&lt;/p&gt;

&lt;p&gt;A comparison often used to describe them to people familiar with RxJs are BehaviorSubjects, except for the need to manually subscribe/unsubscribe. &lt;/p&gt;

&lt;p&gt;Signals always have a value, signals are side effect free, and signals are reactive, keeping their dependents in sync.&lt;/p&gt;

&lt;p&gt;All together they deliver one simple model of how things change in Angular applications. Something that does not quite exist today. Let's take a look by what I mean by that.&lt;/p&gt;

&lt;h2&gt;
  
  
  It all started (about) 10 years ago
&lt;/h2&gt;

&lt;p&gt;Angular's journey towards signals started 10 years ago. This might seem like a weird statement. We are talking about signals. THE framework that inspired the Angular team SolidJs (v1.0.0) was not released until June 28th, 2021. How could Angular's journey towards signals have started 10 years ago?&lt;/p&gt;

&lt;p&gt;Well, that is about when v1.0.0 of Angular was released (13th of June 2012,) and with it an abundance of design choices. Design choices that each came with their own set of tradeoffs and (often significant) implications on developer and user experience.&lt;/p&gt;

&lt;p&gt;These design choices were made based on the then current technology and the information available at that time. After 10 years of advancement in browser technology (there were no arrow functions until ES6 was finalized in 2015, async functions were added with ES8 in 2017), and hundreds of thousands of real world applications running on Angular, the Angular team decided to &lt;a href="https://www.youtube.com/watch?v=IY-QOz4oLCE" rel="noopener noreferrer"&gt;review those design decisions&lt;/a&gt; and see how they played out. As always with technology (and life in general) it turns out that some decisions turned out better than others.&lt;/p&gt;

&lt;p&gt;Clearly, one of the best decisions was to build Angular on top of Typescript. What seems like an obvious choice now, was by no means one back in 2012. Typescript was just gaining popularity, many frontend developers never had to bother with type safety before and it seemed to unnecessarily slow things down. Angular became the first major framework to be built on top of Typescript. Today we know that type safety incredibly increases confidence and velocity in building applications at scale. But let's not get too sidetracked here.&lt;/p&gt;

&lt;p&gt;Another decision was that the view state can not only live but also be mutated anywhere in the application. It does not matter if it is a simple boolean value in your component or an item of a list nested deeply in an object of a global service. Angular will detect the changes and update the DOM accordingly.&lt;/p&gt;

&lt;p&gt;This gives developers incredible freedom to structure our applications in ways that make sense to us, and extract more complicated logic into services, all with minimal effort and concern about how our new data gets rendered in the DOM. No matter where we put and change our state, Angular's automatic global change detection will figure out the changes and the value will magically update in the DOM.&lt;/p&gt;

&lt;h3&gt;
  
  
  The challenges of automatic change detection
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ZoneJs and when to check for changes
&lt;/h4&gt;

&lt;p&gt;Unfortunately, in reality, it is not as easy as that. Those magic updates come at a price. &lt;/p&gt;

&lt;p&gt;To enable its automatic change detection Angular depends on ZoneJs. ZoneJs is a library that patches native browser APIs and notifies the framework whenever a significant event occurs. This leads us to the first trade-off. Before anything can happen in an Angular application ZoneJs needs to load and run, which means that Angular sort of comes with a built-in performance deficit compared to frameworks that take a different approach to synchronize model changes and the DOM.&lt;/p&gt;

&lt;p&gt;You might also be wondering what those significant events are that I mentioned above. The list includes event listeners, setTimeouts, Promises, and more. It turns out that to keep state that is mutable anywhere in sync with the DOM pretty much any browser event is significant. This means that often, even if we do not intend to even update the DOM, Angular will need to check the complete component tree and see if any of our data bindings' values are to be updated.&lt;/p&gt;

&lt;p&gt;So on the one side of the story, Angular tends to overcheck and do unnecessary work trying to detect changes to the model. On the other side, the algorithm that determines which bindings changed comes with significant implications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Unidirectional data flow or ExpressionChangedAfterItHasBeenCheckedError
&lt;/h4&gt;

&lt;p&gt;We learned that there is an abundance of events that trigger Angular's change detection mechanism. To ensure that applications stay performant even with a great number of components in the DOM, the underlying mechanism must be incredibly efficient. &lt;/p&gt;

&lt;p&gt;To ensure that, Angular's change detection runs in DOM order, and checks every binding between the model and the DOM only once. Again, after years and millions of lines of Angular it becomes clear that this way of checking for changes has its downsides. &lt;/p&gt;

&lt;p&gt;The most important is that you cannot update any of your parents' data after it has&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;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;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;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChildComponent&lt;/span&gt; &lt;span class="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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;changed&lt;/span&gt; &lt;span class="o"&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;private&lt;/span&gt; &lt;span class="nx"&gt;parent&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;ParentComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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;changed&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;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;from child&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;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;my-app&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;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;`
  {{text}}
    &amp;lt;app-child [changed]='true'/&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;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;from parent&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;This results in the famous ExpressionChangedAfterItHasBeenCheckedError:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ERROR
Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: &lt;span class="s1"&gt;'from parent'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Current value: &lt;span class="s1"&gt;'from child'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Find more at https://angular.io/errors/NG0100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-2u4dvf?file=src/main.ts" rel="noopener noreferrer"&gt;Check out the Stackblitz to see the code in action.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within a single change detection cycle we decided to go against the natural flow of the operation and update the parent after Angular has already checked its value. &lt;/p&gt;

&lt;p&gt;You might say that this example seems a little artificial, but there are plenty of examples where the logical data flow of an application does not fit into the top-to-bottom nature of Angular's change detection. Even within Angular itself, we find this exact problem. &lt;/p&gt;

&lt;p&gt;In the framework's FormsModule code, the validity of a parent is driven by the child. To avoid the ExpressionChangedAfterItHasBeenCheckedError when updating the parent's status. The operation had to be wrapped by an immediately resolved promise. This schedules a micro task with our update to the parents, which on completion triggers another change detection cycle, which can finally check the component tree in the allowed order and update the DOM.&lt;/p&gt;

&lt;p&gt;I hope you are still following me.&lt;/p&gt;

&lt;p&gt;Of course, there are techniques, like using a promise, that works around change detection's current limitations. However, they seem a little hacky and more importantly need a deep understanding of how change detection with ZoneJs works. In reality, Angular's global, automatic change detection does not always "just work."&lt;/p&gt;

&lt;h2&gt;
  
  
  OnPush, RxJs &amp;amp; why it doesn't solve all our (diamond) problems
&lt;/h2&gt;

&lt;p&gt;If you are not familiar with OnPush change detection and/or want a refresher on how RxJs is commonly used, please view this &lt;a href="https://www.youtube.com/watch?v=36G-ZFcllkk" rel="noopener noreferrer"&gt;super informative video&lt;/a&gt; by Joshua Morony that touches both on OnPush and RxJs and their importance for Angular development.&lt;/p&gt;

&lt;h3&gt;
  
  
  OnPush &amp;amp; async pipe. Performance improvement by addressing symptoms, not solving the root cause.
&lt;/h3&gt;

&lt;p&gt;As mentioned above, Angular triggers change detection many times and while this is not an issue for simple applications, performance becomes a much more important topic once the amount of components and data bindings in the DOM increases. &lt;/p&gt;

&lt;p&gt;One common way to improve performance in an Angular application is to use the OnPush change detection strategy and let Angular handle subscription management using the AsyncPipe.&lt;/p&gt;

&lt;p&gt;The OnPush change detection strategy excludes the component marked as OnPush and all its children from the default change detection mechanism. There is a &lt;a href="https://blog.angular-university.io/onpush-change-detection-how-it-works/" rel="noopener noreferrer"&gt;great article&lt;/a&gt; written by Angular University, which goes deeper into the mechanism. I highly recommend reading it. &lt;/p&gt;

&lt;p&gt;Again, OnPush changes the way Angular detects changes for the component marked as onPush &lt;strong&gt;AND&lt;/strong&gt; all its children.&lt;/p&gt;

&lt;p&gt;The docs clearly state that:&lt;br&gt;
&lt;em&gt;Use the CheckOnce strategy, meaning that automatic change detection is deactivated until reactivated by setting the strategy to Default (CheckAlways). Change detection can still be explicitly invoked. This strategy applies to all child directives and cannot be overridden.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The implications of this are huge. If one of your components high up in the tree is OnPush, all other components will have to support OnPush also. This means that UI library authors have to build their library OnPush compatible because if your components do not support OnPush change detection you cannot guarantee that everyone in the Angular ecosystem can use your components.&lt;/p&gt;
&lt;h3&gt;
  
  
  RxJs. Powerful, declarative, and reactive programming with asynchronous streams
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mike Pearson describes its incredible power perfectly in this tweet:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1618219357255106560-861" src="https://platform.twitter.com/embed/Tweet.html?id=1618219357255106560"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1618219357255106560-861');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1618219357255106560&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;RxJs allows us to compose declarative, reactive pipelines using Observables that clearly show in the code what series of actions and mutations will happen in an asynchronous flow. It avoids race conditions and ultimately makes your code more readable and easier to reason about.&lt;/p&gt;

&lt;p&gt;Angular introduced many of us to RxJs. It uses in many parts of the frameworks. Most famously in the HttpClient, which exposes the response of an HTTP-call through an Observable. Also, every Angular FormControl has a property called valueChanges. Which is &lt;em&gt;a multicasting observable that emits an event every time the value of the control changes, in the UI or programmatically.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Combined with OnPush and Angular's AsyncPipe, a utility that binds our Observable directly to the template, these reactive pipelines have become a major pattern to build performant, race-condition-free, declarative Angular applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Streams are not behaviors. Why RxJs is not the solution.
&lt;/h3&gt;

&lt;p&gt;However, if you take a closer look at how Angular uses Observables you notice a pattern. Angular uses RxJs for events, more specifically to expose streams of events. These event streams do not have a current value. Alex Rickabaugh gives a great way of thinking about this in &lt;a href="https://www.youtube.com/live/HstDoVQeP9g?feature=share&amp;amp;t=3905" rel="noopener noreferrer"&gt;their conversation with Ryan Carniato.&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;He is talking about click events specifically. &lt;/p&gt;

&lt;p&gt;You cannot ask what is the current click event. That question does not make sense. You might ask: What is the most recent click event? However, this is a fundamentally different question. Behaviors (and Angular's signals fall into this definition of behaviors) always have a value. You will always be able to ask: What is this behavior's (signal's) current value? That is the most fundamental shortcoming of RxJs streams (Observables) as a solution to the challenges the Angular team is trying to address with their new reactive primitive.&lt;/p&gt;

&lt;p&gt;In the same conversation, they do acknowledge that RxJs's BehaviorSubject is the closest thing the library offers to a signal. It always has a value. It can notify subscribers of changes to that value, and it exposes a way to get the current value and set a new value. However, it is not integrated into RxJs very well. As soon as pipe it through an operator (e.g. map()), it becomes an Observable and loses the connection that it was a BehaviorSubject that always has a current value. Also, RxJs has a plethora of powerful operators that can be used to map, join, debounce, etc. streams. For beginners, this power comes with a very steep learning curve. The Angular team needed something more focused to build their reactive primitive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Picking the right tool for your (diamond) problems
&lt;/h3&gt;

&lt;p&gt;Also, let's remind ourselves what the team set out to do in the first place. They want to introduce a reactive primitive that they can integrate with Angular's templating engine to notify the framework when the value bound to the view changes and needs to be updated in the DOM. &lt;/p&gt;

&lt;p&gt;One of the major goals of such a primitive is glitch-free execution. Glitch-free execution means never allowing user code to see an intermediate state where only some reactive elements have been updated (by the time you run a reactive element, every source should be updated.) &lt;br&gt;
Credit for this definition goes to Milo's &lt;a href="https://dev.to/modderme123/super-charging-fine-grained-reactive-performance-47ph"&gt;awesome article on fine-grained reactive performance.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, RxJs only offers a solution that feels hacky at best. Let's look at the example below:&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;normal&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;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;p&amp;gt;Hello from {{fullName$ | async}}!&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;{{fullNameCounter}}&amp;lt;/p&amp;gt;

    &amp;lt;button (click)="changeName()"&amp;gt;Change Name&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;NormalComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;firstName&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;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Peter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;lastName&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;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Parker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;fullNameCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;fullName$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combineLatest&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;firstName&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;lastName&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;tap&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;fullNameCounter&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&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;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;changeName&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;firstName&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Spider&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;lastName&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Man&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;ol&gt;
&lt;li&gt;We declare two BehaviorSubjects for &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;lastName&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We combine them into an observable &lt;code&gt;fullName$&lt;/code&gt; that simply concatenates the two.&lt;/li&gt;
&lt;li&gt;We declare a &lt;code&gt;fullNameCounter&lt;/code&gt; which we increment every time our &lt;code&gt;fullName$&lt;/code&gt; Observable emits.&lt;/li&gt;
&lt;li&gt;We add a &lt;code&gt;changeName&lt;/code&gt; function that we can trigger to set &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;lastName&lt;/code&gt; to a different value AT THE SAME TIME.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our component initially displays the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Peter Parker!

1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you click on the button the UI is updated to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Spider Man!

3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that our &lt;code&gt;fullName$&lt;/code&gt; observable actually emitted twice. One time for each change to &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;lastName&lt;/code&gt;. While it happened so fast that we could not see it, this component actually rendered a intermediate state just to be instantly replaced by the final, correct state.&lt;/p&gt;

&lt;p&gt;To avoid this and achieve our goal of glitch free execution, we need to add a &lt;code&gt;debounceTime&lt;/code&gt; to our &lt;code&gt;fullName$&lt;/code&gt; to avoid the duplicate execution and end up with glitch-free execution.&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;debounced&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;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;p&amp;gt;Hello from {{fullName$ | async}}!&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;{{fullNameCounter}}&amp;lt;/p&amp;gt;

    &amp;lt;button (click)="changeName()"&amp;gt;Change Name&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;DebouncedComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;firstName&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;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Peter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;lastName&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;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Parker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;fullNameCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;fullName$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combineLatest&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;firstName&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;lastName&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;debounceTime&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;tap&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;fullNameCounter&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&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;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;changeName&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;firstName&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Debounced Spider&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;lastName&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Man&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;Now we see the counter only increasing by one at a time, indicating that we indeed will not render an intermediate state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Peter Parker!

1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you click on the button the UI is updated to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Debounced Spider Man!

2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, I encourage you to check out &lt;a href="https://stackblitz.com/edit/angular-p9t4mr?file=src/main.ts" rel="noopener noreferrer"&gt;the working example.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An important take away is that &lt;code&gt;combineLatest&lt;/code&gt; emitting once for every change to one of the observables it combines would also would also apply if Angular decided to make &lt;code&gt;@Input()&lt;/code&gt;s observables. While this is of the most requested features of the community, you see that it comes with additional complexity.&lt;/p&gt;

&lt;p&gt;So what does the exact same functionality look like with a signal primitive? I am glad you asked.&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;my-app&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;{{ fullName() }}&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;{{signalCounter}}&amp;lt;/p&amp;gt;
    &amp;lt;button (click)="changeName()"&amp;gt;Increase&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;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Peter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Parker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;signalCounter&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="nx"&gt;fullName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;signalCounter&lt;/span&gt;&lt;span class="o"&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;signal name change&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="s2"&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;firstName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;changeName&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;firstName&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;Signal Spider&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;lastName&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;Man&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;Isn't the &lt;a href="https://stackblitz.com/edit/angular-p9t4mr?file=src/main.ts" rel="noopener noreferrer"&gt;signal version's code&lt;/a&gt; so much easier and so much more straight forward.&lt;/p&gt;

&lt;p&gt;Did you notice that even when you spam the changeName button, the count never goes over two?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Signal Spider Man!

2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will go into more detail on this later, but signals only notify consumers of their changes if their value changes. To get the same functionality in RxJs we would have to add yet another operator to our Observable: &lt;code&gt;distinctUnitlChanged&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is important to note that this does not mean that RxJs time is over. That it's on its way out of Angular and not useful anymore. RxJs shines by letting developers declare asynchronous, reactive streams. You can listen to an input's change events, debounce its values, switch them to parameters for an HTTP call, and map the response to the exact model that you need in the view, all in one place. This is simply not possible with signals.&lt;/p&gt;

&lt;p&gt;All this boils down to one thing: While both are reactive by nature, RxJs and signals solve different issues. They are complementing each other, they are not substitutes for each other. Together, they will allow for much more powerful and straightforward Angular applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/this-is-angular/i-changed-my-mind-angular-needs-a-reactive-primitive-n2g"&gt;This Article&lt;/a&gt; by Mike Pearson goes into much more detail about the short comings of RxJs as a reactive primitive for Angular. It helped me immensely to understand why exactly RxJs &amp;amp; Observable inputs are not the solution Angular needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  A new reactive primitive: signals
&lt;/h2&gt;

&lt;p&gt;Now that we understand the issues the Angular team identified, and clarified why RxJs is not the solution to those challenges, we can move on and introduce the star of today's article: signals.&lt;/p&gt;

&lt;p&gt;Let's revisit Solid's definition of a signal that we quickly introduced at the beginning of this article: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Signals are the cornerstone of reactivity in Solid. They contain values that change over time; when you change a signal's value, it automatically updates anything that uses it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We will take a closer look at how signals achieve this later, but let's first focus on the API currently provided by the Angular team. Most of this is copied directly from the README provided &lt;a href="https://github.com/angular/angular/tree/main/packages/core/src/signals#settable-signals-signal" rel="noopener noreferrer"&gt;here.&lt;/a&gt; It is an excellent resource and definitely worth the time spent reading it 1,2,3 times:&lt;/p&gt;

&lt;p&gt;Angular Signals are zero-argument functions (&lt;code&gt;() =&amp;gt; T&lt;/code&gt;). When executed, they return the current value of the signal. Executing signals does not trigger side effects, though it may lazily recompute intermediate values (lazy memoization).&lt;/p&gt;

&lt;p&gt;Particular contexts (such as template expressions) can be reactive. In such contexts, executing a signal will return the value, but also register the signal as a dependency of the context in question. The context's owner will then be notified if any of its signal dependencies produces a new value (usually, this results in the re-execution of those expressions to consume the new values).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: This is what makes signals so powerful as a mechanism of change detection and DOM synchronization. The affected template is directly notified. No walking of component trees and guessing when to re-check everything is necessary!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This context and getter function mechanism allows for signal dependencies of a context to be tracked automatically and implicitly. Users do not need to declare arrays of dependencies, nor does the set of dependencies of a particular context need to remain static across executions.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Compare this to &lt;code&gt;combineLatest&lt;/code&gt; and having to add each observable to the dependency array before being able to access their values later.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Settable signals: signal()
&lt;/h3&gt;

&lt;p&gt;The signal() function produces a specific type of signal known as a SettableSignal. In addition to being a getter function, SettableSignals have an additional API for changing the value of the signal (along with notifying any dependents of the change). These include the .set operation for replacing the signal value, .update for deriving a new value, and .mutate for performing internal mutation of the current value. These are exposed as functions on the signal getter itself.&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;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signal value can be also updated in-place, using the dedicated .mutate method:&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;todoList&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;Todo&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;todoList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutate&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="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&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;One more task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;completed&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: We do not need immutability for signals to correctly notify their dependents of changes!!&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Equality
&lt;/h4&gt;

&lt;p&gt;The signal creation function one can, optionally, specify an equality comparator function. The comparator is used to decide whether the new supplied value is the same, or different, as compared to the current signal’s value.&lt;/p&gt;

&lt;p&gt;If the equality function determines that 2 values are equal it will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;block update of signal’s value;&lt;/li&gt;
&lt;li&gt;skip change propagation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Declarative derived values: computed()
&lt;/h3&gt;

&lt;p&gt;computed() creates a memoizing signal, which calculates its value from the values of some number of input signals.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c1"&gt;// Automatically updates when `counter` changes:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isEven&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the calculation function used to create the computed is executed in a reactive context, any signals read by that calculation will be tracked as dependencies, and the value of the computed signal recalculated whenever any of those dependencies changes.&lt;/p&gt;

&lt;p&gt;Similarly to signals, the computed can (optionally) specify an equality comparator function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Side effects: effect()
&lt;/h3&gt;

&lt;p&gt;effect() schedules and runs a side-effectful function inside a reactive context. Signal dependencies of this function are captured, and the side effect is re-executed whenever any of its dependencies produces a new value.&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;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;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="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;The counter is:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="c1"&gt;// The counter is: 0&lt;/span&gt;

&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// The counter is: 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Effects do not execute synchronously with the set (see the section on glitch-free execution below), but are scheduled and resolved by the framework. The exact timing of effects is unspecified.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: This might sound scary at first. As developers, unspecified behavior is always the enemy. However, in this case, this gives Angular the freedom to decide when exactly an effect is executed and it can use this to optimize performance for example.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Non reactive: untracked()
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Note: This is currently not part of the official README. I do want to add this here to give you a complete overview of the API.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This prevents the wrapping computation from tracking any reads of the untracked signal. This means that even if the signal changes, the context is not notified of its change.&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;counter0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter1&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;// Executes when `counter0` changes, not when `counter1` changes:&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;counter0&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;untracked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;counter0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// logs 1 0&lt;/span&gt;
&lt;span class="nx"&gt;counter1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// does not log&lt;/span&gt;
&lt;span class="nx"&gt;counter1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// does not log&lt;/span&gt;
&lt;span class="nx"&gt;counter1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// does not log&lt;/span&gt;
&lt;span class="nx"&gt;counter0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// logs 2 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, whenever the context is executed it will use the latest value of the untracked signal.&lt;/p&gt;

&lt;p&gt;This is the current version of the API. Again, I want to encourage you to take the time to read the &lt;a href="https://github.com/angular/angular/tree/main/packages/core/src/signals" rel="noopener noreferrer"&gt;README&lt;/a&gt; on Github. It is absolutely phenomenal and an invaluable resource for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do signals work?
&lt;/h2&gt;

&lt;p&gt;Again, I will mostly quote the awesome explanations of the README.&lt;/p&gt;

&lt;h3&gt;
  
  
  Producers and Consumers
&lt;/h3&gt;

&lt;p&gt;Signals internally depend on two abstractions, &lt;code&gt;Producer&lt;/code&gt; and &lt;code&gt;Consumer&lt;/code&gt;. They are interfaces implemented by various parts of the reactivity system. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Producer&lt;/code&gt; represents values which can deliver change notifications, such as the various flavors of Signals.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Consumer&lt;/code&gt; represents a reactive context which may depend on some number of Producers. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, Producers produce reactivity, and Consumers consume it.&lt;/p&gt;

&lt;p&gt;Some concepts are both Producers and Consumers. For example, derived &lt;code&gt;computed&lt;/code&gt; expressions consume other signals to produce new reactive values.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;Producer&lt;/code&gt; and &lt;code&gt;Consumer&lt;/code&gt; keep track of dependency &lt;code&gt;Edge&lt;/code&gt;s to each other. &lt;code&gt;Producer&lt;/code&gt;s are aware of which &lt;code&gt;Consumer&lt;/code&gt;s depend on their value, while &lt;code&gt;Consumer&lt;/code&gt;s are aware of all of the &lt;code&gt;Producer&lt;/code&gt;s on which they depend. These references are always bidirectional.&lt;/p&gt;

&lt;p&gt;Together, they build a dependency graph that clearly lays out how the different nodes are related to each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  How changes propagate through the dependency graph
&lt;/h3&gt;

&lt;p&gt;We have already seen how RxJs Observables struggle to provide us with glitch free execution. We have also seen that signals elegantly solve this challenge. &lt;/p&gt;

&lt;p&gt;Let's explore how they do that:&lt;/p&gt;

&lt;h4&gt;
  
  
  Push/Pull Algorithm
&lt;/h4&gt;

&lt;p&gt;Angular Signals guarantees glitch-free execution by separating updates to the &lt;code&gt;Producer&lt;/code&gt;/&lt;code&gt;Consumer&lt;/code&gt; graph into two phases:&lt;/p&gt;

&lt;p&gt;The first phase is performed eagerly when a Producer value is changed. This change notification is propagated through the graph, notifying &lt;code&gt;Consumer&lt;/code&gt;s which depend on the &lt;code&gt;Producer&lt;/code&gt; of the potential update.&lt;/p&gt;

&lt;p&gt;Crucially, during this first phase, no side effects are run, and no recomputation of intermediate or derived values is performed, only invalidation of cached values.&lt;/p&gt;

&lt;p&gt;Once this change propagation has completed (synchronously), the second phase can begin. In this second phase, signal values may be read by the application or framework, triggering recomputation of any needed derived values which were previously invalidated.&lt;/p&gt;

&lt;p&gt;We refer to this as the "push/pull" algorithm: "dirtiness" is eagerly pushed through the graph when a source signal is changed, but recalculation is performed lazily, only when values are pulled by reading their signals.&lt;/p&gt;

&lt;h4&gt;
  
  
  valueVersioning
&lt;/h4&gt;

&lt;p&gt;This is probably the most complicated part of signals. First, we will take a look at the explanation provided by the Angular team. Then, we will take a look at an example.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Producer&lt;/code&gt;s track a monotonically increasing valueVersion, representing the semantic identity of their value. The valueVersion is incremented when the &lt;code&gt;Producer&lt;/code&gt; produces a semantically new value. The current valueVersion is saved into the dependency &lt;code&gt;Edge&lt;/code&gt; structure when a &lt;code&gt;Consumer&lt;/code&gt; reads from the &lt;code&gt;Producer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before Consumers trigger their reactive operations (e.g. the side effect function for effects, or the recomputation for computeds), they poll their dependencies and ask for valueVersion to be refreshed if needed. For a computed, this will trigger recomputation of the value and the subsequent equality check, if the value is stale (which makes this polling a recursive process as the computed is also a &lt;code&gt;Consumer&lt;/code&gt; which will poll its own &lt;code&gt;Producer&lt;/code&gt;s). If this recomputation produces a semantically changed value, valueVersion is incremented.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Consumer&lt;/code&gt; can then compare the valueVersion of the new value with the one cached in its dependency Edge, to determine if that particular dependency really did change. By doing this for all &lt;code&gt;Producer&lt;/code&gt;s, the &lt;code&gt;Consumer&lt;/code&gt; can determine that, if all valueVersions match, that no actual change to any dependency has occurred, and it can skip reacting to that change (e.g. skip running the side effect function).&lt;/p&gt;

&lt;p&gt;Let's look at an example to better understand what is going on.&lt;/p&gt;

&lt;p&gt;Let's assume we have the following code:&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;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isEven&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isEven&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;even!&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;odd!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// logs odd!&lt;/span&gt;
&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// this is the change we are going to look at&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%2Fuuxm28o2n1y3pi8gfzce.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%2Fuuxm28o2n1y3pi8gfzce.jpg" alt="Push/Pull algorithm for setting signal to 2" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The change to our SettableSignal sets off our push/pull algorithm.&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;Producer&lt;/code&gt; &lt;code&gt;counter&lt;/code&gt; pushes down its dirtiness and notifies its consumer &lt;code&gt;isEven&lt;/code&gt; that its value is stale.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isEven&lt;/code&gt; is also a &lt;code&gt;Producer&lt;/code&gt;, which means that it also notifies its &lt;code&gt;Consumer&lt;/code&gt;s. In this case our console.log &lt;code&gt;effect&lt;/code&gt;. This completes the push phase. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;effect&lt;/code&gt; now polls for &lt;code&gt;isEven&lt;/code&gt;'s current value.
5.&lt;code&gt;isEven&lt;/code&gt; again polls for the newest version of &lt;code&gt;counter&lt;/code&gt;.
6.&lt;code&gt;counter&lt;/code&gt; has updated its value and valueVersion notifying &lt;code&gt;isEven&lt;/code&gt; of its new state.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isEven&lt;/code&gt; recomputes its own value, determines that its value changed, and increments its value version&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;effect&lt;/code&gt; recognizes the new version value. Pulls the new value that changed to true, executes with the new value, and logs 'even!' to the console.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's look at what happens when we set the counter value to 4.&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;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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%2Fm9g1tfjt0cbjs7d4f6t6.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%2Fm9g1tfjt0cbjs7d4f6t6.jpg" alt="Push/Pull algorithm for setting signal to 4" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Again, the change to our SettableSignal to 4 sets off our push/pull algorithm.&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;Producer&lt;/code&gt; &lt;code&gt;counter&lt;/code&gt; pushes down its dirtiness and notifies its consumer &lt;code&gt;isEven&lt;/code&gt; that its value is stale.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isEven&lt;/code&gt; is also a &lt;code&gt;Producer&lt;/code&gt;, which means that it also notifies its &lt;code&gt;Consumer&lt;/code&gt;s. In this case our console.log &lt;code&gt;effect&lt;/code&gt;. This completes the push phase. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;effect&lt;/code&gt; now polls for &lt;code&gt;isEven&lt;/code&gt;'s current value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isEven&lt;/code&gt; again polls for the newest version of &lt;code&gt;counter&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;counter&lt;/code&gt; has updated its value to 4 and its valueVersion 3 notifying &lt;code&gt;isEven&lt;/code&gt; of its new state.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isEven&lt;/code&gt; recomputes its own value, determines in fact its value did &lt;strong&gt;not&lt;/strong&gt; change. Therefore, it keeps its value version the same.&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;effect&lt;/code&gt; recognizes that &lt;code&gt;isEven&lt;/code&gt;'s valueVersion did not change. And it does not need to execute.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the eager pushing of dirtiness down the dependency graph and the lazy pulling of values &lt;code&gt;Consumer&lt;/code&gt; depend on signals to achieve exactly what we want: glitch-free execution.&lt;/p&gt;

&lt;p&gt;A similar mechanism is used to track if the dependencies of a Producer went out of scope and can be garbage collected.&lt;br&gt;
This means that you will never have to worry about unsubscribing from a signal. Memory leaks can simply not occur due to this way of dependency tracking!&lt;/p&gt;
&lt;h2&gt;
  
  
  Fine grained reactivity = fine grained change detection = performance explosion
&lt;/h2&gt;

&lt;p&gt;With its own reactive primitive Angular has first class integration and can leverage all the benefits this new reactive way of doing things brings for change detection.&lt;/p&gt;

&lt;p&gt;I think of rendering the template as an &lt;code&gt;effect&lt;/code&gt;. Almost 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doubleCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;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="nf"&gt;renderTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;div&amp;gt;My counter is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;My double counter is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;doubleCounter&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We render the template in an effect, which becomes a consumer of each signal used in the template. These signals used will notify us when they change and we will know exactly when to re-render our template.&lt;/p&gt;

&lt;p&gt;To better understand how incredibly efficient fine-grained reactivity and signals are when it comes to keeping the DOM and model in sync becomes clear when we compare the current change detection mechanism and the new signal-enabled mechanism.&lt;/p&gt;

&lt;p&gt;Before we start. This is what every symbol means in the following diagrams&lt;br&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%2Fevt3ovzq2br9y8om0ru8.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%2Fevt3ovzq2br9y8om0ru8.jpg" alt="Symbols explained" width="512" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How Angular currently detects changes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdjqdu6jnb60w26w2g8d.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%2Fkdjqdu6jnb60w26w2g8d.jpg" alt="Current Angular change detection mechanism" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assume we have an application that has multiple components. Some of them have models that logically depend on each other. Others do not. Their views are all connected through parent/child relationships within the DOM tree. &lt;/p&gt;

&lt;p&gt;Angular creates a similar tree with its top-down dirty checking change detection algorithm. Regardless of how the data of components depend on each other, the framework creates parent/child relationships between the different components.&lt;/p&gt;

&lt;p&gt;Then, for every change detection cycle, it walks down the tree one time and compares the bindings' old value with the new value of the model. Every single binding will be checked exactly one time.&lt;/p&gt;

&lt;p&gt;We see that a model has changed inside our component tree, symbolized by the orange circle. The model itself has no logical dependency on any other component. As we enter a new change detection cycle the following happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular starts at the top of the tree and begins to check that node&lt;/li&gt;
&lt;li&gt;It then continues to walk along the tree to determine which components need to be updated&lt;/li&gt;
&lt;li&gt;Finally, it reaches the component in which the model has changed.&lt;/li&gt;
&lt;li&gt;The equality check fails and the DOM is updated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps happen every time change detection is triggered.&lt;/p&gt;

&lt;p&gt;Let's see how signal-based change detection works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal powered change detection
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwfj5oufsnwljqp9u02.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%2Fifwfj5oufsnwljqp9u02.jpg" alt="Signal based change detection" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have the same application with multiple components. Some models logically depend on each other, while others do not. Their views are all connected through parent/child relationships within the DOM tree.&lt;/p&gt;

&lt;p&gt;With signals, there is no need to create a tree that enables change detection. The signals used in the template notify it of their changes. That's why in this abstraction the change detection arrow is directly connected to the DOM node.&lt;/p&gt;

&lt;p&gt;So what happens when the model changes?&lt;/p&gt;

&lt;p&gt;The template is notified of this change and the DOM updates.&lt;/p&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mind blown
&lt;/h3&gt;

&lt;p&gt;No more top-down walking of a graph.&lt;/p&gt;

&lt;p&gt;No more unnecessary comparisons. &lt;/p&gt;

&lt;p&gt;The notification mechanism that informs the framework when to update the view is built into signals.&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%2Fyydagvlyjv8xiqjiow07.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%2Fyydagvlyjv8xiqjiow07.gif" alt="Mind blown!" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One can only imagine the performance improvements that come from this fine grained reactivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  signals + RxJs = &amp;lt;3
&lt;/h2&gt;

&lt;p&gt;I hope at this point you are just as excited for signals as I am!&lt;/p&gt;

&lt;p&gt;I want to end this article by reiterating that signals and RxJs complement each other.&lt;/p&gt;

&lt;p&gt;Many of the applications built with OnPush, RxJs, and the async pipe are already set up to take complete advantage of the performance increase provided by signals. The async pipe will be replaced by a signal. OnPush will completely disappear as signals notify the framework whenever a DOM update is necessary. RxJs will shine by focusing on what it does best: Modeling complex, asynchronous streams in a declarative way.&lt;/p&gt;

&lt;p&gt;Together they will power the Angular applications of the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready. Set. Go.
&lt;/h2&gt;

&lt;p&gt;It truly is an exciting time in the Angular community. I am 1000% convinced that signals will significantly improve developer and user experience of Angular applications. They provide one simple model to update Angular's views. They are performant, reactive, and will soon be an indispensable part of Angular.&lt;/p&gt;

&lt;p&gt;I hope you are now equipped with the knowledge to take full advantage of signals when they land later this year.&lt;/p&gt;

&lt;p&gt;As always, do you have any further questions or suggestions for blog posts? Are you excited about signals or do you still see issues the team needs to address? I am curious to hear your thoughts. Please don't hesitate to leave a comment or send me a message.&lt;/p&gt;

&lt;p&gt;Finally, if you liked this article feel free to like and share it with others. If you enjoy my content follow me on &lt;a href="https://twitter.com/goetzrobin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/goetzrobin" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>signals</category>
      <category>typescript</category>
      <category>rxjs</category>
    </item>
    <item>
      <title>Dark mode with Analog &amp; Tailwind</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Fri, 17 Feb 2023 15:58:03 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/dark-mode-with-analog-tailwind-4049</link>
      <guid>https://forem.com/playfulprogramming-angular/dark-mode-with-analog-tailwind-4049</guid>
      <description>&lt;p&gt;I entirely rebuilt my personal website with Analog and Tailwind a few weeks ago. I tweeted about it and received lot of positive feedback.&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1624087896088096770-652" src="https://platform.twitter.com/embed/Tweet.html?id=1624087896088096770"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1624087896088096770-652');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1624087896088096770&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;One of the questions I received was about how I implemented the dark mode experience. Specifically, how does the website recognize the user's preferred color scheme while also allowing her to manually adjust the theme and remember her selection for the next time you visit the page?&lt;/p&gt;

&lt;p&gt;I decided to publish a brief post outlining the approach I used so that others may implement a comparable user experience. In this post, we will build the following dark mode mechanism:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We identify the color scheme picked by the user and change our theme accordingly.&lt;/li&gt;
&lt;li&gt;A button will allow the user to change the current theme and toggle between dark and light mode.&lt;/li&gt;
&lt;li&gt;The current theme should be saved (I'll do this with localStorage), so that the right theme is used the next time the user accesses the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Note
&lt;/h4&gt;

&lt;p&gt;Analog, Vite, and Tailwind are used in this tutorial. When building dark mode for standard Angular projects that use Webpack, the same concepts apply. Tailwind makes enabling dark mode a breeze. Again, the same functionality may be implemented with normal CSS or style processors such as SCSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AnalogJS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://analogjs.org" rel="noopener noreferrer"&gt;Analog&lt;/a&gt; is a full-stack meta-framework for building applications and websites with Angular. It is similar to other meta-frameworks such as Next.JS, Nuxt, or SvelteKit but built on top of Angular. It's features include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vite/Vitest/Playwright&lt;/li&gt;
&lt;li&gt;File-based routing&lt;/li&gt;
&lt;li&gt;Support for API/server routes&lt;/li&gt;
&lt;li&gt;Hybrid SSR/SSG support&lt;/li&gt;
&lt;li&gt;Supports Angular CLI/Nx workspaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with Analog
&lt;/h2&gt;

&lt;p&gt;The easiest way to get started is to use the &lt;a href="https://stackblitz.com/github/analogjs/analog/tree/main/packages/create-analog/template-angular-v15?file=vite.config.ts&amp;amp;preset=node" rel="noopener noreferrer"&gt;Open Stackblitz&lt;/a&gt; button on the &lt;a href="https://analogjs.org" rel="noopener noreferrer"&gt;analogjs.org&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;If you want to develop locally you can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create analog@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will scaffold a basic Analog application. Once all dependencies are installed you can start your development server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have a vanilla Analog project up and running we can start implementing the dark mode functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Tailwind to Analog
&lt;/h2&gt;

&lt;p&gt;The great news is that Analog &amp;amp; Vite support PostCSS out of the box. So we can mostly just follow Tailwind's &lt;a href="https://tailwindcss.com/docs/installation/using-postcss" rel="noopener noreferrer"&gt;Using PostCSS&lt;/a&gt; installation guide.&lt;/p&gt;

&lt;p&gt;Fist we install our dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss postcss autoprefixer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in our root directory we create two files:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. postcss.config.js
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tailwindcss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;autoprefixer&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 file tells Vite to enable PostCSS and run the tailwindcss and autoprefixer plugins.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. tailwind.config.js
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('tailwindcss').Config} */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.{html,ts}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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;plugins&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 file ensures that all Tailwind classes in the source folder are picked up.&lt;br&gt;
Two small changes to this file compared to the Tailwind guide:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For the &lt;code&gt;content&lt;/code&gt; property we change the file ending to &lt;code&gt;ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We add the &lt;code&gt;darkMode&lt;/code&gt; property and set its value to &lt;code&gt;class&lt;/code&gt;.
These changes ensure that our Angular files are picked up and allow us to manually toggle Tailwind's dark mode classes by adding or removing the dark class from the &lt;code&gt;html&lt;/code&gt; element.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  3. Add default styles to styles.css
&lt;/h4&gt;

&lt;p&gt;Finally, we need to add the Tailwind directives to our main css file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/*
  Allow percentage-based heights in the application
*/&lt;/span&gt;
&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;body&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;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/*
  Remove built-in form typography styles
*/&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;textarea&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/*
  Avoid text overflows
*/&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h6&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;break-word&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/*
  Create a root stacking context
*/&lt;/span&gt;
&lt;span class="nt"&gt;app-root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;isolation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;isolate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you noticed, I also added some other awesome css resets inspired by a great Jon Comeau &lt;a href="https://www.joshwcomeau.com/css/custom-css-reset/" rel="noopener noreferrer"&gt;blog post.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding initialization script to index.html
&lt;/h2&gt;

&lt;p&gt;After setting up Tailwind, we can move on to the initial stage of incorporating dark mode onto our page.&lt;/p&gt;

&lt;p&gt;In order to get started, we first add a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element to our index.html file. This blocking script makes sure that the appropriate theme value is saved in localStorage and that the &lt;code&gt;dark&lt;/code&gt; class is immediately applied to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element as soon as the user loads the page. &lt;/p&gt;

&lt;p&gt;All this occurs before our Angular application takes control and, more importantly, before any content is painted. This allows the browser to immediately apply the appropriate dark Tailwind classes when painting our user interface.&lt;/p&gt;

&lt;p&gt;For more information, check out &lt;a href="https://www.joshwcomeau.com/react/dark-mode/#a-workable-solution-5" rel="noopener noreferrer"&gt;this part&lt;/a&gt; of another outstanding Jon Comeau blog post that describes this technique in detail.&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;MyApp&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/x-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/src/favicon.ico"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/src/styles.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// check if user had saved dark as their &lt;/span&gt;
        &lt;span class="c1"&gt;// theme when accessing page before&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&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="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;||&lt;/span&gt;
        &lt;span class="c1"&gt;// or user's requesting dark color &lt;/span&gt;
        &lt;span class="c1"&gt;// scheme through operating system&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;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&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;// then if we have access to the document and the element&lt;/span&gt;
        &lt;span class="c1"&gt;// we add the dark class to the html element and&lt;/span&gt;
        &lt;span class="c1"&gt;// store the dark value in the localStorage&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;)&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// else if we have access to the document and the element&lt;/span&gt;
        &lt;span class="c1"&gt;// we remove the dark class to the html element and &lt;/span&gt;
        &lt;span class="c1"&gt;// store the value light in the localStorage&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;)&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="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="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="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;app-root&amp;gt;&amp;lt;/app-root&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/src/main.ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take a closer look at what is happening inside our script tag:&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// check if user had saved dark as their &lt;/span&gt;
        &lt;span class="c1"&gt;// theme when accessing page before&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&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="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;||&lt;/span&gt;
        &lt;span class="c1"&gt;// or user's requesting dark color &lt;/span&gt;
        &lt;span class="c1"&gt;// scheme through operating system&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;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&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;// then if we have access to the document and the element&lt;/span&gt;
        &lt;span class="c1"&gt;// we add the dark class to the html element and&lt;/span&gt;
        &lt;span class="c1"&gt;// store the dark value in the localStorage&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;)&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// else if we have access to the document and the element&lt;/span&gt;
        &lt;span class="c1"&gt;// we remove the dark class to the html element and &lt;/span&gt;
        &lt;span class="c1"&gt;// store the value light in the localStorage&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;)&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;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="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="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="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;We check if there is already a &lt;code&gt;dark&lt;/code&gt; value set to the &lt;code&gt;theme&lt;/code&gt; property of the user's localStorage OR if the user is requesting a dark theme using the operating system.&lt;/li&gt;
&lt;li&gt;If that is the case and we have access to the document and its &lt;code&gt;html&lt;/code&gt; element, we &lt;strong&gt;add&lt;/strong&gt; the &lt;code&gt;dark&lt;/code&gt; class to that element and store the &lt;code&gt;dark&lt;/code&gt; value for our &lt;code&gt;theme&lt;/code&gt; in the localStorage.&lt;/li&gt;
&lt;li&gt;If that is NOT the case we and we have access to the document and its &lt;code&gt;html&lt;/code&gt; element, we &lt;strong&gt;remove&lt;/strong&gt; the &lt;code&gt;dark&lt;/code&gt; class from that element and store the &lt;code&gt;light&lt;/code&gt; value for our &lt;code&gt;theme&lt;/code&gt; in the localStorage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the source code for this file &lt;a href="https://stackblitz.com/edit/github-ojlugz?file=index.html" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point we have a page that supports Tailwind and initially renders the correct color scheme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the ThemeService
&lt;/h2&gt;

&lt;p&gt;Let's move on and allow our visitors to manually toggle their preferred theme.&lt;br&gt;
To do this we will create a singleton Angular service that will do 5 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It will sync with localStorage when the application first loads.&lt;/li&gt;
&lt;li&gt;It will keep track of theme changes in memory.&lt;/li&gt;
&lt;li&gt;It will exposes a method to toggle the theme and write those theme changes back to localStorage.&lt;/li&gt;
&lt;li&gt;It will add/remove the &lt;code&gt;dark&lt;/code&gt; class from the &lt;code&gt;html&lt;/code&gt; element as necessary.&lt;/li&gt;
&lt;li&gt;It will expose the current theme as an Observable for other parts of the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's create a new Angular service in the following location: &lt;code&gt;/src/libs/theme/theme.service.ts&lt;/code&gt;. We add the following code:&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="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;ThemeService&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnDestroy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// A. Setting up our dependencies&lt;/span&gt;
  &lt;span class="c1"&gt;// A.1 since we will access localStorage with AnalogJS&lt;/span&gt;
  &lt;span class="c1"&gt;// (which can be used for server side rendering)&lt;/span&gt;
  &lt;span class="c1"&gt;// we will use the PLATFORM_ID to see if we are executing in the browser and&lt;/span&gt;
  &lt;span class="c1"&gt;// it is available&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_platformId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLATFORM_ID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// A.2 we use Angular's renderer to add/remove the dark class from the html element&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;RendererFactory2&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="c1"&gt;// A.3 we use Angular's DOCUMENT injection token to avoid directly accessing the document object&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&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="c1"&gt;// B. Initializing our in memory theme store&lt;/span&gt;
  &lt;span class="c1"&gt;// B.1 we want to give every subscriber the current value of our theme&lt;/span&gt;
  &lt;span class="c1"&gt;// even if they subscribe after the first value was emitted&lt;/span&gt;
  &lt;span class="k"&gt;private&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;new&lt;/span&gt; &lt;span class="nx"&gt;ReplaySubject&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// B.2 we expose the current theme so our app can access it and e.g. show&lt;/span&gt;
  &lt;span class="c1"&gt;// a different icon for the button to toggle it&lt;/span&gt;
  &lt;span class="k"&gt;public&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="nx"&gt;_theme$&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="c1"&gt;// B.3 this emits when the service is destroyed and used to clean up subscriptions&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_destroyed$&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="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// C. Sync and listen to theme changes on service creation&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;// we check the current value in the localStorage to see what theme was set&lt;/span&gt;
    &lt;span class="c1"&gt;// by the code in the index.html file and load that into our _theme$ replaysubject&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;syncThemeFromLocalStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// we also immediately subscribe to our theme$ variable and add/remove&lt;/span&gt;
    &lt;span class="c1"&gt;// the dark class from the html element&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;toggleClassOnThemeChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// C.1 sync with the theme set in the localStorage by our index.html script tag&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;syncThemeFromLocalStorage&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="c1"&gt;// if we are in the browser we know we have access to localstorage&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPlatformBrowser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_platformId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// we load the appropriate value from the localStorage into our _theme$ replaysubject&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;_theme$&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="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&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="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// C.2 Subscribe to theme changes until the service is destroyed&lt;/span&gt;
  &lt;span class="c1"&gt;// and add/remove class from html element&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;toggleClassOnThemeChanges&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="c1"&gt;// until our service is destroyed we subscribe to all changes in the theme$ variable&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;theme$&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;takeUntil&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;_destroyed$&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;theme&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;// if it is dark we add the dark class to the html element&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;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="k"&gt;this&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="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// else if is added already, we remove it&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;_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&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;dark&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;_renderer&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="k"&gt;this&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="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="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;// D. Expose a public function that allows us to change the theme from anywhere in our application&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;toggleDarkMode&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;newTheme&lt;/span&gt; &lt;span class="o"&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;getItem&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="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="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="nx"&gt;newTheme&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;_theme$&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="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// E. Clean up our subscriptions when the service gets destroyed&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnDestroy&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;_destroyed$&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="nx"&gt;_destroyed$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&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;Let's break down what is happening here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.&lt;/strong&gt; First we set up our dependencies&lt;br&gt;
&lt;strong&gt;A.1&lt;/strong&gt; We will need to access localStorage. Analog can be used for server side rendering. Therefore, we are not guaranteed that our code will only be run in the browser. Therefore, we inject the PLATFORM_ID so we can check if we are executing in the browser and localStorage is available.&lt;br&gt;
&lt;strong&gt;A.2&lt;/strong&gt; We inject Angular's RendererFactory and pass in null as the arguments when executing the &lt;code&gt;createRenderer&lt;/code&gt; function. This will give us the default renderer. We will use this renderer to add/remove the &lt;code&gt;dark&lt;/code&gt; class from our document's &lt;code&gt;html&lt;/code&gt; element.&lt;br&gt;
&lt;strong&gt;A.3&lt;/strong&gt; We get access to the document using Angular's &lt;code&gt;DOCUMENT&lt;/code&gt; injection token. Again, Analog can be used with Server Side Rendering. Therefore, we must avoid directly accessing the browser's document object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B.&lt;/strong&gt; We continue with initializing our in memory theme store&lt;br&gt;
&lt;strong&gt;B.1&lt;/strong&gt; We use a ReplaySubject with buffer size 1 to share the theme value (which can be &lt;code&gt;light&lt;/code&gt; or &lt;code&gt;dark&lt;/code&gt;.) The ReplaySubject ensures that the last value is provided to any subscriber even if they subscribe after the latest value was emitted.&lt;br&gt;
&lt;strong&gt;B.2&lt;/strong&gt; Outside our service we expose the current theme as a simple Observable.&lt;br&gt;
&lt;strong&gt;B.3&lt;/strong&gt; Finally, we set up a &lt;code&gt;_destroyed$&lt;/code&gt; Subject, which we use to unsubscribe from all our Observables when our service is destroyed.&lt;/p&gt;

&lt;p&gt;Now that everything is set up let's see what happens when the service is created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C.&lt;/strong&gt; When the service is constructed we sync our theme from the localStorage. Then, we subscribe to our &lt;code&gt;theme$&lt;/code&gt; changes and add/remove the &lt;code&gt;dark&lt;/code&gt; class from our &lt;code&gt;html&lt;/code&gt; element accordingly.&lt;br&gt;
&lt;strong&gt;C.1&lt;/strong&gt; The appropriate theme has already been saved in the localStorage by the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; in our &lt;code&gt;index.html&lt;/code&gt; file has already stored the correct theme in the localStorage. We just pull that value from localStorage. To achieve that, we first determine whether we are in the browser and hence have access to localStorage. If we are, we load the stored value into our &lt;code&gt;_theme$&lt;/code&gt; ReplaySubject.&lt;br&gt;
&lt;strong&gt;C.2&lt;/strong&gt; Still in the constructor we subscribe to &lt;code&gt;theme$&lt;/code&gt; changes until our service is &lt;code&gt;_destroyed$&lt;/code&gt;. If the theme is &lt;code&gt;dark&lt;/code&gt; we add the &lt;code&gt;dark&lt;/code&gt; class to the &lt;code&gt;_document&lt;/code&gt;'s &lt;code&gt;html&lt;/code&gt; element using Angular's &lt;code&gt;_renderer&lt;/code&gt;. If the theme is &lt;code&gt;light&lt;/code&gt; and the &lt;code&gt;dark&lt;/code&gt; class already exists on the &lt;code&gt;html&lt;/code&gt; element, then we simply remove it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;D.&lt;/strong&gt; Then we expose a public function, which toggles our theme based on the current value in the localStorage. The new theme is subsequently pushed as the next value into of &lt;code&gt;_theme$&lt;/code&gt; ReplaySubject. This ensures that our &lt;code&gt;_theme$&lt;/code&gt; and localStorage are always in sync.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E.&lt;/strong&gt; Lastly, we configure the mechanism to terminate our subscriptions  when our service gets destroyed. Our service is a singleton provided in root. It would only be destroyed if the entire application was. Therefore, we should be fine even if we don't unsubscribe here, but it's always a good idea to clean up your subscriptions.&lt;/p&gt;

&lt;p&gt;The live code for our service can be found &lt;a href="https://stackblitz.com/edit/github-ojlugz?file=src/libs/theme/theme.service.ts" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Note:&lt;/strong&gt; All import paths must be the same for Angular to construct a singleton service. Therefore, we can not import our ThemeService using relative paths. We add the following to &lt;a href="https://stackblitz.com/edit/github-ojlugz?file=vite.config.ts" rel="noopener noreferrer"&gt;vite.config.js&lt;/a&gt; in order to support Vite's use of absolute import paths:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;resolve&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;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src&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;p&gt;As a result, we are able to import everything from the src directory. For instance, in our (home).ts route, we will now import the service with&lt;br&gt;
&lt;code&gt;import { ThemeService } from 'src/libs/theme/theme.service';&lt;/code&gt; &lt;br&gt;
rather than &lt;br&gt;
&lt;code&gt;import { ThemeService } from '../../libs/theme/theme.service';&lt;/code&gt;. This is because using relative paths can cause Angular to create multiple service instances, which can have very strange and unexpected effects. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yes, I spent way too much time trying to figure out why my ThemeService was showing different themes in different components...&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Using our ThemeService in our Analog app
&lt;/h2&gt;

&lt;p&gt;Now that our service is set up, let's use it in our Analog application. We will add a button to the header that allows us to toggle the theme from anywhere in the app. We also display the theme in our HomeComponent to illustrate that the current theme can be accessed from anywhere in our application. We also add Tailwind classes that support both light and dark theme styling. &lt;/p&gt;
&lt;h3&gt;
  
  
  Adding a button to the AppComponent
&lt;/h3&gt;

&lt;p&gt;Let's add the following to our &lt;code&gt;/src/app/app.component.ts&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-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;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;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;AsyncPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RouterOutlet&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="na"&gt;class&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 h-full bg-zinc-50 text-zinc-900 dark:text-zinc-50 dark:bg-zinc-900&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;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;` 
  &amp;lt;header class="p-4"&amp;gt;
  &amp;lt;button (click)="toggleTheme()"&amp;gt;Toggle theme&amp;lt;/button&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt; `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_themeService&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;ThemeService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;toggleTheme&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;_themeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggleDarkMode&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 code for the component is &lt;a href="https://stackblitz.com/edit/github-ojlugz?file=src/app/app.component.ts" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will be the entry point to our Analog application. &lt;/p&gt;

&lt;p&gt;The user can toggle the theme by clicking a button that is displayed in the template's header. &lt;/p&gt;

&lt;p&gt;To make our application look super sleek, we also added Tailwind classes to the host. These include classes that start with the prefix &lt;code&gt;dark&lt;/code&gt;, which are used when the &lt;code&gt;dark&lt;/code&gt; class is added to the site's &lt;code&gt;html&lt;/code&gt; element. The following classes instruct Tailwind to use a super-light zinc-gray background and a super-dark zinc-gray text-color by default and to invert them when &lt;code&gt;dark&lt;/code&gt; is present:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bg-zinc-50 text-zinc-900 dark:text-zinc-50 dark:bg-zinc-900&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Inside the component we hooked up its &lt;code&gt;toggleTheme&lt;/code&gt; method with the ThemeService's &lt;code&gt;toggleDarkMode&lt;/code&gt; method. The service is simply injected to the component using Angular's &lt;code&gt;inject&lt;/code&gt; function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Displaying the current theme on Analog's (home)page
&lt;/h3&gt;

&lt;p&gt;Finally, let's display the current theme on our homepage.&lt;/p&gt;

&lt;p&gt;Using the Angular Router as a foundation, Analog offers filesystem-based routing. It provides us with a simple method for organizing our application's folders and filenames to create our route tree. To find out more about the supported types of routes, I strongly recommend checking out Analog's &lt;a href="https://analogjs.org/docs/features/routing/overview" rel="noopener noreferrer"&gt;routing documentation.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will keep it simple for our purposes and simply create an index route by creating the following component at &lt;code&gt;/src/app/routes/(home).ts&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-home&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;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;AsyncPipe&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="na"&gt;class&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="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;div class="flex p-12 gap-8 items-center justify-center"&amp;gt;
  &amp;lt;img class="h-20 w-20" src="/analog.svg"/&amp;gt;
  &amp;lt;div class='w-[1px] h-14 dark:bg-zinc-50 bg-zinc-900'&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;img class="h-20 w-20" src="/tailwind.svg"/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;h2 class="text-2xl text-center"&amp;gt;Analog + Tailwind: Darkmode&amp;lt;/h2&amp;gt;
  &amp;lt;p class="mt-2 text-center"&amp;gt;Current theme: {{theme$ | async}}&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="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeComponent&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;_themeService&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;ThemeService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;public&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="nx"&gt;_themeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme$&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 for the HomeComponent is &lt;a href="https://stackblitz.com/edit/github-ojlugz?file=src/app/routes/(home).ts" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, we add some Tailwind magic, including some &lt;code&gt;dark&lt;/code&gt; classes. Additionally, we inject our ThemeService into the component and expose it's &lt;code&gt;theme$&lt;/code&gt; observable, which we simply display in our template using Angular's &lt;code&gt;AsyncPipe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we set up our application to import our service via &lt;code&gt;import { ThemeService } from 'src/libs/theme/theme.service';&lt;/code&gt; instead of a relative path, we will see that our &lt;code&gt;theme$&lt;/code&gt; value will correctly display &lt;code&gt;light&lt;/code&gt; if we are using our &lt;code&gt;light&lt;/code&gt; theme and &lt;code&gt;dark&lt;/code&gt; if we decide to toggle to our &lt;code&gt;dark&lt;/code&gt; theme.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/github-ojlugz" rel="noopener noreferrer"&gt;Check out the complete example here!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Awesome! We have now implemented dark mode in our Analog app. Our implementation respects the user's preferred color scheme by default. It avoids flashing of the wrong theme, and allows us to access and toggle the theme from anywhere in our application!&lt;/p&gt;

&lt;p&gt;Do you have any further questions or suggestions for blog posts? Have you tried Analog before? Do you know what meta-frameworks are and how we can benefit from them? I want to hear your ideas. Please don't hesitate to leave a comment or send me a message.&lt;/p&gt;

&lt;p&gt;Finally, if you liked this article feel free to like and share it with others. If you enjoy my content follow me on &lt;a href="https://twitter.com/goetzrobin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/goetzrobin" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>voice</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Mastering Angular Structural Directives - Micro-syntax in the wild</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Sun, 12 Feb 2023 16:00:00 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-micro-syntax-in-the-wild-1kkg</link>
      <guid>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-micro-syntax-in-the-wild-1kkg</guid>
      <description>&lt;p&gt;We covered every aspect of micro-syntax in the &lt;a href="https://dev.to/this-is-angular/mastering-angular-structural-directives-micro-syntax-demystified-340l"&gt;previous article&lt;/a&gt;. We gained a much better understanding of what Angular does when we use structural directives by examining the various parts of the official syntax reference. To reinforce this knowledge, this article will look closely at Angular's built-in NgIf and NgFor directives.&lt;/p&gt;

&lt;h2&gt;
  
  
  *ngIf - Micro-syntax to ng-template
&lt;/h2&gt;

&lt;p&gt;Let's start with a very common use case that most of you have probably seen in some form or another.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our template, we want to show some asynchronous data that we are fetching from an API.&lt;/li&gt;
&lt;li&gt;We want to show a loading indicator while we wait for the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following best practices, we do not manually subscribe in our component, but instead move that logic into the template. Allowing Angular's AsyncPipe to handle the heavy lifting.&lt;/p&gt;

&lt;p&gt;We will write something like this:&lt;br&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%2Fh7fprn9uph36uvbq8nch.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%2Fh7fprn9uph36uvbq8nch.png" alt="*ngIf" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Let's go through the same process of identifying the various parts of this statement and looking at how Angular will translate them.&lt;/p&gt;

&lt;p&gt;We apply the same color code:&lt;br&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%2Fmbetjttqo1szgum8o8zm.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%2Fmbetjttqo1szgum8o8zm.png" alt="*ngIf color coded" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
We have the asterisk &lt;strong&gt;*&lt;/strong&gt; and the &lt;code&gt;ngIf&lt;/code&gt; selector on the left side.&lt;br&gt;
Then there's the right side. It is composed of three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an &lt;u&gt;:expression&lt;/u&gt;
&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;:as&lt;/strong&gt; declaration&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;:keyExp&lt;/strong&gt; declaration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's take a closer look at each one:&lt;br&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%2F5dp0rkohitxlac36gwp1.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%2F5dp0rkohitxlac36gwp1.png" alt="items" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Because the right side begins with an expression, we know that this expression will be assigned to a '@Input()' with the same name as the selector (ngIf).&lt;br&gt;
When Angular translates our micro-syntax, it becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ng-template [ngIf]="(items$ | async)"&amp;gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's continue.&lt;br&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%2F1z6fq14hutjx8bhoyez8.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%2F1z6fq14hutjx8bhoyez8.png" alt="as items" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Next, we see that we export our expression into a &lt;em&gt;local&lt;/em&gt; variable for use in our template. While this is not explicitly stated in the documentation, it is entirely possible. The mechanism enabling this is similar to the a :keyExp declaration mechanism.&lt;br&gt;
To make 'items' hold the correct value, we must add the &lt;code&gt;ngIf&lt;/code&gt; key to our &lt;code&gt;context&lt;/code&gt; with the value of our &lt;code&gt;@Input() ngIf&lt;/code&gt;. The reason for this becomes clear when we translate our micro-syntax into the &lt;code&gt;ng-template&lt;/code&gt; version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ng-template
[ngIf]="(items$ | async)"
let-items="ngIf"
&amp;gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an:as declaration, Angular uses the name of the &lt;code&gt;@Input()&lt;/code&gt; variable as the :export or &lt;code&gt;context&lt;/code&gt;' key assigned to the &lt;em&gt;local&lt;/em&gt; variable that can be used in the template. Because the &lt;code&gt;@Input()&lt;/code&gt; in our case is &lt;code&gt;ngIf&lt;/code&gt;, &lt;code&gt;ngIf&lt;/code&gt; must be a key in the &lt;code&gt;context&lt;/code&gt;, and before rendering our template, we must assign our &lt;code&gt;@Input ngIf&lt;/code&gt; to our &lt;code&gt;context&lt;/code&gt;' &lt;code&gt;ngIf&lt;/code&gt; key.&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%2F4y6d0nwm3fh4u66gnquv.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%2F4y6d0nwm3fh4u66gnquv.png" alt="else" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Finally, we have a regular :keyExp declaration that assigns the &lt;code&gt;loading&lt;/code&gt; TemplateRef to a &lt;code&gt;@Input()&lt;/code&gt; of the NgIf directive. We use our camelCase-fusing logic to determine that our input must be named &lt;code&gt;ngIfElse&lt;/code&gt;.&lt;br&gt;
When we translate this part of our micro-syntax into the &lt;code&gt;ng-template&lt;/code&gt; version, we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ng-template
...
[ngIfElse]="loading"
&amp;gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting it all together we end up with the following:&lt;br&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%2Fxb18m3ablfgl0fgg0lb1.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%2Fxb18m3ablfgl0fgg0lb1.png" alt="micro-syntax to ng-template" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows us to infer a lot of information about how the NgIf directive. Let's make some predictions about the source code and see if they hold up.&lt;/p&gt;

&lt;p&gt;We know that the NgIf directive has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an &lt;code&gt;@Input()&lt;/code&gt; called ngIf&lt;/li&gt;
&lt;li&gt;a context object with an &lt;code&gt;ngIf&lt;/code&gt; key&lt;/li&gt;
&lt;li&gt;another &lt;code&gt;@Input()&lt;/code&gt; called ngIfElse&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's check out the official &lt;a href="https://github.com/angular/angular/blob/main/packages/common/src/directives/ng_if.ts" rel="noopener noreferrer"&gt;NgIf source code&lt;/a&gt; and see if we are correct:&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="cm"&gt;/**
   * The Boolean expression to evaluate as the condition for showing a template.
   */&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;ngIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;condition&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="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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$implicit&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ngIf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;condition&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;_updateView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;_updateView&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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$implicit&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_thenViewRef&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;_viewContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;_thenTemplateRef&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;_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;else&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;_elseViewRef&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;_viewContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;_elseTemplateRef&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;_context&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;We see the &lt;code&gt;@Input() ngIf&lt;/code&gt;. Whenever the condition passed is set, the NgIf &lt;code&gt;context&lt;/code&gt;'s &lt;code&gt;implicit&lt;/code&gt; and &lt;code&gt;ngIf&lt;/code&gt; keys are assigned our condition. Then the view is updated. When the condition passed in is truthy, the our template is rendered, else we render the &lt;code&gt;_elseTemplateRef&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="cm"&gt;/**
   * A template to show if the condition expression evaluates to false.
   */&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;ngIfElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TemplateRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NgIfContext&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;&amp;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="nf"&gt;assertTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ngIfElse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;templateRef&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;_elseTemplateRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;templateRef&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;_elseViewRef&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="c1"&gt;// clear previous view if any.&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;_updateView&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 template comes from our &lt;code&gt;@Input ngIfElse&lt;/code&gt;, which makes sure it is a valid template and assigns it to our &lt;code&gt;_elseTemplateRef&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="cm"&gt;/**
 * @publicApi
 */&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;NgIfContext&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="nx"&gt;unknown&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;public&lt;/span&gt; &lt;span class="na"&gt;$implicit&lt;/span&gt;&lt;span class="p"&gt;:&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;null&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="na"&gt;ngIf&lt;/span&gt;&lt;span class="p"&gt;:&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;null&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can confirm that our context indeed has an &lt;code&gt;ngIf&lt;/code&gt; key, next to the &lt;code&gt;$implicit&lt;/code&gt;. Both are assigned to our truthy expression passed in through the &lt;code&gt;@Input() ngIf&lt;/code&gt;. That's why we can extract our async items$ using a :let declaration using the micro syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*ngIf="(items$ | async); let items; else: template"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or using a let-declaration using the &lt;code&gt;ng-template&lt;/code&gt; and our &lt;code&gt;$implicit&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ng-template
  [ngIf]="(items$ | async)"
  let-items&amp;gt;
  {{items | json}}
&amp;lt;/ng-template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will get the exact same results! &lt;/p&gt;

&lt;p&gt;To see examples of everything &lt;a href="https://stackblitz.com/edit/angular-1kpdvn?file=src/main.ts" rel="noopener noreferrer"&gt;check out this Stackblitz!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  *ngFor - Micro-syntax to ng-template, reloaded.
&lt;/h2&gt;

&lt;p&gt;Let's move on to the NgFor directive. We assume another familiar scenario similar to the one for NgIf.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want to display a list of some asynchronous data that we are fetching from an API in our template.&lt;/li&gt;
&lt;li&gt;Once we receive the list, we want to display every item of the list using a custom template. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our code will look like this: &lt;br&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%2Ft0j9sr66lk28aiu096gi.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%2Ft0j9sr66lk28aiu096gi.png" alt="*ngFor" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Again, we color, divide, and conquer:&lt;br&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%2Fe1g69zrfdg1ol8dbtvup.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%2Fe1g69zrfdg1ol8dbtvup.png" alt="*ngFor color coded" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
The asterisk &lt;strong&gt;*&lt;/strong&gt; and the &lt;code&gt;ngFor&lt;/code&gt; selector make up the left side.&lt;br&gt;
The right side is made up out of 3 parts: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an &lt;strong&gt;:let&lt;/strong&gt; declaration&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;:keyExp&lt;/strong&gt; declaration&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;:as&lt;/strong&gt; declaration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's take a closer look at each one:&lt;br&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%2Ftbo6pgksdroyysxm3x5c.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%2Ftbo6pgksdroyysxm3x5c.png" alt="let item" width="800" height="74"&gt;&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%2Fwv6pkj5kuzfrpph90hw7.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%2Fwv6pkj5kuzfrpph90hw7.png" alt="of items" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
While the colon is frequently left out for a cleaner reading of the NgFor micro-syntax, this is actually a classic :keyExp. We assign our &lt;code&gt;(items$ | async)&lt;/code&gt; expression to our directive's camelCase-fused &lt;code&gt;@Input() ngForOf&lt;/code&gt;. This input value is also assigned to the &lt;code&gt;context&lt;/code&gt;' &lt;code&gt;ngForOf&lt;/code&gt; key, allowing us to use an :as expression to access it. We can then use our local &lt;code&gt;items&lt;/code&gt; variable in the template directly.&lt;br&gt;
When translated to the &lt;code&gt;ng-template&lt;/code&gt; version, this section becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ng-template
  [ngForOf]="(items$ | async)"
  let-items="ngForOf"
&amp;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%2Fnm0fdcduh91y375499d6.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%2Fnm0fdcduh91y375499d6.png" alt="as index" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Last but not least, we have a simple :as declaration extracting the &lt;code&gt;index&lt;/code&gt; key of our &lt;code&gt;context&lt;/code&gt; into the &lt;code&gt;i&lt;/code&gt; variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ng-template
  let-i="index"
&amp;gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting everything together we end up with this:&lt;br&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%2Faog4uvpmofm7ahltqhsf.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%2Faog4uvpmofm7ahltqhsf.png" alt="micro-syntax to ng-template" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, we can use the info above to make some predictions about the source code and see if they hold up.&lt;/p&gt;

&lt;p&gt;We know that the NgFor directive has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an &lt;code&gt;@Input()&lt;/code&gt; called &lt;code&gt;ngForOf&lt;/code&gt; taking in an iterable, for which each item one template gets rendered&lt;/li&gt;
&lt;li&gt;a context object with an &lt;code&gt;$implicit&lt;/code&gt; key making the current to-be-rendered item accessible. &lt;/li&gt;
&lt;li&gt;a context object with an &lt;code&gt;ngForOf&lt;/code&gt; key to expose the value above&lt;/li&gt;
&lt;li&gt;a context object with an &lt;code&gt;index&lt;/code&gt; key making the current to-be-rendered index accessible. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's check out the official &lt;a href="https://github.com/angular/angular/blob/main/packages/common/src/directives/ng_for_of.ts" rel="noopener noreferrer"&gt;NgIf source code&lt;/a&gt; and see if we are correct:&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;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;ngForOf&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="nx"&gt;U&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;NgIterable&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="kc"&gt;undefined&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="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;_ngForOf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ngForOf&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;_ngForOfDirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, we see in the source code the &lt;code&gt;@Input() ngForOf&lt;/code&gt; taking in our iterable. In addition, a flag is used to indicate that this variable is dirty. This is done because NgFor does not simply render and re-render every item in our iterable every time. It actually performs some advanced calculations to determine whether a template needs to be re-rendered in order to ensure that the NgFor directive performs well even with larger lists and more complex templates.&lt;/p&gt;

&lt;p&gt;If you want to learn more about NgFor, I recommend you look at its source code. We'll proceed to look at the &lt;code&gt;context&lt;/code&gt; object.&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="cm"&gt;/**
 * @publicApi
 */&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;NgForOfContext&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="nx"&gt;U&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;NgIterable&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NgIterable&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;&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="k"&gt;public&lt;/span&gt; &lt;span class="na"&gt;$implicit&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="k"&gt;public&lt;/span&gt; &lt;span class="na"&gt;ngForOf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="na"&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;public&lt;/span&gt; &lt;span class="na"&gt;count&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;get&lt;/span&gt; &lt;span class="nf"&gt;first&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;last&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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;count&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="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;even&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&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="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;odd&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="k"&gt;return&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;even&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;Again, we find our &lt;code&gt;$implicit&lt;/code&gt;, &lt;code&gt;ngForOf&lt;/code&gt;, and &lt;code&gt;index&lt;/code&gt; keys. You can also see that there is a bunch of other keys available for you to access in your template!&lt;br&gt;
If you are interested in seeing NgFor and it's &lt;code&gt;context&lt;/code&gt; in action. &lt;a href="https://stackblitz.com/edit/angular-nw65s4?file=src/main.ts" rel="noopener noreferrer"&gt;Check out this StackBlitz!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I can't tell you how proud I am of you if you've made it this far! I hope you now have a clearer understanding of how Angular works its magic on structural directive micro-syntax. Undoubtedly, this subject is among the most challenging aspects of the Angular framework. I hoped that by breaking things down into smaller, more manageable chunks, I would be able to provide you with the knowledge and tools you need to both better comprehend the built-in structural directives and make your own custom structural directives.&lt;/p&gt;

&lt;p&gt;Let us pause for a moment (or two, or three) to digest all of this new information and celebrate that the asterisk has returned.&lt;/p&gt;

&lt;p&gt;We will improve our custom "*exchangeRate" directive in the following (and possibly final?) article of this series so that it can work with the micro-syntax with ease. Additionally, instead of re-rendering the template after each successful API call, we will expose the response as an observable to our template.&lt;/p&gt;

&lt;p&gt;Do you have any further queries or subjects you'd like me to discuss? Do you believe a video on structural directives would be beneficial? I'm interested in hearing your ideas. If you have any suggestions for future posts, please do leave a comment or send me a message.&lt;/p&gt;

&lt;p&gt;Finally, if you liked this article feel free to like and share it with others. If you enjoy my content follow me on &lt;a href="https://twitter.com/goetzrobin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/goetzrobin" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Mastering Angular Structural Directives - Micro-syntax demystified</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Fri, 10 Feb 2023 19:52:38 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-micro-syntax-demystified-340l</link>
      <guid>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-micro-syntax-demystified-340l</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/this-is-angular/mastering-angular-structural-directives-its-all-about-the-context-5hai"&gt;previous article&lt;/a&gt; of this series, we took a closer look at the &lt;code&gt;context&lt;/code&gt; object and how we can use it to expose all kinds of data and even functions to our template. Combined with &lt;code&gt;@Input()&lt;/code&gt;s and dependency injection, we got a first glimpse of how powerful structural directives are once they can interact with the rest of our application or even the outside world (using Angular's &lt;code&gt;HttpClient&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;While I quickly "introduced" the structural directive micro-syntax in the &lt;a href="https://dev.to/this-is-angular/mastering-angular-structural-directives-the-basics-jhk"&gt;first article&lt;/a&gt;, we have applied all our custom directives directly to &lt;code&gt;ng-template&lt;/code&gt;s. Now it is time to reintroduce the asterisk and take the time to understand how Angular interprets this shorthand syntax.&lt;/p&gt;

&lt;h2&gt;
  
  
  One structural directive per element
&lt;/h2&gt;

&lt;p&gt;Before we begin exploring the ins and outs of Angular's micro-syntax, I should note that we can only ever apply one structural directive per host element. As the official documentation states: &lt;em&gt;The reason is simplicity. Structural directives can do complex things with the host element and its descendants. When two directives lay claim to the same host element, which one should take precedence?&lt;/em&gt; &lt;br&gt;
Therefore, if we add multiple structural directives to the same element the Angular compiler will throw an error.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the syntax reference
&lt;/h2&gt;

&lt;p&gt;I spent a lot of time trying to figure out the best way to introduce Angular's micro syntax. &lt;br&gt;
Should I do it the same way I picked up structural directives. I got gradually introduced to more and more bits, pieces, and gotcha's as I used structural directives. However, I always lacked a deeper, more complete understanding of the underlying mechanisms that make them work. &lt;br&gt;
Or should I start with the official syntax reference, which is intimidating to say the least. I always found that part of the Angular documentation to be one of the most confusing. However, once I truly wrapped my head around it, it was like an epiphany and absolutely changed the way I look at and understand structural directives.&lt;/p&gt;

&lt;p&gt;Therefore, I decided to start with that: The official syntax reference. I will do my best to break things down and explain every piece of it. Together, we will tackle it and take our Angular skills to the next level. &lt;/p&gt;

&lt;p&gt;We got this!&lt;/p&gt;
&lt;h3&gt;
  
  
  Without further ado: the structural directive syntax reference
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftl6jvqyrpmwzp51xqnzf.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%2Ftl6jvqyrpmwzp51xqnzf.png" alt="*:prefix=" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
When I first saw this expression in the documentation, I had no idea any of this meant. I was overwhelmed to be completely honest. But equipped with the knowledge from our previous articles, some clever coloring, and breaking this monstrosity down into smaller pieces, we will demystify this syntax and understand what is going on.&lt;/p&gt;

&lt;p&gt;Adding colors makes the distinct parts more obvious.&lt;br&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%2Fs33e3jsb09s9oq9p24i4.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%2Fs33e3jsb09s9oq9p24i4.png" alt="Angular Structural Directive Syntax Reference with Colors" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Much better! I also want to flip the first :let and :expression. The reason will become clear a little later.&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%2F7eabg3rwv3bszir5vhss.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%2F7eabg3rwv3bszir5vhss.png" alt="Angular Structural Directive Syntax Reference with Colors and Reordered" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Awesome! This already looks a lot less intimidating. &lt;/p&gt;
&lt;h2&gt;
  
  
  Components of the structural directive syntax reference
&lt;/h2&gt;

&lt;p&gt;Let's dive in and start breaking the syntax down into its sub-components.&lt;br&gt;
The equal sign divides the expression into two sides:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. *:prefix
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk19tbkerdq57si6nz7m8.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%2Fk19tbkerdq57si6nz7m8.png" alt="*prefix" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
The asterisk &lt;strong&gt;*&lt;/strong&gt; and the &lt;strong&gt;:prefix&lt;/strong&gt; make up the left side. &lt;br&gt;
The asterisk instructs Angular to encapsulate this directive in a &lt;code&gt;ng-template&lt;/code&gt;. &lt;br&gt;
&lt;strong&gt;:prefix&lt;/strong&gt; is always the directive's selector. If the directive has an input with the same name as our :prefix AND the right side starts with an &lt;strong&gt;:expression&lt;/strong&gt;, it can also serve as the first input of our directive.&lt;br&gt;
I will explain this in detail in the section below.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. (:expression | :let) (';' | ',')? (:let | :as | :keyExp)*
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a5rzduzsifgwt13voir.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%2F9a5rzduzsifgwt13voir.png" alt="(:expression | :let) (';' | ',')? (:let | :as | :keyExp)*" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
The right side is made up of several subgroups. A logical group is denoted by a pair of blue parentheses. The blue vertical line &lt;strong&gt;|&lt;/strong&gt; indicates a logical &lt;strong&gt;OR&lt;/strong&gt; within a group. If the group is followed by a blue question mark &lt;strong&gt;?&lt;/strong&gt;, it is optional. If it is followed by a blue asterisk *****, the group can be repeated multiple times. (This includes 0 times.)&lt;/p&gt;

&lt;p&gt;Again, we can divide the right side into 3 smaller parts:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. (:expression | :let)
&lt;/h4&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%2Ffstwgnocrhlobn0k5llt.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%2Ffstwgnocrhlobn0k5llt.png" alt="(:expression | :let)" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
This first part is either an &lt;strong&gt;:expression&lt;/strong&gt; or a &lt;strong&gt;:let&lt;/strong&gt; declaration. &lt;/p&gt;
&lt;h5&gt;
  
  
  1.1 :expression
&lt;/h5&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%2Fgc31arn7t85j5cemcdjs.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%2Fgc31arn7t85j5cemcdjs.png" alt=":prefix=" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
If we have an input parameter with the same name as the selector in our directive, this part must always be a &lt;strong&gt;:expression&lt;/strong&gt; or Angular will throw an error.&lt;br&gt;
Any valid Angular expression is regarded an &lt;strong&gt;:expression&lt;/strong&gt;. This includes boolean (true/false) values, function calls, and component variables.&lt;/p&gt;

&lt;p&gt;The following example demonstrates how the micro-syntax binds the &lt;strong&gt;:expression&lt;/strong&gt; to the &lt;strong&gt;:prefix&lt;/strong&gt; input of the component:&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;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="c1"&gt;// our directives selector, which becomes the :prefix&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;[myNgIf]&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;MyNgIfDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// dependencies needed to render&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_vcr&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;ViewContainerRef&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;_template&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;TemplateRef&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;_isRendered&lt;/span&gt; &lt;span class="o"&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="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// an input with the same name as the selector&lt;/span&gt;
  &lt;span class="c1"&gt;// binding the :expression to the :prefix (myNgIf)&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;myNgIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expression&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_isRendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expression&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;_isRendered&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;_vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;_template&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&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="nf"&gt;clear&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="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;my-app&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;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="nx"&gt;MyNgIfDirective&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)="isRendering = !isRendering"&amp;gt;Toggle&amp;lt;/button&amp;gt;
  &amp;lt;p&amp;gt;We can now bind an Angular expression like our component variable isRendering ({{isRendering}}) to our myNgIf input.
  If isRendering is true we will see our component rendered below:

  &amp;lt;div *myNgIf="isRendering"&amp;gt;Rendered.&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;public&lt;/span&gt; &lt;span class="nx"&gt;isRendering&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-8aqkv3?file=src/main.ts" rel="noopener noreferrer"&gt;Link to Stackblitz.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If no &lt;code&gt;@Input()&lt;/code&gt; property matches the selector of the directive, this first part must be a &lt;strong&gt;:let&lt;/strong&gt; declaration.&lt;/p&gt;

&lt;h5&gt;
  
  
  1.2 :let declaration
&lt;/h5&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%2Fzj6g2f5ah7cmb44wfcpp.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%2Fzj6g2f5ah7cmb44wfcpp.png" alt=":let" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
In the previous article, we saw let declarations applied directly to the &lt;code&gt;ng-template&lt;/code&gt;. We learned how they extract the variables from our &lt;code&gt;context&lt;/code&gt; and make them available for use in our template.&lt;/p&gt;

&lt;p&gt;The micro-syntax :let declarations accomplish the same thing.&lt;br&gt;
Their structures are very similar and look like this:&lt;/p&gt;

&lt;p&gt;let &lt;em&gt;local&lt;/em&gt;=“&lt;strong&gt;export&lt;/strong&gt; ';'?”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;local&lt;/em&gt; is the local variable name used in the template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;export&lt;/strong&gt; refers to the value exported by the directive under a specific name, which is one of the keys in our &lt;code&gt;context&lt;/code&gt; object! Except when the key is &lt;code&gt;$implicit&lt;/code&gt;. This key is unique and is assigned to 'let local' by default. If we don't include ="&lt;strong&gt;export&lt;/strong&gt;" in our let declaration, the value of &lt;code&gt;$implicit&lt;/code&gt; is automatically assigned to our local variable.&lt;/li&gt;
&lt;li&gt;';'? is an optional semi-colon that indicates the end of the let declaration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example of the micro-syntax :let declaration:&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;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;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;[myLet]&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;MyLetDirective&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// dependencies needed to render&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_vcr&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;ViewContainerRef&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;_template&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;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&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="k"&gt;this&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="nf"&gt;createEmbeddedView&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;_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;$implicit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World from implicit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World from second&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;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;my-app&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;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="nx"&gt;MyLetDirective&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 *myLet="let ctx;"&amp;gt;
  &amp;lt;p&amp;gt;{{ctx | json}}&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div *myLet="let secondCtx = second"&amp;gt;
  &amp;lt;p&amp;gt;{{secondCtx | json}}&amp;lt;/p&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-bkq621?file=src/main.ts" rel="noopener noreferrer"&gt;Link to Stackblitz.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see we can extract both the &lt;code&gt;$implicit&lt;/code&gt; into the &lt;code&gt;ctx&lt;/code&gt; template variable and the &lt;code&gt;second&lt;/code&gt; key into the &lt;code&gt;secondCtx&lt;/code&gt; variable using our :let declaration. Take note that we added a semi-colon at the end of the first declaration. This is not required. The second declaration without it also works.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. (; | ,)?
&lt;/h4&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%2F96i80xfxezyad3srmqm8.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%2F96i80xfxezyad3srmqm8.png" alt="(; | ,)?" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
We can add an optional semi-colon or comma after our initial :expression or :let declaration to separate it from the following component. This can be used to make your code more readable. It is not necessary.&lt;/p&gt;

&lt;p&gt;Let us update our simple directive from the:let declaration example to demonstrate that we can, but are not required to, use semi-colons and commas to separate our :let declarations.&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;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;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;[myLet]&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;MyLetDirective&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// dependencies needed to render&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_vcr&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;ViewContainerRef&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;_template&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;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&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="k"&gt;this&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="nf"&gt;createEmbeddedView&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;_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;$implicit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World from implicit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World from second&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;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;my-app&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;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="nx"&gt;MyLetDirective&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 *myLet="let context; let secondCtx = second"&amp;gt;
  semi-colon
  &amp;lt;p&amp;gt;{{context | json}}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;{{secondCtx | json}}&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div *myLet="let context, let secondCtx = second"&amp;gt;
  colon
  &amp;lt;p&amp;gt;{{secondCtx | json}}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;{{secondCtx | json}}&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div *myLet="let context let secondCtx = second"&amp;gt;
  nothing
  &amp;lt;p&amp;gt;{{context | json}}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;{{secondCtx | json}}&amp;lt;/p&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-aysump?file=src/main.ts" rel="noopener noreferrer"&gt;Link to Stackblitz.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make our code clearer, we can add a semicolon or even a comma between our :let statements. If we exclude these, our code still functions as intended.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. (:let |:as | :keyExp)*
&lt;/h4&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%2Fr5hd9mdvpfb8g8rqwtma.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%2Fr5hd9mdvpfb8g8rqwtma.png" alt="(:let |:as | :keyExp)*" width="800" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  3.1 :let declaration
&lt;/h5&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%2Fk7trtdox3uuw3ogf1x02.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%2Fk7trtdox3uuw3ogf1x02.png" alt=":let declaration" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
This third section may be repeated as many times as needed following the initial :expression, :let declaration, or the optional semicolon or comma. It can either be a :let, :as, or :keyExp declaration. The :let declaration is the same as the one we used above. We move on to the :as declaration.&lt;/p&gt;
&lt;h5&gt;
  
  
  3.2 :as declaration
&lt;/h5&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%2Fe0j7u52iq2o82urh06u7.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%2Fe0j7u52iq2o82urh06u7.png" alt=":as declaration" width="800" height="74"&gt;&lt;/a&gt;&lt;br&gt;
Similar to the :let declaration, the :as declaration extracts &lt;code&gt;context&lt;/code&gt; variables and binds them to the template.&lt;br&gt;
It's syntax is very similar to our :let declaration.&lt;br&gt;
It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;export&lt;/strong&gt; as &lt;em&gt;local&lt;/em&gt; ';'?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;export&lt;/strong&gt; is the value exported by the directive under a given name, aka one of the keys in our &lt;code&gt;context&lt;/code&gt; object!&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;local&lt;/em&gt; is the local variable name used in the template.&lt;/li&gt;
&lt;li&gt;';'? is an optional semi-colon that indicates the end of the let declaration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One could say, that :as is pretty much :let in reverse order. Angular actually translates both expression to the same "simple" let declaration: let-&lt;em&gt;local&lt;/em&gt;="&lt;strong&gt;export&lt;/strong&gt;" on the &lt;code&gt;ng-template&lt;/code&gt;.&lt;br&gt;
We can see this in action rewriting our AppComponent to use an :as declaration instead of the second :let declaration:&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;my-app&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;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="nx"&gt;MyLetDirective&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 *myLet="let context; second as secondCtx"&amp;gt;
  nothing
  &amp;lt;p&amp;gt;{{context | json}}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;{{secondCtx | json}}&amp;lt;/p&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-jtz5sq?file=src/main.ts" rel="noopener noreferrer"&gt;Link to Stackblitz.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the: as an d:let declarations, we now have two tools to extract &lt;code&gt;context&lt;/code&gt; data for usage in our template. If a property of our directive has the same name as our selector, we also know how to assign a &lt;code&gt;@Input()&lt;/code&gt; value to it. We frequently need to give our directive multiple inputs. See how :keyExp declarations do this.&lt;/p&gt;

&lt;h5&gt;
  
  
  3.3 :keyExp declaration
&lt;/h5&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%2Fuuiu08fol8gxxji3opu7.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%2Fuuiu08fol8gxxji3opu7.png" alt=":keyExp declaration" width="800" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's syntax is very similar to our :let declaration.&lt;br&gt;
It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;key ":"?&lt;/strong&gt; &lt;u&gt;:expression&lt;/u&gt; (as &lt;em&gt;local&lt;/em&gt;)? ';'?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;key ":"?&lt;/strong&gt; indicates an assignment to a special &lt;code&gt;@Input()&lt;/code&gt; variable of our directive. 
Why special? The literal &lt;strong&gt;key&lt;/strong&gt; value and the directive's selector, or the :prefix, are combined to form the variable's name. The &lt;strong&gt;key&lt;/strong&gt; is appended to the end of the :prefix in camelCase. :prefix and &lt;strong&gt;key&lt;/strong&gt; become :prefixKey, ngIf and else become ngIfElse. You can see this in the Angular &lt;a href="https://github.com/angular/angular/blob/a7597dd08026a4071758323d54ccbfb382e0c780/packages/common/src/directives/ng_if.ts#L190" rel="noopener noreferrer"&gt;source code.&lt;/a&gt; Also, you can see that the colon to indicate an assignment (similar to assigning a value to a key in a JavaScript object) is optional. &lt;code&gt;*ngIf="loaded; else loadingTemplate"&lt;/code&gt; and &lt;code&gt;*ngIf="loaded; else: loadingTemplate"&lt;/code&gt; are the same thing.&lt;/li&gt;
&lt;li&gt;
&lt;u&gt;:expression&lt;/u&gt; is the Angular expression bound to your :prefixKey &lt;code&gt;@Input()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(as &lt;em&gt;local&lt;/em&gt;)? is an optional way to directly use the &lt;u&gt;:expression&lt;/u&gt;, which was passed in as an &lt;code&gt;@Input()&lt;/code&gt;, in the template with the &lt;em&gt;local&lt;/em&gt; variable name.
&lt;strong&gt;Very important:&lt;/strong&gt; For this to work, the directive's &lt;code&gt;context&lt;/code&gt; object must have a camelCase-fused-key. A directive like this: &lt;code&gt;&amp;lt;div *calculateAvg="let avg; data: (data$ | async) as testData"&amp;gt;...&amp;lt;/div&amp;gt;&lt;/code&gt; Would need to have a &lt;code&gt;calculateAvgTestData&lt;/code&gt; key in its &lt;code&gt;context&lt;/code&gt; object for &lt;code&gt;testData&lt;/code&gt; to expose the correct value.&lt;/li&gt;
&lt;li&gt;';'? is an optional semi-colon that indicates the end of the let declaration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at the full source code of the &lt;code&gt;*calculateAvg&lt;/code&gt; example from above:&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="c1"&gt;// directive to calculate the average&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;[calculateAvg]&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="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;CalculateAvgDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// dependencies needed to render the template&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_vcr&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;ViewContainerRef&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;_template&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;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// our average that we calculate whenever&lt;/span&gt;
  &lt;span class="c1"&gt;// new data is provided&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_avg&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="c1"&gt;// private reference to our data so we can calculate the&lt;/span&gt;
  &lt;span class="c1"&gt;//  average in the set function&lt;/span&gt;
  &lt;span class="k"&gt;private&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// we want to be able to use the key data in our key expression&lt;/span&gt;
  &lt;span class="c1"&gt;// we know that we need to camelCase-fuse our key with the directive name&lt;/span&gt;
  &lt;span class="c1"&gt;// calculateAvg (:prefix) + data (key) =&amp;gt; calculateAvgData (:prefixKey)&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;calculateAvgData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// store data passed in&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;values&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;sum&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;_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;// calculate the average&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;_avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sum&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;max&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="nx"&gt;values&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="c1"&gt;// render template&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;_vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// make avg available through $implicit&lt;/span&gt;
      &lt;span class="na"&gt;$implicit&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;_avg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// make data available for as expression&lt;/span&gt;
      &lt;span class="na"&gt;calculateAvgData&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="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;my-app&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;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="nx"&gt;CalculateAvgDirective&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 *calculateAvg="let avg; data: (data$ | async) as testData"&amp;gt;
  micro-syntax: {{avg}} is the average of {{testData}}
  &amp;lt;/div&amp;gt;

  &amp;lt;ng-template 
    calculateAvg
    [calculateAvgData]="(data$ | async)"
    let-testData="calculateAvgData"
    let-avg
    &amp;gt;
    ng-template: {{avg}} is the average of {{testData}}
    &amp;lt;/ng-template&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="nx"&gt;data$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;of&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;3&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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="mi"&gt;2&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;1&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-4vwtaw?file=src/main.ts" rel="noopener noreferrer"&gt;I highly recommend to examine working code is here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't worry if a lot of information sounds confusing or still a bit unclear. :keyExp declarations are definitely the hardest part to understand about structural directives and their micro-syntax. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Today, we've covered every aspect of micro-syntax. We dissected and thoroughly examined the official syntax reference. I hope this has helped you understand what Angular does when structural directives are used in your applications. However, I believe that in order to cement today's knowledge, we should look at how the syntax is used in Angular's built-in directives. In &lt;a href="https://dev.to/this-is-angular/mastering-angular-structural-directives-micro-syntax-in-the-wild-1kkg"&gt;the following article,&lt;/a&gt; we will apply what we've learned today and dissect the NgIf and NgFor directives.&lt;/p&gt;

&lt;p&gt;If you liked this article feel free to like and share it with others. If you enjoy my content follow me on &lt;a href="https://twitter.com/goetzrobin" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/goetzrobin" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>healthydebate</category>
    </item>
    <item>
      <title>Mastering Angular Structural Directives - It’s all about the context</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Sat, 21 Jan 2023 18:54:55 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-its-all-about-the-context-5hai</link>
      <guid>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-its-all-about-the-context-5hai</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/this-is-angular/mastering-angular-structural-directives-the-basics-jhk"&gt;first article of this series,&lt;/a&gt; we learned how to render our first template to the DOM using structural directives. We saw that the asterisk micro syntax is actually optional and expanded to an &lt;code&gt;ng-template&lt;/code&gt; by Angular. Then, we leveraged dependency injection to obtain references to our template and view container and used them to create new DOM elements.&lt;/p&gt;

&lt;p&gt;So far we have only rendered static HTML to the DOM. There was no outside interaction between our template and the rest of the application. Let's change that and introduce structural directive's &lt;code&gt;context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note: We will only use the &lt;code&gt;ng-template&lt;/code&gt; notation in this post and dedicate the next post solely to understanding the asterisk micro syntax.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structural Directive's Context - Definition &amp;amp; Structure
&lt;/h2&gt;

&lt;p&gt;We already know how to use the ViewContainerRef's &lt;code&gt;createEmbeddedView&lt;/code&gt; method to render our template. This function takes a second optional parameter called &lt;code&gt;context&lt;/code&gt;. The ViewContainerRef's documentation defines it as such:&lt;br&gt;
&lt;em&gt;The data-binding context of the embedded view, as declared in the &lt;code&gt;&amp;lt;ng-template&amp;gt;&lt;/code&gt; usage. Optional. Default is undefined.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It seems like we can use this &lt;code&gt;context&lt;/code&gt; object to bind data to our template. Unfortunately, this is all the information we get directly from the ViewContainerRef documentation. We are left in the dark on how to use it or if there is a specific structure to this context object.&lt;/p&gt;

&lt;p&gt;More about the structure is provided by the documentation of another built-in structural directive we have not mentioned yet: &lt;strong&gt;NgTemplateOutlet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This directive &lt;em&gt;inserts an embedded view from a prepared TemplateRef&lt;/em&gt; and takes in a template reference and (amongst others) an additional input: &lt;code&gt;context&lt;/code&gt;. Let’s see how &lt;code&gt;context&lt;/code&gt; is described in this part of the documentation:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A context object to attach to the EmbeddedViewRef. This should be an object, the object's keys will be available for binding by the local template let declarations. Using the key $implicit in the context object will set its value as default&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We combine the information from both docs and recap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can pass a &lt;code&gt;context&lt;/code&gt; object to the ViewContainerRef's &lt;code&gt;createEmbeddedView&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;This object will be used to bind data to the embedded view, which is rendered based on our &lt;code&gt;ng-template&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The keys of the &lt;code&gt;context&lt;/code&gt; object will be available for the &lt;code&gt;ng-template&lt;/code&gt; through &lt;strong&gt;let declarations&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;There is a special key &lt;code&gt;$implicit&lt;/code&gt; that is used as the default value of those let declarations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Already, things are much clearer. But what exactly are those &lt;strong&gt;let declarations&lt;/strong&gt;? And how can we use them to connect our &lt;code&gt;context&lt;/code&gt; object to our &lt;code&gt;ng-template&lt;/code&gt;?&lt;/p&gt;
&lt;h2&gt;
  
  
  Let declarations - binding context to the template
&lt;/h2&gt;

&lt;p&gt;Let declarations can be used on &lt;code&gt;ng-template&lt;/code&gt; tags to give us access to the &lt;code&gt;context&lt;/code&gt; provided by the applied structural directive. &lt;/p&gt;

&lt;p&gt;Their structure looks like this: &lt;/p&gt;

&lt;p&gt;let-&lt;em&gt;local&lt;/em&gt;=“&lt;strong&gt;export&lt;/strong&gt;”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;local&lt;/em&gt; is the local variable name used in the template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;export&lt;/strong&gt; is the value exported by the directive under a given name, aka one of the keys in our context object! Unless the key is &lt;code&gt;$implicit&lt;/code&gt;. This key is special and is assigned to let-&lt;em&gt;local&lt;/em&gt; by default. If =“&lt;strong&gt;export&lt;/strong&gt;” is omitted in a let declaration, the value of &lt;code&gt;$implicit&lt;/code&gt; is automatically assigned to our &lt;em&gt;local&lt;/em&gt; variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at an example to better understand how this data binding works:&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;// We create a directive that allows us to provide&lt;/span&gt;
  &lt;span class="c1"&gt;// the template with information on how long a&lt;/span&gt;
  &lt;span class="c1"&gt;// specific unit of measurement is in meters.&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;[unitsInMeters]&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;UnitsInMetersDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The context passed to the ng-template&lt;/span&gt;
  &lt;span class="c1"&gt;// it holds information about how long a unit is in meters&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;unitsInMetersContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// the default is meter&lt;/span&gt;
    &lt;span class="na"&gt;$implicit&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="c1"&gt;// a mile is 1609.34 meters long&lt;/span&gt;
    &lt;span class="na"&gt;mile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1609.34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// To render our template to the DOM we:&lt;/span&gt;
  &lt;span class="c1"&gt;// get the template ref from the ng-template host&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;template&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;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// get the viewcontainerref from the host: &amp;lt;!--comment--&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;vcr&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;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// on initialization of our directive we&lt;/span&gt;
  &lt;span class="c1"&gt;// render our template to the DOM passing&lt;/span&gt;
  &lt;span class="c1"&gt;// our unitsInMetersContext&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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;vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;template&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;unitsInMetersContext&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;Awesome! We can finally bind data to our templates! For now, we only used constant values, but we are in no way limited to only that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic context - Unleashing the full power of structural directives
&lt;/h2&gt;

&lt;p&gt;As we build more complex directives we will need to pass more than just constant values to our template. Angular does not limit the type of values of our context's keys. We can pass pretty much any type of value to our template. &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static values&lt;/strong&gt; such as constant numbers, strings, or other objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic values&lt;/strong&gt;, which are references to properties of our directive. Of course, can include &lt;code&gt;@Input()&lt;/code&gt; properties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observables&lt;/strong&gt;, which are references to observables within our directive. I realize that those are also "just" properties of our directive, but they are really powerful and can avoid excessive re-rendering of our template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functions&lt;/strong&gt;, which are references to our directive's functions. It should be noted, that the &lt;code&gt;this&lt;/code&gt; keyword used in those functions needs to be bound to the directive's &lt;strong&gt;execution context&lt;/strong&gt; (a different context than the one this article is focused on). To achieve that you can either wrap the function passed in the context with an arrow function or use &lt;code&gt;.bind(this)&lt;/code&gt; on the function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see the true power of &lt;code&gt;context&lt;/code&gt; in practice and create a structural directive exposing information about currency exchange rates - the &lt;strong&gt;exchangeRate&lt;/strong&gt; directive.&lt;/p&gt;

&lt;p&gt;Our requirements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The directive should allow the user to input the &lt;strong&gt;from&lt;/strong&gt; currency and the &lt;strong&gt;to&lt;/strong&gt; currency. These inputs are &lt;a href="https://knowledgecenter.zuora.com/Quick_References/Country%2C_State%2C_and_Province_Codes/D_Currencies_and_Their_3-Letter_Codes" rel="noopener noreferrer"&gt;ISO 3-Letter Currency Codes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;By default, we convert from USD to EUR&lt;/li&gt;
&lt;li&gt;We call an API to return the current rate between the currencies&lt;/li&gt;
&lt;li&gt;Once we get the value we render our template and expose:

&lt;ol&gt;
&lt;li&gt;the &lt;strong&gt;from&lt;/strong&gt; currency code&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;to&lt;/strong&gt; currency code&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;rate&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;reverseFn&lt;/strong&gt; function, which can be called to switch the &lt;strong&gt;from&lt;/strong&gt; and &lt;strong&gt;to&lt;/strong&gt; variables. Reversing the current &lt;strong&gt;rate&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using it in a component should be as easy as 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;my-app&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;label&amp;gt;From &amp;lt;input [(ngModel)]="fromInput"&amp;gt; &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;To &amp;lt;input [(ngModel)]="toInput"&amp;gt; &amp;lt;/label&amp;gt;

  &amp;lt;ng-template exchangeRate [from]="fromInput" [to]="toInput" let-from="from" let-to="to" let-rate="rate" let-reverse="reverseFn"&amp;gt;
  &amp;lt;p&amp;gt;Converting from {{from}} to {{to}} the exchange rate is: {{rate}}&amp;lt;/p&amp;gt;
  &amp;lt;button (click)="reverse()"&amp;gt;Reverse&amp;lt;/button&amp;gt;
  &amp;lt;/ng-template&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;fromInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;toInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EUR&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;Let's look at the overall structure of how our directive might implement this functionality:&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;[exchangeRate]&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;ExchangeRateDirective&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnChanges&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// from input which defaults to USD if none is provided&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// to input which defaults to EUR if none is provided&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EUR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// TemplateRef and ViewContainerRef to render to DOM&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;template&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;TemplateRef&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;vcr&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;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// HttpClient to query API&lt;/span&gt;
  &lt;span class="k"&gt;private&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="c1"&gt;// initally we render our template with the default values&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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="nf"&gt;getExchangeRateFromApiCreateContextRenderTemplate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// whenever an input value changes we query our&lt;/span&gt;
  &lt;span class="c1"&gt;// api for the new rate and re-render the template&lt;/span&gt;
  &lt;span class="c1"&gt;// given the new input is a 3 letter currency code&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnChanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SimpleChanges&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="c1"&gt;// get the new from value or keep old&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newFrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changes&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="nx"&gt;changes&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="nx"&gt;currentValue&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="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// get the new to value or keep old&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentValue&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;to&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// over simplified check if inputs are currency code&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;newFrom&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;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;newTo&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;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// stop processing changes as definitely not a valid currency code&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;// get new rate and render template to DOM&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;getExchangeRateFromApiCreateContextRenderTemplate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getExchangeRateFromApiCreateContextRenderTemplate&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="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;reverseRate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// this is for demonstration purposes only&lt;/span&gt;
    &lt;span class="c1"&gt;// since from and to are inputs reassigning those inputs&lt;/span&gt;
    &lt;span class="c1"&gt;// might be confusing to the consumer of the directive&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldFrom&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="k"&gt;from&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="k"&gt;from&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;to&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;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldFrom&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;getExchangeRateFromApiCreateContextRenderTemplate&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;First, we take in our &lt;strong&gt;from&lt;/strong&gt; and &lt;strong&gt;to&lt;/strong&gt; inputs with the defaults from the requirements. Then, we inject our dependencies which we need to render our template to the DOM and make API calls to get the newest exchange rate.&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;// from input which defaults to USD if none is provided&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// to input which defaults to EUR if none is provided&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EUR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// TemplateRef and ViewContainerRef to render to DOM&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;template&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;TemplateRef&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;vcr&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;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// HttpClient to query API&lt;/span&gt;
  &lt;span class="k"&gt;private&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On initialization, we get the exchange rate from the API, create the &lt;code&gt;context&lt;/code&gt; and render the template.&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;// initally we render our template with the default values&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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="nf"&gt;getExchangeRateFromApiCreateContextRenderTemplate&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;On every subsequent change, we determine if the inputs changed and if they are a currency code. If they did not, we do nothing. If they did, we again get the exchange rate from the API, create the &lt;code&gt;context&lt;/code&gt; and render the template.&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;// whenever an input value changes we query our&lt;/span&gt;
  &lt;span class="c1"&gt;// api for the new rate and re-render the template&lt;/span&gt;
  &lt;span class="c1"&gt;// given the new input is a 3 letter currency code&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnChanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SimpleChanges&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="c1"&gt;// get the new from value or keep old&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newFrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changes&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="nx"&gt;changes&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="nx"&gt;currentValue&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="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// get the new to value or keep old&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentValue&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;to&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// over simplified check if inputs are currency code&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;newFrom&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;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;newTo&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;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// stop processing changes as definitely not a valid currency code&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;// get new rate and render template to DOM&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;getExchangeRateFromApiCreateContextRenderTemplate&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;Finally, we define a reverse function that reverses the &lt;strong&gt;from&lt;/strong&gt; and &lt;strong&gt;to&lt;/strong&gt; variable, then gets the reversed rate from the API, creates the context, and renders the template.&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;public&lt;/span&gt; &lt;span class="nf"&gt;reverseRate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// this is for demonstration purposes only&lt;/span&gt;
    &lt;span class="c1"&gt;// since from and to are inputs reassigning those inputs&lt;/span&gt;
    &lt;span class="c1"&gt;// might be confusing to the consumer of the directive&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldFrom&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="k"&gt;from&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="k"&gt;from&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;to&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;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldFrom&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;getExchangeRateFromApiCreateContextRenderTemplate&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;Let's take a closer look at the &lt;code&gt;getExchangeRateFromApiCreateContextRenderTemplate&lt;/code&gt; method and see how it ties everything together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getExchangeRateFromApiCreateContextRenderTemplate&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="c1"&gt;// 1. we get the new rate based on the from and to currencies and re-render our template&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="s2"&gt;`https://open.er-api.com/v6/latest/&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="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// 2. we only care about the immediate response&lt;/span&gt;
        &lt;span class="nf"&gt;take&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="c1"&gt;// 3. we extract the rate for the currency&lt;/span&gt;
        &lt;span class="c1"&gt;// we convert to&lt;/span&gt;
        &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExchangeRateResponse&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;return&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;rates&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;to&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;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;rate&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;// 4. once the rate arrives, we build the&lt;/span&gt;
        &lt;span class="c1"&gt;// context which will be exposed to our template.&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exchangeRateContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// 4.1 current value of our from property&lt;/span&gt;
          &lt;span class="na"&gt;from&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="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// 4.2 current value of our to property&lt;/span&gt;
          &lt;span class="na"&gt;to&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;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// 4.3 rate returned by api&lt;/span&gt;
          &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// 4.4 function reference to refresh&lt;/span&gt;
          &lt;span class="na"&gt;reverseFn&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;reverseRate&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;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="c1"&gt;// 5. we render the template with the new context&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;vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;exchangeRateContext&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;ol&gt;
&lt;li&gt;The method uses the HttpClient's &lt;em&gt;get&lt;/em&gt; method to request a new rate from the API for our &lt;strong&gt;from&lt;/strong&gt; currency code and
returns an observable of the response.&lt;/li&gt;
&lt;li&gt;We ensure we only react to the first value emitted using the &lt;code&gt;take(1)&lt;/code&gt; RxJs operator.&lt;/li&gt;
&lt;li&gt;With the &lt;code&gt;map&lt;/code&gt; operator, the API response inside of the observable is mapped to the rate for our &lt;em&gt;to&lt;/em&gt; currency code. If we cannot find the code, we return a symbolic value of -1. This indicates to users of our directive that something is off so they can display an appropriate message. Of course, this is oversimplified, but I hope you get the idea.&lt;/li&gt;
&lt;li&gt;We subscribe to our observable and obtain the rate.
Once the rate is received, we build our &lt;code&gt;context&lt;/code&gt; with the following keys:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;from&lt;/strong&gt;: the current currency code of our directives &lt;em&gt;from&lt;/em&gt; property.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;to&lt;/strong&gt;: the current currency code of our directives &lt;em&gt;to&lt;/em&gt; property&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rate&lt;/strong&gt;: the exchange rate returned from the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reverseFn&lt;/strong&gt;: a reference to our directives reverseRate function bound to the current execution context with an arrow function.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;We render our template to the DOM and pass the new &lt;code&gt;context&lt;/code&gt;.&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Now, we can use our directive in the AppComponent as described above:&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;my-app&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;label&amp;gt;From &amp;lt;input [(ngModel)]="fromInput"&amp;gt; &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;To &amp;lt;input [(ngModel)]="toInput"&amp;gt; &amp;lt;/label&amp;gt;

  &amp;lt;ng-template exchangeRate [from]="fromInput" [to]="toInput" let-from="from" let-to="to" let-rate="rate" let-reverse="reverseFn"&amp;gt;
  &amp;lt;p&amp;gt;Converting from {{from}} to {{to}} the exchange rate is: {{rate}}&amp;lt;/p&amp;gt;
  &amp;lt;button (click)="reverse()"&amp;gt;Reverse&amp;lt;/button&amp;gt;
  &amp;lt;/ng-template&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;fromInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;toInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EUR&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;And see our code in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/A9sb8jm6ANkGVsuK3n/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/A9sb8jm6ANkGVsuK3n/giphy.gif" width="614" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! We have now interacted with the rest of the app through inputs and outputs, even the world outside our application by injecting the HttpClient and making API calls to remote servers! Everything from within our structural directive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-ivy-phkd8x?file=src/app/app.component.ts" rel="noopener noreferrer"&gt;Check out the working directive here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One step at a time
&lt;/h2&gt;

&lt;p&gt;There are a lot of ways to improve our directive such as improving performance by avoiding re-renders using observables for our exposed variables and strict type checking for our context in the &lt;code&gt;ng-template&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, these are topics for another post. If you are interested in how to strictly type your context exposed to templates Thomas Laforge wrote this great &lt;a href="https://dev.to/this-is-angular/directive-type-checking-45oe"&gt;article&lt;/a&gt; covering everything you need to know. I highly recommend you read it!&lt;/p&gt;

&lt;p&gt;Let's be proud of ourselves today. We took another step to master structural directives in Angular by understanding the key concept of the &lt;code&gt;context&lt;/code&gt;. Let's take some time to digest all this new information and get ready to learn &lt;a href="https://dev.to/this-is-angular/mastering-angular-structural-directives-micro-syntax-demystified-340l"&gt;everything about the structural directive micro syntax.&lt;/a&gt; The magic that brings us back our asterisk.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>fullstack</category>
      <category>ai</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Mastering Angular Structural Directives - The basics</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Thu, 12 Jan 2023 21:14:50 +0000</pubDate>
      <link>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-the-basics-jhk</link>
      <guid>https://forem.com/playfulprogramming-angular/mastering-angular-structural-directives-the-basics-jhk</guid>
      <description>&lt;p&gt;Structural directives in Angular are one of the framework’s most powerful tools. We encounter them early in our Angular journeys. They are integral to developing even the most basic Angular applications.&lt;/p&gt;

&lt;p&gt;Whether it is rendering a list of todos or toggling some icon once a todo is completed, *ngFor and *ngIf become familiar faces in the earliest days as Angular developers. From then on, they stay our trusted partners on which we frequently rely in day-to-day development tasks. However, the inner workings of these directives are often a mystery to both new and experienced developers.&lt;/p&gt;

&lt;p&gt;In this series, we will delve deeper into the internal workings of structural directives, providing a comprehensive understanding of what is going on under the asterisk. In this article, we will examine what needs to happen for a structural directive to render to the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  *easyToSpot - A minimal introduction to structural directive’s micro-syntax
&lt;/h2&gt;

&lt;p&gt;You probably spotted two of the built-in structural directives: NgIf and NgFor. They were easy to identify, as I followed the convention laid out directly in the Angular docs: &lt;em&gt;When structural directives are applied they generally are prefixed by an asterisk, *.&lt;/em&gt; More interestingly, the docs also state that Angular uses this convention to wrap the element the directive is applied to, also known as the host element, with an &lt;code&gt;ng-template&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ngIf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hero&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="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes&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="o"&gt;&amp;lt;&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;template&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ngIf&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&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;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ng-template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at this longhand version, we can identify two ways of defining structural directives: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;By &lt;strong&gt;what they do&lt;/strong&gt; (as the official documentation does): &lt;em&gt;Structural directives are directives that change the DOM layout by adding and removing DOM elements.&lt;/em&gt; &lt;/li&gt;
&lt;li&gt;By &lt;strong&gt;what they are&lt;/strong&gt;: Directives that are applied to &lt;code&gt;ng-template&lt;/code&gt;s and come with an optional micro syntax that makes our HTML nicer to read.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Directive superpowers - Rendering to the DOM with the help of dependency injection
&lt;/h2&gt;

&lt;p&gt;We can fully utilize Angular's dependency injection (DI). because we are dealing with a directive.  Given that we know the personality of the directive's host, we can access it by simply injecting it into our directive.&lt;/p&gt;

&lt;p&gt;The following example demonstrates 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="c1"&gt;// in our app&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ourDirective is applied to the host component&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;host ourDirective &amp;gt;&amp;lt;/host&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;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;host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// the host simply renders the currentName&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;`{{ currentName }}`&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;HostComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// by default the currentName is setByTheHost&lt;/span&gt;
  &lt;span class="nx"&gt;currentName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setByTheHost&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;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;[ourDirective]&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;OurDirective&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ourDirective uses DI to get access to the HostComponent&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;hostComponent&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;HostComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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="c1"&gt;// after 3 seconds OurDirective sets the hostComponent's currentName as changedByDirective&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;hostComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;changedByDirective&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;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see the code in action you can check out this &lt;a href="https://stackblitz.com/edit/angular-ivy-n5jbb9?file=src/app/app.component.ts" rel="noopener noreferrer"&gt;Stackblitz.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Injecting the template
&lt;/h3&gt;

&lt;p&gt;From earlier, we know that structural directives are always applied to &lt;code&gt;ng-template&lt;/code&gt;s. Therefore, we can inject Angular's TemplateRef, which provides us with the necessary information to render the template to the DOM. Let's look at the code below to see the internal workings of the TemplateRef:&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;my-app&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;ng-template [ourDirective]&amp;gt;I am in the template&amp;lt;/ng-template&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;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;[ourDirective]&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;OurDirective&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&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;template&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;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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="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="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;template&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;_declarationTContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tViews&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will log the TemplateRef's instructions, which tell Angular how to generate our DOM element to the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function AppComponent_ng_template_0_Template(rf, ctx) { if (rf &amp;amp; 1) {
i0.ɵɵtext(0, "I am in the template");
} }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-ivy-brjapu?file=src/app/app.component.ts" rel="noopener noreferrer"&gt;Link to Stackblitz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have the information on how to render our template, we need somewhere to render it. Again, Angular's dependency injection system gives us access to what we need. The &lt;strong&gt;ViewContainerRef&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Injecting the view container
&lt;/h3&gt;

&lt;p&gt;Every Angular component or directive has access to something called the ViewContainerRef. The official documentation defines it as &lt;em&gt;a container where one or more views can be attached to a component.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;We can think of it as a reference to a virtual container around an anchor element. The anchor element indicates the place in the DOM where we can dynamically create new elements. The container can instantiate new elements dynamically. It will render those new elements as siblings of the anchor element. &lt;/p&gt;

&lt;p&gt;Our anchor element can be a custom element, an element node, or even a comment element. Let's take a look at the example below:&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;my-app&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;our-component&amp;gt;&amp;lt;/our-component&amp;gt;
  &amp;lt;div ourDirective&amp;gt;On div&amp;lt;/div&amp;gt;
  &amp;lt;ng-template ourDirective&amp;gt;On ng-template&amp;lt;/ng-template&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;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;[ourDirective]&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;OurDirective&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;vcr&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;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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="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="k"&gt;this&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;element&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="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;our-component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div&amp;gt;Our Component&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;OurComponent&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;vcr&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;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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="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="k"&gt;this&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;element&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="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 produces the following results in the Stackblitz and Chrome consoles:&lt;br&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%2F8drjm2q3q8zdccav3bv3.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%2F8drjm2q3q8zdccav3bv3.png" alt="screenshot from Stackblitz, showing the custom HTMLElement, HTMLDivElement, Comment logged by our directive" width="800" height="440"&gt;&lt;/a&gt;&lt;br&gt;
We see that our ViewContainerRef's native elements are our custom element, a regular HTML &lt;code&gt;div&lt;/code&gt; element, and in the case of the &lt;code&gt;ng-template&lt;/code&gt;, a &lt;code&gt;&amp;lt;!--container--&amp;gt;&lt;/code&gt; comment that Angular inserts into the HTML for any (potential) view it manages. Each time we get the DOM anchor which the ViewContainerRef can use to create new (sibling) elements.&lt;/p&gt;

&lt;p&gt;Again, I encourage you to check out the working code in the &lt;a href="https://stackblitz.com/edit/angular-ivy-krvagm?file=src/app/app.component.ts" rel="noopener noreferrer"&gt;Stackblitz.&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Putting the two together
&lt;/h3&gt;

&lt;p&gt;Finally, we have everything we need to live up to the official definition of structural directives: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Change the DOM layout by adding and removing DOM elements.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's create a custom structural directive that renders our template to the DOM not once, but &lt;strong&gt;TWO&lt;/strong&gt; times! Super exciting!&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;[twoTimes]&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;TwoTimesDirective&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// get the template ref from the ng-template host&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;template&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;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// get the viewcontainerref from the host: &amp;lt;!--comment--&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;vcr&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;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// on initialization of our directive, render our template to the DOM twice&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ngOnInit&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;vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;template&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;vcr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&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;template&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;We inject both TemplateRef and ViewContainerRef into our directive. In our &lt;code&gt;ngOnInit&lt;/code&gt; lifecycle hook, we create two sibling elements based on the template obtained from the directive's host.&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;my-app&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 *twoTimes&amp;gt;Two times from asterisk&amp;lt;/p&amp;gt;
  &amp;lt;ng-template twoTimes&amp;gt;&amp;lt;p&amp;gt;Two times from ng-template&amp;lt;/p&amp;gt;&amp;lt;/ng-template&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To illustrate that our micro-syntax is correctly transpiled we use both alternatives in our AppComponent. The result is a total of four elements rendered to the DOM. Each component created two siblings to the &lt;code&gt;&amp;lt;!--comment--&amp;gt;&lt;/code&gt; from the respective ViewContainerRef:&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;my-app&lt;/span&gt; &lt;span class="na"&gt;ng-version=&lt;/span&gt;&lt;span class="s"&gt;"15.0.2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Two times from asterisk&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Two times from asterisk&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!--container--&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Two times from ng-template&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Two times from ng-template&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!--container--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/my-app&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackblitz.com/edit/angular-ivy-ezyipj?file=src/app/app.component.ts" rel="noopener noreferrer"&gt;Link to Stackblitz&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Just getting started
&lt;/h2&gt;

&lt;p&gt;In this article, we took the first steps to truly understanding how structural directives work. However, we have just scratched the surface. We barely even peeked under the asterisk. To be able to truly leverage the power of structural components we will need to understand how we can pass data to our template using a &lt;strong&gt;context&lt;/strong&gt; object, how we can ensure &lt;strong&gt;strict template type checking&lt;/strong&gt; for said context, and how the &lt;strong&gt;structural directive syntax&lt;/strong&gt; is parsed.&lt;/p&gt;

&lt;p&gt;So let's pat ourselves on the back, take a quick &lt;a href="https://www.youtube.com/watch?v=AKGrmY8OSHM" rel="noopener noreferrer"&gt;NSDR (Non Sleep Deep Rest)&lt;/a&gt; break to let the information settle, and get excited about the next part of our journey to structural directive mastery. &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>community</category>
    </item>
    <item>
      <title>Reusable Buttons with Angular, Tailwind &amp; Storybook</title>
      <dc:creator>Robin Goetz</dc:creator>
      <pubDate>Mon, 28 Nov 2022 06:30:14 +0000</pubDate>
      <link>https://forem.com/goetzrobin/reusable-buttons-with-angular-tailwind-ki9</link>
      <guid>https://forem.com/goetzrobin/reusable-buttons-with-angular-tailwind-ki9</guid>
      <description>&lt;h2&gt;
  
  
  What will we build?
&lt;/h2&gt;

&lt;p&gt;We will build a custom Angular directive that applies our style systems button design to the element it is applied to.&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%2Fz9lqpcevu7f9dveiz64b.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%2Fz9lqpcevu7f9dveiz64b.png" alt="Storybook view of button directive" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a directive?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You get all the benefits of tailwind and its powerful styling techniques without losing the accessibility coming from the default html elements.&lt;/li&gt;
&lt;li&gt;With a directive your styles are not tied to the button element. Other elements, like links/anchors, can also look like a button. This flexibility makes it easier for you to write great looking and accessible applications &amp;amp; websites.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Tailwind?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Its set of utility classes have good design principles built in.&lt;/li&gt;
&lt;li&gt;Building your reusable UI components with tailwind gives you consistency and flexibility out of the box&lt;/li&gt;
&lt;li&gt;You can use Angular’s directives and components to hide the often complained about ugly markup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The code.
&lt;/h2&gt;

&lt;p&gt;This tutorial needs an Angular project that is configured to run with Tailwind (and Storybook if you want to get the interactive demo from above). &lt;/p&gt;

&lt;h3&gt;
  
  
  The setup.
&lt;/h3&gt;

&lt;p&gt;The easiest way to follow along is by forking my project on &lt;a href="https://github.com/goetzrobin/nx-angular-tailwind" rel="noopener noreferrer"&gt;Github&lt;/a&gt; and checking out the &lt;code&gt;0-base-storybook-setup&lt;/code&gt; branch&lt;/p&gt;

&lt;p&gt;You will find an NX project with an &lt;code&gt;example&lt;/code&gt; app and a &lt;code&gt;libs&lt;/code&gt; with a &lt;code&gt;ui&lt;/code&gt; subdirectory, which contains the &lt;code&gt;atoms&lt;/code&gt; directory and the &lt;code&gt;storybook&lt;/code&gt; library. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;storybook&lt;/code&gt; library allows you to run the &lt;code&gt;npx nx run ui-storybook:storybook&lt;/code&gt; (or &lt;code&gt;nx run ui-storybook:storybook&lt;/code&gt; if you have nx installed globally) command. This generates all stories in the &lt;code&gt;ui&lt;/code&gt; directory. It is also configured to correctly render all Tailwind classes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;atoms&lt;/code&gt; directory (based on Brad Frost’s &lt;a href="https://atomicdesign.bradfrost.com/chapter-2/" rel="noopener noreferrer"&gt;atomic design principles&lt;/a&gt; finally contains our &lt;code&gt;button&lt;/code&gt; library, the entry point of today's tutorial.&lt;/p&gt;

&lt;p&gt;Let’s see if we wired up our storybook and tailwind config correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's get started!
&lt;/h3&gt;

&lt;p&gt;We create a simple attribute directive that adds a blue background color to our button.&lt;/p&gt;

&lt;p&gt;Create a directory named &lt;code&gt;button&lt;/code&gt; in the src directory.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;button.directive.ts&lt;/code&gt; file in this newly created folder.&lt;/p&gt;

&lt;p&gt;Add the following contents to the file:&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;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;[natButton]&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="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;ButtonDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nl"&gt;Note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;As&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt; &lt;span class="nx"&gt;see&lt;/span&gt; &lt;span class="nx"&gt;we&lt;/span&gt; &lt;span class="nx"&gt;are&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;Angular&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;standalone&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;If&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt; &lt;span class="nx"&gt;are&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;Modules&lt;/span&gt; &lt;span class="nx"&gt;make&lt;/span&gt; &lt;span class="nx"&gt;sure&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;directive&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;declarations&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="nx"&gt;arrays&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will use Angular’s HostBinding decorator &amp;amp; a private &lt;code&gt;twClasses&lt;/code&gt; variable to bind the tailwind utility class &lt;code&gt;bg-sky-500&lt;/code&gt; to our host elements class attribute&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;HostBinding&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;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;[natButton]&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="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;ButtonDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&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&lt;/span&gt;&lt;span class="dl"&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;twClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-sky-500&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;So far so good! Let’s add &lt;code&gt;button.stories.ts&lt;/code&gt; in the same directory and write a simple story to see if storybook renders our new button directive correctly.&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./button.directive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/angular&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&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;Design System/Atoms/Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirective&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirective&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="na"&gt;moduleMetadata&lt;/span&gt;&lt;span class="p"&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&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 natButton&amp;gt;Click&amp;lt;/button&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! You just created your first story following the &lt;a href="https://storybook.js.org/docs/angular/api/csf" rel="noopener noreferrer"&gt;Component Story Format.&lt;/a&gt; Let’s break down the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our default export tells storybook the metadata of our stories. Following storybooks naming convention we create our &lt;em&gt;Button&lt;/em&gt; stories in the &lt;em&gt;Atoms&lt;/em&gt; directory of our &lt;em&gt;Design System&lt;/em&gt; project.&lt;/li&gt;
&lt;li&gt;We then export the &lt;em&gt;Default&lt;/em&gt; story for our ButtonDirective. To configure the story we add the directive to the &lt;code&gt;moduleMetadata&lt;/code&gt; array and the &lt;code&gt;button&lt;/code&gt; element in our template.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets run the &lt;code&gt;npx nx run ui-storybook:storybook&lt;/code&gt; command and see if we get our Click button with the sky blue background.&lt;/p&gt;

&lt;p&gt;We open &lt;code&gt;localhost:4400&lt;/code&gt;, navigate into the Atoms folder and select our Default Button Story.&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%2Fl9nqmnmf0zd9e1pkzj50.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%2Fl9nqmnmf0zd9e1pkzj50.png" alt="Storybook UI displays Click button with blue background" width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! Let's take this button to the next level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making it real.
&lt;/h3&gt;

&lt;p&gt;So far our button is not very impressive. &lt;/p&gt;

&lt;p&gt;So let’s grab some designs from our (imaginary) designer friends and give our directive superpowers. Luckily, I prepared some good looking buttons for you &lt;a href="https://play.tailwindcss.com/DDXuw6Y4ln" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the classes of the primary button and add them to our directive.&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;HostBinding&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;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;[natButton]&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="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;ButtonDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&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&lt;/span&gt;&lt;span class="dl"&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;twClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`inline-flex items-center rounded-md border border-transparent bg-sky-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh your storybook page and see how beautiful our button looks now.&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%2Fmly5mmaukc0plopz110q.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%2Fmly5mmaukc0plopz110q.png" alt="Primary styled button" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Making it smart.
&lt;/h3&gt;

&lt;p&gt;While our button looks great, so far we just copied around a bunch of tailwind classes.&lt;/p&gt;

&lt;p&gt;Time to make our directive smart and support multiple themes and sizes.&lt;/p&gt;

&lt;p&gt;First, let’s split our long twClasses string into smaller groups based on their functionality.&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;HostBinding&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inline-flex items-center rounded-md border font-medium shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-4 py-2 text-sm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-transparent bg-sky-600 text-white hover:bg-sky-700 focus:ring-sky-500&lt;/span&gt;&lt;span class="dl"&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;[natButton]&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="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;ButtonDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&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&lt;/span&gt;&lt;span class="dl"&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;twClasses&lt;/span&gt; &lt;span class="o"&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;base&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;size&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;theme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better! Can you guess where we are going with this?&lt;br&gt;
Let’s add an input property to our directive that lets us change the theme!&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;HostBinding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inline-flex items-center rounded-md border font-medium shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-4 py-2 text-sm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-transparent bg-sky-600 text-white hover:bg-sky-700 focus:ring-sky-500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeSecondary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-gray-300 bg-white text-gray-700 hover:bg-sky-700 focus:ring-gray-50&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&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;secondary&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;buildTwClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&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;size&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;currentTheme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;themeSecondary&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&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="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;[natButton]&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="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;ButtonDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&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&lt;/span&gt;&lt;span class="dl"&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;twClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildTwClasses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&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;_theme&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&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;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Theme&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;_theme&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="nx"&gt;twClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildTwClasses&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;_theme&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 exactly is happening in this code? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We added the &lt;code&gt;theme&lt;/code&gt; input to our directive which takes in a parameter of type &lt;code&gt;Theme&lt;/code&gt;, which is our currently supported themes, &lt;code&gt;primary&lt;/code&gt; and &lt;code&gt;secondary&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;There is a &lt;code&gt;buildTwClasses&lt;/code&gt; helper function that builds the tailwind classes for our buttons based on the &lt;code&gt;currentTheme&lt;/code&gt; we pass in.&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;set&lt;/code&gt; function rebuilds the &lt;code&gt;twClasses&lt;/code&gt; every time the theme input changes and assigns it to our &lt;code&gt;twClasses&lt;/code&gt; property, which is bound to the host's class property.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s manually change our story template to use the secondary theme and see if our code works.&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./button.directive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/angular&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&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;Design System/Atoms/Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirective&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirective&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="na"&gt;moduleMetadata&lt;/span&gt;&lt;span class="p"&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&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 natButton theme="secondary"&amp;gt;Click&amp;lt;/button&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh the page to see if the changes are picked up.&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%2Fdczko9vtv3lbw4vebxng.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%2Fdczko9vtv3lbw4vebxng.png" alt="Storybook UI showing the secondary button style" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They are!! If you got this far you are now equipped with the fundamental knowledge to build beautiful buttons or links that look like buttons or anything you want that looks like buttons. &lt;/p&gt;

&lt;p&gt;However, there are a lot of improvements and enhancements we can add. So if I sparked your interest keep on reading!&lt;/p&gt;

&lt;h3&gt;
  
  
  Making it super smart
&lt;/h3&gt;

&lt;p&gt;So far we have a decent button directive. However, as our design system grows and designers are adding large buttons used in CTA’s of the new version of our app. They also pointed out that there is no visual hint to when a button is disabled. Let’s update our &lt;code&gt;button.directive.ts&lt;/code&gt; and make sure we add some styles for that to our button base.&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;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`inline-flex items-center rounded-md border font-medium shadow-sm
  focus:outline-none focus:ring-2 focus:ring-offset-2
  disabled:opacity-50 disabled:focus:ring-0 disabled:active:ring-0`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we add a new input property called size to our directive.&lt;br&gt;
It accepts inputs of type &lt;code&gt;Size&lt;/code&gt;, which is our supported sizes, &lt;code&gt;base&lt;/code&gt; and &lt;code&gt;l&lt;/code&gt;.&lt;br&gt;
While we are here let’s refactor our Tailwind configurations. &lt;/p&gt;

&lt;p&gt;Instead of toggling between string constants, let's create objects that hold the class strings organized by the &lt;code&gt;Theme&lt;/code&gt; and &lt;code&gt;Size&lt;/code&gt; inputs respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themes&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;primary&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;secondary&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;themes&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeClasses&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;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Theme&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="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-transparent bg-sky-600 text-white hover:bg-sky-700 disabled:bg-sky-600 focus:ring-sky-500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-gray-300 bg-white text-gray-700 hover:bg-gray-50 disabled:bg-white focus:ring-sky-500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sizes&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;base&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;l&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;sizes&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sizeClasses&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;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Size&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="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-4 py-2 text-sm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;l&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-5 py-3 text-base&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;Did you notice that we create those types based on constant string arrays of our supported themes &amp;amp; sizes. As we add support for sizes and themes our types will magically adjust! Super smart!&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;buildTwClasses&lt;/code&gt; function we can now use our inputs as keys to look up the respective classes. Using this approach we can easily add new themes or sizes as our design grows.&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;buildTwClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&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;sizeClasses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;size&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;themeClasses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentTheme&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our directive with all its inputs on configuration now looks 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;Directive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HostBinding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`inline-flex items-center rounded-md border font-medium shadow-sm
  focus:outline-none focus:ring-2 focus:ring-offset-2
  disabled:opacity-50 disabled:focus:ring-0 disabled:active:ring-0`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themes&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;primary&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;secondary&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;themes&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeClasses&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;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Theme&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="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-transparent bg-sky-600 text-white hover:bg-sky-700 disabled:bg-sky-600 focus:ring-sky-500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-gray-300 bg-white text-gray-700 hover:bg-gray-50 disabled:bg-white focus:ring-sky-500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sizes&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;base&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;l&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="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;sizes&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sizeClasses&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;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Size&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="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-4 py-2 text-sm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;l&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-5 py-3 text-base&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;buildTwClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;base&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;sizeClasses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;size&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;themeClasses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentTheme&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="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;[natButton]&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="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;ButtonDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostBinding&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&lt;/span&gt;&lt;span class="dl"&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;twClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildTwClasses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&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;base&lt;/span&gt;&lt;span class="dl"&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;_theme&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&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;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Theme&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;_theme&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="nx"&gt;twClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildTwClasses&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;_theme&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;_size&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="nx"&gt;_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base&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;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Size&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;_size&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="nx"&gt;twClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildTwClasses&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;_theme&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;_size&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;Again, we can manually update our story, checking if our new size input is picked up correctly.&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./button.directive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@storybook/angular&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&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;Design System/Atoms/Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirective&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirective&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="na"&gt;moduleMetadata&lt;/span&gt;&lt;span class="p"&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&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 natButton theme="secondary" size="l"&amp;gt;Click&amp;lt;/button&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect! A large button with our secondary theme is displayed!&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%2F4yg68ojld4nhoxaowkvy.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%2F4yg68ojld4nhoxaowkvy.png" alt="A large button with our secondary theme is displayed" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Leveling up our storytelling.
&lt;/h3&gt;

&lt;p&gt;Right now we are still updating our stories file a lot to preview our changes. Wouldn’t it be great to be able to see all the different button styles update dynamically in our storybook? Let’s make it happen.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;button.stories.ts&lt;/code&gt; file, we create a &lt;code&gt;ButtonDirectiveProps&lt;/code&gt; type that will drive our story controls. It extends the &lt;code&gt;ButtonDirective&lt;/code&gt; with the &lt;code&gt;content&lt;/code&gt; 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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonDirectiveProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ButtonDirective&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then specify that we want a &lt;code&gt;select&lt;/code&gt; control for our size &amp;amp; theme inputs and tell storybook to populate their options with the supported sizes &amp;amp; themes respectively.&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&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;Design System/Atoms/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;argTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;control&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;control&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;themes&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="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirectiveProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we add some default args for our stories. Let’s take the first of each input options and set our content to &lt;em&gt;Angular &amp;amp; Tailwind rock&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DefaultArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sizes&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;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;themes&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Angular &amp;amp; Tailwind rock&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;We then rename our &lt;code&gt;Default&lt;/code&gt; story to &lt;code&gt;Template&lt;/code&gt; and add the args parameter to the function. Our args will be of type &lt;code&gt;ButtonDirectiveProps&lt;/code&gt;, which we extend with our button specific &lt;code&gt;disabled&lt;/code&gt; property and then bind to the stories props. We update our template to bind our angular inputs to the new props we provide. &lt;br&gt;
Lastly, we copy the &lt;code&gt;Template&lt;/code&gt; as described in the &lt;a href="https://storybook.js.org/docs/angular/writing-stories/introduction" rel="noopener noreferrer"&gt;Storybook introduction&lt;/a&gt; and set the stories arguments to our defaults, finally setting the &lt;code&gt;disabled&lt;/code&gt; property to &lt;code&gt;false&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirectiveProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="na"&gt;disabled&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;moduleMetadata&lt;/span&gt;&lt;span class="p"&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&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 natButton [disabled]="disabled" [size]="size" [theme]="theme"&amp;gt;{{content}}&amp;lt;/button&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;const&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="nx"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;DefaultArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;disabled&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s reload our stories at localhost:4400.&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%2Fz9lqpcevu7f9dveiz64b.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%2Fz9lqpcevu7f9dveiz64b.png" alt="Storybook view of button directive" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Did you see the controls appear and our button updating when we change any of them!&lt;/p&gt;

&lt;p&gt;I mentioned that the great thing about directives is that we can apply them to a variety of tags. Let’s see what happens if we add our directive to an anchor tag. &lt;/p&gt;

&lt;p&gt;We copy our Template and Default export. &lt;/p&gt;

&lt;p&gt;Change the disabled property to one called link and rename our Template to Anchor template and our export to Anchor. Lastly, we wire up our link property with the anchor tags href property and add a link to the stories args.&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;AnchorTemplate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Story&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonDirectiveProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;link&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;moduleMetadata&lt;/span&gt;&lt;span class="p"&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;ButtonDirective&lt;/span&gt;&lt;span class="p"&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;a target="_blank" [href]="link" natButton [size]="size" [theme]="theme"&amp;gt;{{content}}&amp;lt;/a&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;const&lt;/span&gt; &lt;span class="nx"&gt;Anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AnchorTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="nx"&gt;Anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;DefaultArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://media.giphy.com/media/jJQC2puVZpTMO4vUs0/giphy.gif&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;Reload your storybook one more time and check out the newly added Anchor story. Click on the anchor disguised as our button and you’ll see that it works as expected.&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%2F1clwwx06b29zop8ckpbw.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%2F1clwwx06b29zop8ckpbw.gif" alt="Congratulations! You made it." width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You made it to the end of this tutorial and built an awesome button directive. And did notice how clean our stories markup was? No Tailwind classes in the markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;This is my first time writing an article like this. So first of all, thanks for reading! I hope you learned something new! What’s next? You decide. Did you enjoy this article? Are you interested in learning about the Storybook setup I use for this project? What UI element should we create next? Would you rather have a video tutorial you can follow along with? Let me know in the comments.&lt;/p&gt;

</description>
      <category>github</category>
      <category>webdev</category>
      <category>portfolio</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
