<?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: Alfredo Perez</title>
    <description>The latest articles on Forem by Alfredo Perez (@alfredoperez).</description>
    <link>https://forem.com/alfredoperez</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%2F165435%2F8c6793dd-5008-434f-9870-d3765c98ff06.jpeg</url>
      <title>Forem: Alfredo Perez</title>
      <link>https://forem.com/alfredoperez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/alfredoperez"/>
    <language>en</language>
    <item>
      <title>Getting Started with Angular Toolbar</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Mon, 02 Feb 2026 15:54:00 +0000</pubDate>
      <link>https://forem.com/alfredoperez/getting-started-with-angular-toolbar-248c</link>
      <guid>https://forem.com/alfredoperez/getting-started-with-angular-toolbar-248c</guid>
      <description>&lt;p&gt;You're building an internal business application. Your QA team needs to test what an Admin sees versus a regular user. Your stakeholder wants to demo the new dashboard that's still behind a feature flag. And IT wants to verify that the "Bulk Export" feature is properly gated to licensed users only.&lt;/p&gt;

&lt;p&gt;Three requests. Normally, that's three database changes, three test accounts, and a lot of context switching.&lt;/p&gt;

&lt;p&gt;Or... you could just use the Angular Toolbar.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/blog/introducing-ngx-dev-toolbar"&gt;previous article&lt;/a&gt;, we introduced ngx-dev-toolbar and why it's useful for internal business applications. Now let's get it running and set up Feature Flags, Permissions, and App Features—the three tools that make testing different scenarios instant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Setup
&lt;/h2&gt;

&lt;p&gt;Initialize the toolbar in your &lt;code&gt;main.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;bootstrapApplication&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isDevMode&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;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="nx"&gt;appConfig&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="nf"&gt;isDevMode&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;initToolbar&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ngx-dev-toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;initToolbar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appRef&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;bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dynamic import ensures zero production bundle impact—the toolbar code only loads in development.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;All three tools work the same way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Register&lt;/strong&gt; your options with &lt;code&gt;setAvailableOptions()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read&lt;/strong&gt; the values with &lt;code&gt;getValues()&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The toolbar stores any overrides in localStorage, so they persist across page reloads. When you read values with &lt;code&gt;getValues()&lt;/code&gt;, you get your original values with any toolbar overrides already applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Flags
&lt;/h2&gt;

&lt;p&gt;Remember that new dashboard your stakeholder wants to demo? That's a feature flag. Let's wire it up.&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;// 📃 feature-flags.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ToolbarFeatureFlagService&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;ngx-dev-toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FeatureFlagsService&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;toolbarService&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;ToolbarFeatureFlagService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 👇 Real flag values from your flag provider (LaunchDarkly, Split, etc.)&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;realFlags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flagProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFlags&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 👇 All flags with toolbar overrides already applied&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolbarService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;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;// Register flags with the toolbar when they load&lt;/span&gt;
    &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flags&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;realFlags&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolbarService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAvailableOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;flags&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;f&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&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="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}))&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="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;getFlag&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="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;isEnabled&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;getValues()&lt;/code&gt; method returns all flags with any toolbar overrides already merged in—no manual merging required.&lt;/p&gt;

&lt;p&gt;Your flag provider might return data in a different format than the toolbar expects. You can map the values when registering:&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;// LaunchDarkly returns: { key: 'dark-mode', value: true }&lt;/span&gt;
&lt;span class="c1"&gt;// Toolbar expects: { id: string, name: string, description: string, isEnabled: boolean }&lt;/span&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolbarService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAvailableOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;flags&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;f&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                              &lt;span class="c1"&gt;// key → id&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;           &lt;span class="c1"&gt;// 'dark-mode' → 'Dark Mode'&lt;/span&gt;
    &lt;span class="na"&gt;description&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;descriptions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;// Add descriptions from a local map&lt;/span&gt;
    &lt;span class="na"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;// value → isEnabled&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1qz54g0sxrzbbsw9lua.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%2Fe1qz54g0sxrzbbsw9lua.png" alt="Feature Flags panel showing the four flags with toggle switches" width="800" height="1109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use it in your components:&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;// 📃 dashboard.component.ts&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardComponent&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;featureFlagsService&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;FeatureFlagsService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;showNewDashboard&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;featureFlagsService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new-dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@if (showNewDashboard()) {
  &lt;span class="nt"&gt;&amp;lt;app-new-dashboard&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @else {
  &lt;span class="nt"&gt;&amp;lt;app-classic-dashboard&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Permissions
&lt;/h2&gt;

&lt;p&gt;Now for that QA scenario—testing what an Admin sees versus a regular user. That's all about permissions.&lt;/p&gt;

&lt;p&gt;Permissions work the same way as feature flags, but with three possible states: original value, forced granted, or forced denied.&lt;/p&gt;

&lt;p&gt;For permissions, you might want more control over how overrides are applied. Instead of &lt;code&gt;getValues()&lt;/code&gt;, you can use &lt;code&gt;getForcedValues()&lt;/code&gt; which returns &lt;strong&gt;only&lt;/strong&gt; the permissions that were overridden in the toolbar:&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;// 📃 permissions.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ToolbarPermissionsService&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;ngx-dev-toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PermissionsService&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;toolbarService&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;ToolbarPermissionsService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 👇 Real permissions from your auth backend&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;realPermissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 👇 Only the permissions overridden in the toolbar&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;forcedPermissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolbarService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getForcedValues&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;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;// Register permissions with the toolbar&lt;/span&gt;
    &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;perms&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;realPermissions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;perms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolbarService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAvailableOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;perms&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;p&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;isGranted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;granted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}))&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;hasPermission&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="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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="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="c1"&gt;// 👇 Check toolbar override first&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;forced&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;forcedPermissions&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forced&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;forced&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isGranted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// 👇 Fall back to real permission&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="nf"&gt;realPermissions&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;granted&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="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 pattern gives you explicit control: real values from your backend, with toolbar overrides layered on top only when set.&lt;/p&gt;

&lt;h3&gt;
  
  
  getValues() vs getForcedValues()
&lt;/h3&gt;

&lt;p&gt;Both methods work, but serve different needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;getValues()&lt;/code&gt;&lt;/strong&gt; — Returns all options with overrides already applied. Simpler to use when you want the toolbar to manage everything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;getForcedValues()&lt;/code&gt;&lt;/strong&gt; — Returns only the options that were manually overridden. Use this when your backend is the source of truth and you want to explicitly layer toolbar overrides on top.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose &lt;code&gt;getValues()&lt;/code&gt; for simpler integrations. Choose &lt;code&gt;getForcedValues()&lt;/code&gt; when you need to preserve the distinction between real values and test overrides.&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%2Fnz4zpzkhstihlmn8zh7u.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%2Fnz4zpzkhstihlmn8zh7u.png" alt="Permissions panel showing permissions with grant/deny/original options" width="800" height="1112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  App Features
&lt;/h2&gt;

&lt;p&gt;And finally, that "Bulk Export" feature IT wants to verify? That's a license-gated app feature.&lt;/p&gt;

&lt;p&gt;App Features are perfect for testing subscription tiers and license-based 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="c1"&gt;// 📃 app-features.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ToolbarAppFeaturesService&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;ngx-dev-toolbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppFeaturesService&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;toolbarService&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;ToolbarAppFeaturesService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 👇 Real features from your licensing backend&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;realFeatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;licensingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFeatures&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolbarService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;features&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;realFeatures&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolbarService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAvailableOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;features&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;f&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FEATURE_METADATA&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&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;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;f&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="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FEATURE_METADATA&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;f&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;description&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="na"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}))&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;isEnabled&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="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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="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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;features&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;isEnabled&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you notice the &lt;code&gt;FEATURE_METADATA&lt;/code&gt; mapping? Your licensing API typically returns just feature IDs and enabled status—no user-friendly names or descriptions. We added both &lt;code&gt;name&lt;/code&gt; (to control how it appears in the toolbar) and &lt;code&gt;description&lt;/code&gt; locally:&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;// 📃 app-features.models.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Feature&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Analytics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;analytics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;BulkExport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bulk-export&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;WhiteLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Notifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ApiAccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api-access&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;const&lt;/span&gt; &lt;span class="nx"&gt;FEATURE_METADATA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Analytics&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Analytics Dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Advanced reporting and visualization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BulkExport&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bulk Export&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Export datasets to CSV or Excel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WhiteLabel&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;White Label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom branding with your logo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Real-time push notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApiAccess&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API Access&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;REST API for integrations&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 keeps your feature definitions type-safe and your descriptions in one place.&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%2Ft6192d57azi1d0tsk7qk.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%2Ft6192d57azi1d0tsk7qk.png" alt="App Features panel showing features with enable/disable toggles" width="800" height="1103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can test what users on different subscription tiers see—without touching the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining for Realistic Scenarios
&lt;/h2&gt;

&lt;p&gt;Here's where it all comes together.&lt;/p&gt;

&lt;p&gt;Remember those three requests from the beginning? With the toolbar set up, you can handle all of them without touching the database. Toggle the new dashboard flag for the demo. Grant "can-admin" permission to simulate an admin user. Enable "Bulk Export" to verify the license gate.&lt;/p&gt;

&lt;p&gt;The real power comes when you combine all three tools. Want to test what an "Admin with Enterprise license" sees? Enable the app features and grant the permissions. Testing a "Regular user on Basic tier"? Adjust accordingly.&lt;/p&gt;

&lt;p&gt;In the next article, we'll see how to save these combinations as Presets—so you can switch between user personas with a single click.&lt;/p&gt;

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

&lt;p&gt;Now that you know how to use the built-in tools, check out the next article on Presets to learn how to save and share your configurations with your team.&lt;/p&gt;

&lt;p&gt;Happy developing!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>devtools</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Spec-Driven Development: Stop Vibe Coding</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Sat, 03 Jan 2026 05:13:20 +0000</pubDate>
      <link>https://forem.com/alfredoperez/spec-driven-development-stop-vibe-coding-af2</link>
      <guid>https://forem.com/alfredoperez/spec-driven-development-stop-vibe-coding-af2</guid>
      <description>&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%2F93dub5vi26nay09yjrhu.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%2F93dub5vi26nay09yjrhu.jpg" alt="Spec-Driven Development Header" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to add a feature to my VS Code extension that highlights the current plan step in the workflow progress bar.&lt;/p&gt;

&lt;p&gt;My first instinct? Just start coding... but what if we can vibe code it? Ok, let's give it a shot. Open Claude Code, type "highlight the current plan step in the workflow progress bar and order the sub-menu so Plan is first," and let the AI figure it out. But then the questions start flooding in: How should the highlighting work visually? Should it persist across sessions? What about the sub-menu ordering—alphabetical or by workflow phase? How does the extension communicate between the sidebar and the WebView?&lt;/p&gt;

&lt;p&gt;Here's the thing: I could just prompt the AI and hope it makes the right assumptions. Or I could spend the next hour going back and forth, correcting misunderstandings, and ending up with something that "works" but isn't quite what I had in mind.&lt;/p&gt;

&lt;p&gt;There's a better way. Let me show you how I approached this differently using &lt;strong&gt;spec-driven development&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is "Vibe Coding"?
&lt;/h2&gt;

&lt;p&gt;You know the feeling. You have a rough idea of what you want to build, you open your AI coding assistant, type something like "Highlight the current step in the progress bar," and hope for the best.&lt;/p&gt;

&lt;p&gt;The AI starts generating code. It looks reasonable. It compiles. But then you realize—it highlighted the wrong element. Or it used inline styles when you wanted CSS classes. Or it assumed the highlighting should be permanent when you wanted it to update dynamically as users navigate.&lt;/p&gt;

&lt;p&gt;So you go back and forth. Re-prompt. Clarify. Debug. Re-prompt again. After an hour of this, you're either frustrated enough to just code it yourself, or you accept something that's "good enough" but not quite right.&lt;/p&gt;

&lt;p&gt;This is vibe coding—jumping straight into implementation without making your decisions explicit. And with AI assistants, it happens faster than ever. As &lt;a href="https://developer.microsoft.com/blog/spec-driven-development-spec-kit" rel="noopener noreferrer"&gt;Microsoft's spec-driven development post&lt;/a&gt; puts it: "Code is inherently a binding artifact—once you write an implementation, it's very hard to decouple from it."&lt;/p&gt;

&lt;p&gt;The consequences compound. You spend &lt;strong&gt;endless hours going back and forth&lt;/strong&gt; fixing misunderstandings. There's &lt;strong&gt;no documentation&lt;/strong&gt; of why decisions were made, so each AI session &lt;strong&gt;starts from scratch&lt;/strong&gt;—it doesn't remember your intent. And if you're working with a team? Good luck. Nobody has &lt;strong&gt;shared context&lt;/strong&gt; about what you were actually trying to build.&lt;/p&gt;

&lt;p&gt;If you want to see this problem explained in more detail, check out &lt;a href="https://www.youtube.com/watch?v=a9eR1xsfvHg" rel="noopener noreferrer"&gt;Dan Delamarski's demo of spec-kit&lt;/a&gt; where he walks through the full workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Spec-Driven Development
&lt;/h2&gt;

&lt;p&gt;Spec-driven development flips the script. Instead of jumping to code, you first make your intent explicit through specifications. These specs become executable artifacts that directly generate implementations.&lt;/p&gt;

&lt;p&gt;The core principle: &lt;strong&gt;Define what and why before how.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what changes: the AI finally &lt;strong&gt;understands your intent&lt;/strong&gt; because it's working from detailed specifications, not vague prompts. You get &lt;strong&gt;fewer iterations&lt;/strong&gt; to reach the correct implementation. Your specs become &lt;strong&gt;living documentation&lt;/strong&gt; that evolves with your project. Teams can &lt;strong&gt;coordinate through shared, reviewable specs&lt;/strong&gt;—and new team members can onboard by reading them instead of reverse-engineering your code.&lt;/p&gt;

&lt;p&gt;Think of it as "version control for architectural thinking."&lt;/p&gt;

&lt;p&gt;Tools like &lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; have built their entire IDE around this concept—transforming natural language prompts into detailed specifications, then into functional code, documentation, and tests. GitHub's &lt;a href="https://github.com/github/spec-kit" rel="noopener noreferrer"&gt;spec-kit&lt;/a&gt; brings this methodology to any AI coding assistant you already use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Let me walk you through setting up spec-driven development and then show you exactly how I used it to build the plan step highlight feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install Prerequisites
&lt;/h3&gt;

&lt;p&gt;You'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.11+&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv package manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;An AI coding assistant (&lt;a href="https://claude.com/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, GitHub Copilot, Gemini CLI, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Install spec-kit CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv tool &lt;span class="nb"&gt;install &lt;/span&gt;specify-cli &lt;span class="nt"&gt;--from&lt;/span&gt; git+https://github.com/github/spec-kit.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details, check out the &lt;a href="https://github.com/github/spec-kit" rel="noopener noreferrer"&gt;spec-kit GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Install SpecKit Companion (VS Code Extension)
&lt;/h3&gt;

&lt;p&gt;This extension gives you a visual interface for the spec-driven workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open VS Code Extensions&lt;/li&gt;
&lt;li&gt;Search for "SpecKit Companion"&lt;/li&gt;
&lt;li&gt;Install the extension&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or visit: &lt;a href="https://marketplace.visualstudio.com/items?itemName=alfredoperez.speckit-companion" rel="noopener noreferrer"&gt;SpecKit Companion on Marketplace&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Initialize Your Project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;specify init my-project &lt;span class="nt"&gt;--ai&lt;/span&gt; claude
&lt;span class="c"&gt;# Or in an existing project:&lt;/span&gt;
specify init &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--here&lt;/span&gt; &lt;span class="nt"&gt;--ai&lt;/span&gt; claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spec-kit supports 15+ AI assistants including Claude Code, GitHub Copilot, Gemini CLI, Cursor, and more.&lt;/p&gt;

&lt;p&gt;I'll be using Claude Code for this demo, but the workflow is the same regardless of which AI you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spec-Driven Workflow: Building Plan Step Highlight
&lt;/h2&gt;

&lt;p&gt;Now let's see this in action. I'll walk through each phase of the spec-driven workflow as I build the plan step highlight feature for my VS Code extension. Watch how SpecKit Companion visualizes each step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Constitution
&lt;/h3&gt;

&lt;p&gt;Before writing any specs, I establish the project's governing principles. The constitution defines non-negotiable constraints that anchor all future decisions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/speckit.constitution Create principles for SpecKit Companion focused on:
- TypeScript with strict mode enabled
- VS Code Extension API best practices
- WebView communication via message passing
- State updates within 100ms of navigation
- Must integrate with existing workflow editor architecture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates &lt;code&gt;.specify/memory/constitution.md&lt;/code&gt; with my project principles.&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%2F9oim0peds852td06hihv.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%2F9oim0peds852td06hihv.png" alt="Constitution Setup" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why does this matter? Every spec I write from now on will reference these principles. When the AI plans the highlight feature, it knows to use proper message passing between extension and WebView, keep state updates performant, and follow the existing architecture patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Specify
&lt;/h3&gt;

&lt;p&gt;Now I define &lt;strong&gt;what&lt;/strong&gt; I want to build—without making technical decisions yet.&lt;/p&gt;

&lt;p&gt;In the SpecKit Companion sidebar, I click the &lt;strong&gt;"+"&lt;/strong&gt; button to create a new spec. This opens the "Create New Spec" dialog where I describe what I want:&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%2Fmbfkv1zsmbg5c8kawhvu.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%2Fmbfkv1zsmbg5c8kawhvu.png" alt="Create New Spec Dialog" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the &lt;strong&gt;Attach Image&lt;/strong&gt; button at the bottom—I can add screenshots or mockups to give the AI visual context. This is powerful when describing UI changes.&lt;/p&gt;

&lt;p&gt;I write my description: "We need to make sure that when a sub-section of the plan is opened, the plan step is highlighted. Also, we need to show in the sub-menu inside the plan step that the plan is the first menu option and then the other ones ordered alphabetically."&lt;/p&gt;

&lt;p&gt;Notice what I'm NOT saying: no mention of CSS classes, message passing implementation, or state management patterns. Just the &lt;strong&gt;what&lt;/strong&gt; and &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I click &lt;strong&gt;Submit to AI&lt;/strong&gt;, and the extension generates the specification. The Workflow Editor opens showing my spec in Phase 1 of 4:&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%2Fuukaxbep9lu5mzifltku.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%2Fuukaxbep9lu5mzifltku.png" alt="Workflow Editor - Spec Phase" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 4-phase progress bar at the top shows where I am: &lt;strong&gt;Spec&lt;/strong&gt; → Plan → Tasks → Done.&lt;/p&gt;

&lt;p&gt;If I need to refine the spec, I can click the &lt;strong&gt;Clarify&lt;/strong&gt; button to ask questions like "Should the highlight persist when switching between tabs?" The AI will help resolve ambiguities.&lt;/p&gt;

&lt;p&gt;When I'm satisfied, I click &lt;strong&gt;Approve &amp;amp; Next Phase&lt;/strong&gt; to move to the Plan phase.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Alternatively&lt;/strong&gt;, you can create specs from the terminal with &lt;code&gt;/speckit.specify&lt;/code&gt; followed by your description.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Phase 3: Plan
&lt;/h3&gt;

&lt;p&gt;After clicking &lt;strong&gt;Approve &amp;amp; Next Phase&lt;/strong&gt;, the extension automatically advances to the Plan phase. Now I make the technical decisions—this is where I specify &lt;strong&gt;how&lt;/strong&gt; to implement the feature.&lt;/p&gt;

&lt;p&gt;The Workflow Editor shows the generated Implementation Plan. Notice the tabs at the top—the Plan phase includes multiple sub-sections:&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%2F4eg9aflk6aa1azs0p6z4.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%2F4eg9aflk6aa1azs0p6z4.png" alt="Workflow Editor - Plan Phase" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan&lt;/strong&gt; — Summary and Technical Context (language, dependencies, testing approach)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Model&lt;/strong&gt; — Data structures and schemas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quickstart&lt;/strong&gt; — Quick reference for getting started&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research&lt;/strong&gt; — Background research and references&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The plan includes architecture decisions, dependencies, and testing strategy. Notice how it references my constitution—it confirms that message passing aligns with "WebView communication" and performance targets.&lt;/p&gt;

&lt;p&gt;If I want to validate the plan, I can click the &lt;strong&gt;Checklist&lt;/strong&gt; button to generate validation checklists—like "unit tests for English" that verify my requirements are complete and consistent.&lt;/p&gt;

&lt;p&gt;When the plan looks good, I click &lt;strong&gt;Go to Next Phase&lt;/strong&gt; to proceed to Tasks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Alternatively&lt;/strong&gt;, you can generate plans from the terminal with &lt;code&gt;/speckit.plan&lt;/code&gt; followed by your technical decisions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Phase 4: Tasks
&lt;/h3&gt;

&lt;p&gt;The Tasks phase opens automatically after approving the plan. The extension generates &lt;code&gt;.specify/specs/001-plan-step-highlight/tasks.md&lt;/code&gt;—a breakdown with &lt;strong&gt;ordered tasks and dependencies&lt;/strong&gt;, markers for tasks that can run in parallel, and a &lt;strong&gt;test-driven development structure&lt;/strong&gt; built right in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogua9mx7nvlpemofqtrm.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%2Fogua9mx7nvlpemofqtrm.png" alt="Workflow Editor - Tasks Phase" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tasks are organized by phases (Setup, Core Implementation, etc.) with clear dependencies. The format shows which tasks can run in parallel and which user story each task belongs to.&lt;/p&gt;

&lt;p&gt;Before implementing, I can click the &lt;strong&gt;Analyze&lt;/strong&gt; button to run a cross-artifact consistency check—this validates that my spec, plan, and tasks all align properly.&lt;/p&gt;

&lt;p&gt;The tasks are editable. If I want to reorder something or add a task, I click &lt;strong&gt;Edit Source&lt;/strong&gt; and modify the markdown directly.&lt;/p&gt;

&lt;p&gt;When ready, I click &lt;strong&gt;Approve &amp;amp; Next Phase&lt;/strong&gt; to complete the workflow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Alternatively&lt;/strong&gt;, you can generate tasks from the terminal with &lt;code&gt;/speckit.tasks&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Phase 5: Implement
&lt;/h3&gt;

&lt;p&gt;After clicking &lt;strong&gt;Approve &amp;amp; Next Phase&lt;/strong&gt; in Tasks, the workflow advances to the Done phase—all four circles in the progress bar are now checked. My spec, plan, and tasks are ready for implementation.&lt;/p&gt;

&lt;p&gt;From here, I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copy the tasks&lt;/strong&gt; to paste into my AI assistant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open in Claude Code&lt;/strong&gt; (or your preferred AI) directly from the extension&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run &lt;code&gt;/speckit.implement&lt;/code&gt;&lt;/strong&gt; in the terminal to let the AI execute the plan systematically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI works through each task in order, respecting dependencies. Because it has full context from the spec and plan, it knows exactly what I want and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;Here's what the &lt;code&gt;.specify&lt;/code&gt; folder looks like after going through the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.specify/
├── memory/
│   └── constitution.md      # Project principles
└── specs/
    └── 001-plan-step-highlight/
        ├── spec.md          # Requirements (what/why)
        ├── plan.md          # Technical strategy (how)
        ├── tasks.md         # Implementation roadmap
        ├── data-model.md    # Data structures
        ├── quickstart.md    # Quick reference
        └── research.md      # Background research
&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%2Fhk9p3yc2kink2dih82rl.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%2Fhk9p3yc2kink2dih82rl.png" alt="Project Structure" width="754" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key benefit? &lt;strong&gt;All files are editable markdown&lt;/strong&gt;—you're not locked into AI-generated content. They're &lt;strong&gt;version control friendly&lt;/strong&gt;, so you can branch and iterate on specs before implementing. Your team can &lt;strong&gt;review and contribute&lt;/strong&gt; to specs. And if requirements change, you just &lt;strong&gt;update the spec and re-run&lt;/strong&gt;—no starting from scratch.&lt;/p&gt;

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

&lt;p&gt;Compare this to vibe coding:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vibe Coding&lt;/th&gt;
&lt;th&gt;Spec-Driven&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Highlight the step" → hope AI understands&lt;/td&gt;
&lt;td&gt;Explicit spec defines exactly what highlighting means&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5-10 iterations to get it right&lt;/td&gt;
&lt;td&gt;1-2 refinements with clear context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No documentation&lt;/td&gt;
&lt;td&gt;Living docs in &lt;code&gt;.specify/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next session starts from scratch&lt;/td&gt;
&lt;td&gt;Specs persist and evolve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"It works... I think?"&lt;/td&gt;
&lt;td&gt;Implementation matches verified spec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  SpecKit Companion: More Than Workflow
&lt;/h2&gt;

&lt;p&gt;The extension does more than guide you through specs. The sidebar gives you quick access to everything in your project:&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%2Fpfrql69hcqnvfm83i5yb.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%2Fpfrql69hcqnvfm83i5yb.png" alt="SpecKit Companion Sidebar" width="800" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Specs&lt;/strong&gt; — Browse all your specifications with their plan and tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steering&lt;/strong&gt; — Access your constitution, project rules, and shared scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; — Configure user and project-level AI agents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt; — Manage MCP servers for extended capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt; — Set up Claude Code hooks for automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll dive deeper into each of these features in the next article.&lt;/p&gt;

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

&lt;p&gt;Spec-driven development transforms how you work with AI. Instead of hoping the AI understands your intent, you make sure it does—with explicit specifications that become living documentation.&lt;/p&gt;

&lt;p&gt;For my plan step highlight feature, I went from a vague idea to a clear spec, a technical plan, and an ordered task list—all before writing a single line of implementation code. When the AI finally implemented the feature, it had full context about what I wanted and why.&lt;/p&gt;

&lt;p&gt;Your specs become the single source of truth. They're reviewable, version-controlled, and collaborative. And when requirements change (they always do), you update the spec and re-run—instead of starting from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to stop vibe coding?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/github/spec-kit" rel="noopener noreferrer"&gt;spec-kit on GitHub&lt;/a&gt; — The CLI toolkit&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=alfredoperez.speckit-companion" rel="noopener noreferrer"&gt;SpecKit Companion&lt;/a&gt; — VS Code extension for visual workflow&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; — AI IDE built around spec-driven development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next up:&lt;/strong&gt; A deep dive into SpecKit Companion's sidebar—agents, skills, hooks, and more.&lt;/p&gt;

</description>
      <category>specdrivendevelopment</category>
      <category>aicoding</category>
      <category>speckit</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Universal Knowledge Base for AI</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Sat, 03 Jan 2026 05:12:37 +0000</pubDate>
      <link>https://forem.com/alfredoperez/universal-knowledge-base-for-ai-432g</link>
      <guid>https://forem.com/alfredoperez/universal-knowledge-base-for-ai-432g</guid>
      <description>&lt;p&gt;Okay, tell me if this sounds familiar: You're working on a new project and need to add a page, but suddenly you don't know what patterns to follow. You check the closest page to see how it's structured and verify when it was created, and the code feels dated with outdated patterns. So you look for another page that was recently modified in one of the current PRs, and you notice that some code looks modern, and some looks like legacy code. Now you don't know what pattern or practice to follow because maybe the latest page is just the result of copy-pasting from other pages. It's like a collage of coding patterns and practices.&lt;/p&gt;

&lt;p&gt;Here's another example that might sound familiar: What if you use Cursor and you've set up your Cursor rules, and it knows how to create components using Angular's modern features, but when you try the same with Claude Code, it doesn't follow any of the rules you already set up? What if you use VS Code and you have to set up &lt;a href="https://code.visualstudio.com/docs/copilot/customization/custom-instructions" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;? or what if your team uses five different AI Tools?&lt;/p&gt;

&lt;p&gt;Well, if you've been in this situation, that's why I decided to create a &lt;strong&gt;knowledge base for AI&lt;/strong&gt; that can set our patterns and style guides, share how we configure AI tools, and document all the patterns a company or project needs to follow. This knowledge base needs to be universal—document once, use everywhere, whether you're working with Cursor, Claude Code, or collaborating with your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do we need it?
&lt;/h3&gt;

&lt;p&gt;Okay, so if you still need convincing, let's talk about three reasons why it's important to have this knowledge base specialized for AI.&lt;/p&gt;

&lt;h4&gt;
  
  
  Share Knowledge That Actually Gets Used
&lt;/h4&gt;

&lt;p&gt;I've been a big proponent of documentation as a great way to share knowledge between teams. As your team grows and practices evolve, you can share recipes, tips, and tricks. This makes it super fast for developers to start developing and focus on bigger problems (how to make customers happier or create better product features) instead of figuring out how to build a page, table, or dialog again.&lt;/p&gt;

&lt;p&gt;This is the first reason why I think it's important to have this knowledge base for AI — it's not only used by AI but also shared with all team members. Good for onboarding and standardization.&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%2F4bim7une3fuf92ss4q0p.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%2F4bim7une3fuf92ss4q0p.png" alt="Knowledge sharing diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Stop Recreating AI Tool Configuration
&lt;/h4&gt;

&lt;p&gt;Another reason is that there are a lot of AI tools and if we configure or write agents for one tool, we have to recreate the same strategy in others if we want consistency. By centralizing how we create an agent that checks stability (just an example), we can reuse it in both tools. It doesn't matter what format each tool needs—we just point to the document that has the details.&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%2Fch5ia7pjieheznwusp63.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%2Fch5ia7pjieheznwusp63.png" alt="AI tool configuration reuse" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Allow Developers to Choose Their Workflow
&lt;/h4&gt;

&lt;p&gt;We want to allow developers to try different workflows without forcing them into one approach. You might think, "Well, we can just commit the .claude folder or .cursor folder to GitHub, and that's how they can reuse agents and rules." But moving the knowledge base to a separate folder lets developers choose their own way to use AI tools without being locked into a specific approach.&lt;/p&gt;

&lt;p&gt;Here's the difference: If we create a shared agent that does Angular development one way and put it directly into the &lt;code&gt;.claude/agents&lt;/code&gt; folder, developers have to either use it as-is or create a PR to request changes to that shared configuration.&lt;/p&gt;

&lt;p&gt;But if we put all the recommended agents, commands, and AI modes in a separate knowledge base, every developer can pick and choose what they need to create their own workflow. They're not stuck with our decisions — they can mix and match based on their preferences.&lt;/p&gt;

&lt;p&gt;Also, since AI tooling is changing daily, keeping AI configuration folders out of the main repo allows developers to download and experiment with new tools without risking interference with anyone else's setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we create a Universal Knowledge Base?
&lt;/h3&gt;

&lt;p&gt;Well, now to get this working, we'll create a knowledge base folder in your project with three major sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Code Style&lt;/strong&gt; — Framework and language patterns&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Project&lt;/strong&gt; — Project-specific rules and guides&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Tools&lt;/strong&gt; — AI tool configurations and templates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key is creating specialized, focused files rather than monolithic ones. We don't want one massive file that covers everything from creating components to unit tests to building pages with tables. Instead, we want targeted files that are concrete, concise, and reusable in multiple contexts. These files should be token-efficient and well-formatted for AI consumption (using Markdown and XML tags).&lt;/p&gt;

&lt;p&gt;This modular approach gives us flexibility. For example, we can create a comprehensive agent that knows everything about Angular for complex tasks but also build a lightweight Claude Code command that only reviews component files and just needs a small subset of those specialized files.&lt;/p&gt;

&lt;p&gt;The beauty is in the mix-and-match capability—each file serves a specific purpose, and you can combine them however makes sense for the task at hand.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code Style
&lt;/h4&gt;

&lt;p&gt;The code style folder is for all your framework patterns—guides, rules, and definitions that work across projects. If two projects share Angular, TypeScript, or Jest, they can share this knowledge base. Think about organizing this the way your mind categorizes development work. Angular patterns live in one folder, testing strategies in another, and component libraries get their own space.&lt;/p&gt;

&lt;p&gt;The key is &lt;strong&gt;file granularity&lt;/strong&gt;. When you're building a component creation agent, it doesn't need state management or directive context—just focused component standards. I structure each domain with a core foundation document plus specialized task files. &lt;code&gt;angular/angular-core.md&lt;/code&gt; establishes your fundamental philosophy and shared patterns, while &lt;code&gt;angular/components-guide.md&lt;/code&gt; focuses purely on component creation.&lt;/p&gt;

&lt;p&gt;This approach lets you create surgical workflows instead of feeding every AI tool your entire knowledge base. A component expert gets exactly the context it needs—no token waste, no cognitive overload, just precise guidance that produces consistent results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code-style/
  angular/
    angular-core.md        # Angular setup and conventions
    components-guide.md    # Component patterns
    services-guide.md      # Service patterns
  ngrx/                    # NgRx setup and conventions
    ngrx-core.md           # Main rules on how ngrx is used
  scss-guide.md            # Styling conventions
  testing-guide.md         # Unit testing
  e2e-testing-guide.md     # E2E strategies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Project
&lt;/h4&gt;

&lt;p&gt;The project folder contains the specific patterns and conventions that make your application unique. While Angular fundamentals stay consistent across projects, each application develops its own approach to common problems. This folder documents how we've chosen to solve them and why.&lt;/p&gt;

&lt;p&gt;This becomes crucial for team consistency. New developers can understand how we build table pages, implement pagination, or handle data exports without digging through existing code to figure out our approach. Instead of reverse-engineering patterns from scattered examples, they get direct guidance on our component library structure, usage conventions, and the solutions we've refined through experience.&lt;/p&gt;

&lt;p&gt;When AI generates new code, this knowledge ensures it follows our established patterns rather than creating components that might work but don't align with our standards. The AI understands exactly how we structure pages, organize components, and handle common scenarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project/
  core/
    technical-stack.md      # Tech decisions &amp;amp; versions
    architecture.md         # System design principles
    deployment-flow.md      # How code reaches production
  patterns/
    page-structure.md       # Standard page layout
    component-hierarchy.md  # How components relate
    data-flow.md            # State management approach
    error-handling.md       # Consistent error patterns
  recipes/
    table-with-filters.md   # Complete table implementation
    form-validation.md      # Form patterns &amp;amp; validation
    modal-dialogs.md        # Modal best practices
    api-integration.md      # Service layer patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Tools
&lt;/h4&gt;

&lt;p&gt;The Tools folder tackles a real workflow problem around sharing AI setups without forcing them on everyone. This folder contains agent templates, commands, and configurations that team members can choose to adopt, rather than putting them into the repository.&lt;/p&gt;

&lt;p&gt;Here's the issue we're solving. Claude Code creates agents by adding files to &lt;code&gt;.claude/agents/&lt;/code&gt;, but committing these to your repo means everyone gets the same AI setup whether they want it or not. That's like forcing everyone to use identical workflows by having the same set of agents, commands, etc.&lt;/p&gt;

&lt;p&gt;Instead, we store suggested configurations that teammates can pick and choose from. Want our Angular expert agent? Grab the &lt;code&gt;tools/agents/angular-expert-agent.md&lt;/code&gt;. Prefer building your own? Start with our templates and customize.&lt;/p&gt;

&lt;p&gt;These configurations get powerful when they reference your existing guides. An Angular expert agent pulls from &lt;code&gt;code-style/angular/core.md&lt;/code&gt;. A component creator combines the Angular core &lt;code&gt;code-style/angular/core.md&lt;/code&gt; with your component patterns &lt;code&gt;code-style/angular/component-guide.md&lt;/code&gt;. A table generator uses your table recipes. You're not duplicating content—you're creating smart entry points into your knowledge base.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tools/
  agents/
    angular-expert.md      # Full Angular agent
    component-creator.md   # Component specialist
    test-writer.md         # Test generator
    pr-reviewer.md         # Code reviewer
  commands/
    quick-fixes.md         # Common fixes
    refactoring.md         # Refactor commands
  modes/
    plan-mode.md           # Cursor Mode to start planning
  hooks/
    pre-commit.md          # Claude Code hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Folder Structure Example
&lt;/h4&gt;

&lt;p&gt;Here's the full folder structure bringing it all together:&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%2F12dqlnnq6id6nn2rma2w.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%2F12dqlnnq6id6nn2rma2w.png" alt="Folder structure diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;knowledge-base/
  core.md                    # Index and overview

  code-style/
    angular/
      core.md                # Angular fundamentals
      components.md          # Component patterns
      services.md            # Service patterns
      directives.md          # Custom directives
      pipes.md               # Custom pipes
      ngrx.md                # State management
    html.md                  # HTML standards
    scss.md                  # Styling conventions
    typescript.md            # TS best practices
    testing.md               # Unit testing
    integration-testing.md   # Integration tests
    e2e-testing.md           # E2E strategies
    documentation.md         # Doc standards

  project/
    core/
      technical-specs.md     # Tech stack
      architecture.md        # System design
      folder-structure.md    # Project organization
      environments.md        # Dev/staging/prod
    code-style/
      design-system.md       # UI library
      shared-components.md   # Reusables
      data-selectors.md      # Test selectors
      mock-apis.md           # API mocking
      feature-flags.md       # Feature toggles
    guides/
      create-table-page.md   # Table pattern
      implement-sidebar.md   # Navigation
      form-validation.md     # Complex forms
      error-handling.md      # Error patterns
      auth-flow.md           # Authentication

  tools/
    agents/
      angular-expert.md      # Full Angular dev
      component-creator.md   # Components only
      test-writer.md         # Test generation
      pr-reviewer.md         # Code review
      refactoring.md         # Code cleanup
      bug-fixer.md           # Debug specialist
    commands/
      quick-fixes.md         # Common fixes
      generate-crud.md       # CRUD operations
    modes/
      architecture-mode.md   # System design
      learning-mode.md       # Onboarding
    hooks/
      pre-commit.md          # Validation
      post-generate.md       # After generation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this knowledge base lives in your repository, developers gain genuine workflow autonomy. If I want to create a Claude agent, I can build it in my own workspace and reference whatever combination of guides makes sense for my approach. Maybe I use the suggested Angular expert configuration as-is, or maybe I craft my own version that pulls from specific guides and adds my personal development preferences.&lt;/p&gt;

&lt;p&gt;This flexibility transforms how teams approach AI-assisted development. Instead of one-size-fits-all configurations, each developer can curate their AI toolkit while still building on shared institutional knowledge. The knowledge base becomes the foundation, but the workflows remain personal and adaptable.&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%2Fzfxq76oqc4908ljqn5rc.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%2Fzfxq76oqc4908ljqn5rc.png" alt="Workflow flexibility diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully I've convinced you of the benefits of having a universal knowledge base for your AI that lives outside individual tool configurations. This approach helps not just AI, but your entire team—they get written documentation on how things should be implemented, and you can reuse these documents across multiple tools. In a landscape where new AI tools keep emerging, &lt;em&gt;having the freedom to choose and experiment with different workflows becomes crucial&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The three-folder structure covers this spectrum from general to specific: framework patterns in code style, project conventions, and tool-specific configurations. Each layer builds on the previous one, creating a knowledge hierarchy that scales with your needs.&lt;/p&gt;

&lt;p&gt;Try it out, start small, and most importantly, &lt;strong&gt;make it fit your team's actual workflows and needs.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Links
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://github.com/hesreallyhim/awesome-claude-code" rel="noopener noreferrer"&gt;awesome-claude-code on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://claude.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://code.visualstudio.com/docs/copilot/customization/custom-instructions" rel="noopener noreferrer"&gt;VS Code Custom Instructions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://cursor.directory/" rel="noopener noreferrer"&gt;cursor.directory&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://angular.dev/ai/develop-with-ai" rel="noopener noreferrer"&gt;Angular AI Development Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://angular.dev/ai/mcp" rel="noopener noreferrer"&gt;Angular MCP&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>angular</category>
      <category>claudecode</category>
      <category>cursoride</category>
    </item>
    <item>
      <title>Create Reliable Unit Tests with Claude Code</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Fri, 02 Jan 2026 17:46:43 +0000</pubDate>
      <link>https://forem.com/alfredoperez/create-reliable-unit-tests-with-claude-code-4e8p</link>
      <guid>https://forem.com/alfredoperez/create-reliable-unit-tests-with-claude-code-4e8p</guid>
      <description>&lt;p&gt;So you're using &lt;a href="https://docs.anthropic.com/en/docs/claude-code/sdk/sdk-overview" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; to generate tests and it's creating 45 test files that check if &lt;code&gt;setTimeout&lt;/code&gt; works. Yeah, that's not what we want. And that's in the best-case scenario.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the worst case?&lt;/em&gt; The tests don't even run. Claude might use Jasmine instead of Jest even though your whole project uses Jest. Or it creates mock files for every single service, every component, every dependency—turning your test folder into a maintenance nightmare. Or tests that mock &lt;code&gt;HttpClient&lt;/code&gt; just to verify that… &lt;code&gt;HttpClient&lt;/code&gt; makes HTTP requests.&lt;/p&gt;

&lt;p&gt;At this point, The AI is not useful because it is basically testing the framework instead of your application logic or creating a bunch of useless tests; in a few words, this is just AI Slop.&lt;/p&gt;

&lt;p&gt;… But…BUT…here's the thing—with the right setup, you can transform that mess into a few focused, behavior-driven tests that actually matter.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Does This Happen?
&lt;/h4&gt;

&lt;p&gt;The thing is, AI doesn't understand your codebase's context. It sees a component and thinks, "I should test everything!" So it tests that observables are observables. It verifies that Angular's dependency injection works. It checks if &lt;code&gt;setTimeout&lt;/code&gt; actually sets a timeout. You know, things that the framework authors already tested for us.&lt;/p&gt;

&lt;p&gt;And the mocks? Oh boy. Every single test creates its own mock from scratch. No sharing, no reusability. You end up with test files that are three times longer than the actual component you're testing. A 50-line component gets a 200-line test file filled with mock setup that's copy-pasted everywhere.&lt;/p&gt;

&lt;p&gt;The AI also has this weird obsession with quantity. It thinks more tests equal better coverage, so you get 40+ tests for a simple form component. Most of them are testing edge cases that will never happen or that are already handled in other services or components. Meanwhile, the actual user flow—the thing that matters—gets buried in all that noise.&lt;/p&gt;

&lt;p&gt;But here's the worst part: these tests are fragile. They're testing how your code works, not what it does.&lt;/p&gt;

&lt;p&gt;Change a variable name? Tests break.&lt;br&gt;
Extract a method? Tests break.&lt;br&gt;
Look at your code the wrong way? You guessed it—tests break.&lt;/p&gt;

&lt;p&gt;Refactoring and maintaining tests becomes a nightmare because you're not just updating your code; you're rewriting half your tests. and you might think, "well… we can just tell AI to refactor them," and yes, that is what we should do but if we tell it the how and why, it can create meaningful tests even in a way that we can reuse mocks or fake data for other tests or even StoryBook or Integration tests.&lt;/p&gt;
&lt;h4&gt;
  
  
  Teach Claude Code Your Team's Testing Style
&lt;/h4&gt;

&lt;p&gt;Here's where it gets interesting. Instead of fighting with Claude Code's default behavior, we're going to teach it how to test like your team does.&lt;/p&gt;

&lt;p&gt;Think of it like this: when a developer joins your team—whether they're senior, mid-level, or just starting out—they need to learn how your codebase works. How do you structure tests? What do you mock? What patterns do you follow? This usually happens through pair programming, code reviews, and a lot of talking, but that won't work with Claude Code. Instead, we'll write up all that information in guides that AI and even developers can use.&lt;/p&gt;

&lt;p&gt;The approach is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create testing guides&lt;/strong&gt; that document your team's patterns&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Put them where Claude Code (and your team) can find them&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use them consistently&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep refining them&lt;/strong&gt; when you spot new patterns or issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As mentioned before, the cool thing about this is that these guides don't just help AI or Claude Code; they also become your team's testing documentation. Any team member, whether new or one that has been in the company for a while, can read them to understand your testing philosophy, making code reviews easier because everyone is on the same page, and yeah, Claude code can generate better tests too or it can be used just to review your tests.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up Your Testing Guides
&lt;/h3&gt;
&lt;h4&gt;
  
  
  The Basic Structure
&lt;/h4&gt;

&lt;p&gt;The first thing to do is that we're going to put all this under a &lt;code&gt;knowledge-base&lt;/code&gt; folder with a &lt;code&gt;testing&lt;/code&gt; folder inside, and here is the basic structure that you will have in this directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;knowledge-base/testing/
├── testing-core.md                #Foundation rules and tech stack
├── testing-components.md          #File or Type specific patterns
├── testing-services.md
├── ...
└── testing-when-using-store.md    #Special cases patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  testing-core.md
&lt;/h4&gt;

&lt;p&gt;The testing core file is the foundational file for all your testing. Here we will have all the rules that we are going to use for all our tests, and they have to be really clear and concise. This is what we should include in it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;&lt;br&gt;
We start by specifying what the tech stack used in your application is. This can include the testing framework you are using, either Jest or Jasmine, whether you're using any library for mocking, if you're using any state management, or any other aspects related to the tech stack of the application so it knows better how to test it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;#Core Testing Guidelines&lt;/span&gt;

&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Testing Framework**&lt;/span&gt;: Jest with Angular Testing Library
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Mocking**&lt;/span&gt;: ng-mocks for Angular components/services
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**State Management**&lt;/span&gt;: NgRx with @ngrx/store/testing
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**HTTP Testing**&lt;/span&gt;: Angular HttpClientTestingModule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to test&lt;/strong&gt;&lt;br&gt;
Next, we are going to specify how we want our unit tests to be created. Like you can specify the groupings of the public methods in &lt;code&gt;describe&lt;/code&gt;, test only behaviors or use the SIFERS pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### Test Strategy&lt;/span&gt;
-Group public methods in describe
-Focus on testing the behaviors
-Do not test the internals of the framework or related depndencies
-Use SIFER pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fakes? Mocks? What to Mock and what Not to Mock?&lt;/strong&gt;&lt;br&gt;
In the next section, I like to talk about how we're going to &lt;a href="https://www.youtube.com/watch?v=sbCRbsFs7e8" rel="noopener noreferrer"&gt;fake&lt;/a&gt; data or create mocks. This is interesting because depending on your application or your team, you might have strategies on when to use fake instead of using mocks or when to put the mocks so they can be reusable. This section is where we're going to put all the data related to that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### Mocking Strategy&lt;/span&gt;

&lt;span class="gs"&gt;**DO Mock:**&lt;/span&gt;
-External API services
-Third-party libraries
-Browser APIs (localStorage, etc.)

&lt;span class="gs"&gt;**DON'T Mock:**&lt;/span&gt;
-Angular framework features
-Your own models/interfaces
-Simple utility functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
Finally, we can finish the document by just including a quick example of a testing file so the agent can have better information on what we mean with all the rules.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example: Clean test structure&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UserProfileComponent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Setup&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserProfileComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Fakes (reusable)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userServiceFake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUserServiceFake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Readable tests with BDD naming&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WHEN user data is loaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHOULD display user name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Test focused on behavior, not implementation&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;Here is the complete &lt;code&gt;testing-core.md&lt;/code&gt; we just created. Don't forget that this is just a small template to give you ideas. You should change it to fit your testing plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;#Core Testing Guidelines&lt;/span&gt;

&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Testing Framework**&lt;/span&gt;: Jest with Angular Testing Library
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Mocking**&lt;/span&gt;: ng-mocks for Angular components/services
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**State Management**&lt;/span&gt;: NgRx with @ngrx/store/testing
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**HTTP Testing**&lt;/span&gt;: Angular HttpClientTestingModule

&lt;span class="gu"&gt;### Test Strategy&lt;/span&gt;
-Group public methods in describe
-Focus on testing the behaviors
-Do not test the internals of the framework or related depndencies
-Use SIFER pattern

&lt;span class="gu"&gt;### Mocking Strategy&lt;/span&gt;

&lt;span class="gs"&gt;**DO Mock:**&lt;/span&gt;
-External API services
-Third-party libraries
-Browser APIs (localStorage, etc.)

&lt;span class="gs"&gt;**DON'T Mock:**&lt;/span&gt;
-Angular framework features
-Your own models/interfaces
-Simple utility functions

&lt;span class="gu"&gt;### Example&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
ts&lt;br&gt;
// Example: Clean test structure&lt;br&gt;
describe('UserProfileComponent', () =&amp;gt; {&lt;br&gt;
  // Setup&lt;br&gt;
  let component: UserProfileComponent;&lt;/p&gt;

&lt;p&gt;// Fakes (reusable)&lt;br&gt;
  const userServiceFake = createUserServiceFake();&lt;/p&gt;

&lt;p&gt;// Readable tests with BDD naming&lt;br&gt;
  describe('WHEN user data is loaded', () =&amp;gt; {&lt;br&gt;
    it('SHOULD display user name', () =&amp;gt; {&lt;br&gt;
      // Test focused on behavior, not implementation&lt;br&gt;
    });&lt;br&gt;
  });&lt;br&gt;
});&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The key is keeping this file focused and practical. Don't write a novel—Claude Code needs clear, actionable guidelines it can follow immediately.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Type-Specific Guides
&lt;/h4&gt;

&lt;p&gt;The second type of guides that we need to provide to Claude Code is the type-specific guides. In these guides, we're going to write how to test based on the type of file that we're going to be creating the unit test for. For example, if we want to create a component test, it's going to use something different from creating a test for a service or a utility that doesn't have any dependency injection.&lt;/p&gt;

&lt;p&gt;The reason why we split these guides into multiple is that whenever we use them with Claude Code, we don't have to pass the whole context of how to test everything, and we can create specialized agents or commands that will know how to test that specific file. Here is an example of how to test a component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;#Component Testing Guidelines&lt;/span&gt;

&lt;span class="gu"&gt;## Component Types &amp;amp; Testing Approaches&lt;/span&gt;

&lt;span class="gu"&gt;### Presentational Components (No services, pure display)&lt;/span&gt;
-Use Angular Testing Library directly
-Focus on rendering and user interactions
-Test props/inputs and outputs/events
-Maximum 5-8 tests

&lt;span class="gu"&gt;### Smart Components (Connected to services)&lt;/span&gt;
-Mock external services only
-Test component behavior, not service implementation
-Use fakes for complex service interactions
-Focus on user workflows

&lt;span class="gu"&gt;## Testing Dialogs&lt;/span&gt;
-Don't test dialog opening/closing mechanisms (that's Angular's job)
-Focus on dialog content and user actions
-Mock DialogRef for dialog-specific behavior

&lt;span class="gu"&gt;## Common Mistake to Avoid&lt;/span&gt;
Never test framework internals:
❌ &lt;span class="sb"&gt;`expect(component).toBeTruthy()`&lt;/span&gt;
✅ &lt;span class="sb"&gt;`expect(screen.getByRole('heading')).toHaveTextContent('User Profile')`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Case-Specific Guides
&lt;/h4&gt;

&lt;p&gt;The final type of guide is the case-specific guide. We need these guides because not all the components are created the same way nor have the same complexity. For example, there can be a component that is completely presentational and doesn't have any dependency injection. But then also you can have the case where you have a component that has state management using NgRx and also has dependencies for other services that are related to logging or to metrics, and instead of having to put all this information into the guides for components, we can isolate these case-specific guides to tell Claude what to do for case-by-case situations.&lt;/p&gt;

&lt;p&gt;Another use case of these case-specific guides is to document the testing patterns that we are encountering. Like, for example, if we have a component that uses the user's API service instead of letting Claude Code create the user API with mock data or using ngmocks or whatever it decides at the time, we can tell it the location of the &lt;code&gt;provideFakeUserApi&lt;/code&gt; service that we already created for other components.&lt;/p&gt;

&lt;p&gt;Okay, so far we have created the guides that we're going to use in this case for Claude Code. Just so you know, these guides (since they are created without using anything Claude Code specific) can be reused by any other AI tools that you have. But at this time, I'm going to show you how to use them with Claude Code so they can be easily picked up by it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Agents and Commands in Claude Code
&lt;/h3&gt;

&lt;p&gt;There are two ways that we can use these guides in Claude Code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; By creating an agent&lt;/li&gt;
&lt;li&gt; By creating a slash command&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The benefit of creating an agent is that Claude Code will detect whenever we are about to create a unit test and immediately load the testing agent that has access to all the rules and guides that we just created. The other way that we can use this is by creating commands where we can create a command for testing and pass which component or which file we want to create a unit test for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating an Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creating an agent with Claude Code is super easy because it has a slash command that can help you do it. So the only thing that we're going to do is open Claude Code and then we're going to tell it to use the &lt;code&gt;/agents&lt;/code&gt; and we are going to select the option to &lt;code&gt;Create a new agent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvch8civber1xwmd2vkb.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%2Fzvch8civber1xwmd2vkb.png" alt="Claude Code agents menu" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Select &lt;code&gt;1. Project (.claude/agents/)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Select &lt;code&gt;1. Generate with Claude (recommended)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Give it a small prompt that gives a brief description of the agent objectives and has a reference to the guides we just created
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create an agent that reads my testing guides from knowledge-base/testing and generates focused unit tests.

When generating tests:
1. Read testing-core.md for foundation rules
2. Read the appropriate specialized guide (components, services, etc.)
3. Follow BDD naming conventions (GIVEN/WHEN/SHOULD)
4. Generate maximum 10 tests per file
5. Use established fake patterns
6. Focus on user behavior, not implementation details

Always prioritize readable, maintainable tests over comprehensive coverage.
&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%2Fq0dmh4szbjxg8wiwwc6p.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%2Fq0dmh4szbjxg8wiwwc6p.png" alt="Agent creation prompt" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next you will need to define which tools you want to give the agent access to, and here I usually just give access to &lt;code&gt;All Tools&lt;/code&gt;. Then it will ask for which model you want to use, and here I pick &lt;code&gt;Sonnet&lt;/code&gt;. Finally it will ask for the agent color and here you can pick whatever you like.&lt;/p&gt;

&lt;p&gt;That is it! The agent file will be created at &lt;code&gt;.claude/agents/&lt;/code&gt; and you will get a confirmation like this:&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%2F66p1x7v8o9dur981kh5g.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%2F66p1x7v8o9dur981kh5g.png" alt="Agent creation confirmation" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this agent is created, the next time you want to create a test or Claude Code has a task to create a test, it will automatically use the &lt;code&gt;test-generator-bdd&lt;/code&gt; agent to create it. Here is an example of how I asked to create a test.&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%2Fyn323f3g82wxi95gc6o9.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%2Fyn323f3g82wxi95gc6o9.png" alt="Using the testing agent" width="798" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating Custom Slash Commands
&lt;/h4&gt;

&lt;p&gt;Let's start by defining when you would need a &lt;a href="https://docs.anthropic.com/en/docs/claude-code/slash-commands#custom-slash-commands" rel="noopener noreferrer"&gt;slash command&lt;/a&gt;. If you're using a Claude Code agent, whenever you begin creating a unit test, you can theoretically leverage the agent to generate it using its built-in instructions — in this case, the knowledge base we created for our testing process. However, sometimes you want to ensure the testing follows a reliable pattern or explicitly specify which files need testing. This is where custom slash commands become valuable.&lt;/p&gt;

&lt;p&gt;Slash commands are created using Claude Code itself. The process is straightforward: prompt Claude Code to create a test command, then provide a similar prompt to what you want executed within that command.&lt;/p&gt;

&lt;p&gt;You can even expand this approach to create more sophisticated commands. For example, you could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Pass parameters specifying which files to test&lt;/li&gt;
&lt;li&gt;  Configure the component to use a specific mock API&lt;/li&gt;
&lt;li&gt;  Add any other explicit requirements for your unit testing workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This flexibility makes your testing process more targeted and consistent.&lt;/p&gt;

&lt;p&gt;In the following example, I created a custom command using Claude Code, and this is the slash command that was generated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;focused&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;BDD&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;unit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tests&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;using&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;testing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;guidelines"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Generate unit tests for the specified file using the test-generator-bdd agent.

This command will:
&lt;span class="p"&gt;1.&lt;/span&gt; Read testing guidelines from knowledge-base/testing/
&lt;span class="p"&gt;2.&lt;/span&gt; Analyze the target code
&lt;span class="p"&gt;3.&lt;/span&gt; Generate focused BDD-style unit tests (max 10 per file)
&lt;span class="p"&gt;4.&lt;/span&gt; Follow project testing conventions

Usage: /test &lt;span class="nt"&gt;&amp;lt;file-path&amp;gt;&lt;/span&gt;
Example: /test src/app/users/shared/components/add-user-modal.component.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's make it more useful and add a parameter. One good example would be to test all the files changed in the current branch, and we'll likely call the command like &lt;code&gt;/test --files-changed&lt;/code&gt;. To add this functionality, we need to specify this parameter in the same command file, and the easiest way to do it is to tell Claude to do it, and you'll get something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate focused BDD unit tests for files using testing guidelines&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Generate unit tests using the test-generator-bdd agent.

This command will:
&lt;span class="p"&gt;1.&lt;/span&gt; Read testing guidelines from knowledge-base/testing/
&lt;span class="p"&gt;2.&lt;/span&gt; Analyze the target code
&lt;span class="p"&gt;3.&lt;/span&gt; Generate focused BDD-style unit tests (max 10 per file)
&lt;span class="p"&gt;4.&lt;/span&gt; Follow project testing conventions

Usage:
-Test a specific file: &lt;span class="sb"&gt;`/test &amp;lt;file-path&amp;gt;`&lt;/span&gt;
-Test all changed files in current branch: &lt;span class="sb"&gt;`/test --changed`&lt;/span&gt;

Examples:
-&lt;span class="sb"&gt;`/test src/app/users/shared/components/add-user-modal.component.ts`&lt;/span&gt;
-&lt;span class="sb"&gt;`/test --changed`&lt;/span&gt; (tests all modified .ts files in current branch)

When using &lt;span class="sb"&gt;`--changed`&lt;/span&gt;:
-Compares current branch against main branch
-Only processes TypeScript files (.ts)
-Excludes test files (.spec.ts)
-Generates tests for each changed component/service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Keep refining your rules
&lt;/h3&gt;

&lt;p&gt;Okay, finally! But not least important—to have a successful, reliable way to create unit tests with Claude Code, I think three of the most important things are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Good context usage.&lt;/strong&gt; We're achieving this by creating separate files with our patterns and guides for testing, so we don't contaminate the context by having everything in a single file. We just choose what we need for the specific test we're creating.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Integration with the tools.&lt;/strong&gt; We're achieving this by creating agents and slash commands that we can use to trigger these guides.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Good documentation.&lt;/strong&gt; How we do testing and what patterns we follow. The most important thing here is to actually use these tools as much as possible so we can see what kind of tests they're generating. Once we review those tests, we can make decisions about whether we need to update our rules or what things we need to add. This is all with the goal that in the future, we have a better chance of creating tests the way we want them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are interested in learning about more ways to use AI in your Angular projects, you might be interested in my following articles:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/ngconf/creating-angular-guardrails-a-guide-to-custom-cursor-rules-for-better-code-df5523eafb5d" rel="noopener noreferrer"&gt;&lt;strong&gt;Create Cursor Rules for Angular&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/ngconf/generating-your-first-rules-with-cursor-for-your-angular-project-20a6d013ac9d" rel="noopener noreferrer"&gt;&lt;strong&gt;Generating Your First Rules with Cursor for Your Angular Project&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>angular</category>
      <category>claudecode</category>
      <category>testing</category>
    </item>
    <item>
      <title>Generating Your First Rules with Cursor for Your Angular Project</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Fri, 02 Jan 2026 17:46:27 +0000</pubDate>
      <link>https://forem.com/alfredoperez/generating-your-first-rules-with-cursor-for-your-angular-project-490k</link>
      <guid>https://forem.com/alfredoperez/generating-your-first-rules-with-cursor-for-your-angular-project-490k</guid>
      <description>&lt;p&gt;Building consistent, high-quality Angular applications becomes increasingly challenging as your codebase grows and your team expands. While Cursor IDE's rule system offers powerful automation capabilities, getting started can feel overwhelming when you're staring at a blank &lt;code&gt;.cursor/rules&lt;/code&gt; directory and you might be making the mistake of using Cursor just as a fancy autocomplete.&lt;/p&gt;

&lt;p&gt;In our previous article, we explored the four fundamental rule types that form the backbone of Cursor's automation system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always Rules&lt;/strong&gt;: Global rules that apply to every chat and command, perfect for defining how AI should respond or general behavior patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auto-Apply Rules&lt;/strong&gt;: Rules that automatically activate based on file patterns, ideal for components, services, testing, and other file-specific guidelines&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent-Select Rules&lt;/strong&gt;: Contextual rules that Cursor intelligently chooses based on what you're working on, great for complex scenarios like state management or advanced routing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual Rules&lt;/strong&gt;: Custom-made rules you reference explicitly, useful for unique patterns like feature flags or company-specific conventions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure to read the previous article in case you want to understand more about these rule types:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/ngconf/creating-angular-guardrails-a-guide-to-custom-cursor-rules-for-better-code-df5523eafb5d" rel="noopener noreferrer"&gt;&lt;strong&gt;Creating Angular Guardrails: A Guide to Custom Cursor Rules for Better Code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The challenge many Angular developers face is figuring out &lt;strong&gt;where to start&lt;/strong&gt;. Instead of manually crafting each rule from scratch — trying to figure out naming conventions, folder structures, and frontmatter configurations &lt;strong&gt;&lt;em&gt;What if you could generate in a few minutes a useful starting point made specifically for Angular development?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article introduces a meta-rule approach that provides exactly that foundation. By the end, you'll have a complete rule generation system that understands Angular project structures, automatically organizes rules by functionality, and gives you powerful prompts to generate your initial rule set based on your actual project dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meta Rule Explanation
&lt;/h3&gt;

&lt;p&gt;Before diving into our Angular-specific implementation, let's check two excellent examples that inspired this approach. Understanding these will help you understand why our improved and Angular-focused variation is better for frontend development needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 1—BMAD Method
&lt;/h4&gt;

&lt;p&gt;The BMAD approach, demonstrated in &lt;a href="https://youtu.be/vjyAba8-QA8?si=Ub57pZ0eJsmKl4ho" rel="noopener noreferrer"&gt;this YouTube video&lt;/a&gt; and available on &lt;a href="https://github.com/bmadcode/cursor-custom-agents-rules-generator/blob/main/.cursor/rules/core-rules/rule-generating-agent.mdc" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, provides a comprehensive framework for rule generation.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/vjyAba8-QA8"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The key parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strict Rule Type Definitions&lt;/strong&gt;: Clearly defines the 4 frontmatter types with specific naming conventions (&lt;code&gt;-manual.mdc&lt;/code&gt;, &lt;code&gt;-auto.mdc&lt;/code&gt;, &lt;code&gt;-always.mdc&lt;/code&gt;, &lt;code&gt;-agent.mdc&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Technology-Agnostic Organization&lt;/strong&gt;: Uses folders like &lt;code&gt;core-rules&lt;/code&gt;, &lt;code&gt;ts-rules&lt;/code&gt;, &lt;code&gt;py-rules&lt;/code&gt;, &lt;code&gt;ui-rules&lt;/code&gt; that scale across different tech stacks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comprehensive Template Structure&lt;/strong&gt;: Includes required examples (both valid and invalid), detailed glob pattern guidance, and automated confirmation responses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Built-in Validation&lt;/strong&gt;: Enforces consistent file locations and prevents rule duplication&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here you can find a link to the meta-rule from the BMAD method:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/bmadcode/cursor-custom-agents-rules-generator/blob/main/.cursor/rules/core-rules/rule-generating-agent.mdc" rel="noopener noreferrer"&gt;cursor-custom-agents-rules-generator on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2—Elie Steinbock Method
&lt;/h3&gt;

&lt;p&gt;Elie's approach, featured in &lt;a href="https://www.youtube.com/watch?v=sxqq9Eql6Cc" rel="noopener noreferrer"&gt;this video&lt;/a&gt; with the rule available on &lt;a href="https://github.com/elie222/inbox-zero/blob/main/.cursor/rules/cursor-rules.mdc" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, focuses on simplicity and clarity.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/sxqq9Eql6Cc"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The key parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimal Overhead&lt;/strong&gt;: Provides essential structure without overwhelming complexity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clear Directory Guidelines&lt;/strong&gt;: Simple, straightforward placement rules that are easy to follow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Focus&lt;/strong&gt;: Shows exactly what developers need to know to get started quickly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Human-Readable Format&lt;/strong&gt;: Uses straightforward language that both AI and team members can easily understand&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a link to the meta-rule:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/elie222/inbox-zero/blob/main/.cursor/rules/cursor-rules.mdc" rel="noopener noreferrer"&gt;inbox-zero cursor-rules.mdc on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While both approaches offer valuable insights into creating a meta-rule for rule management, their focus is primarily on general development scenarios. Angular projects have specific patterns—component hierarchies, state management libraries, testing strategies, and styling conventions—that benefit from a more targeted organizational approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor Meta Rule for Angular Development
&lt;/h3&gt;

&lt;p&gt;Building on the strengths of both examples, here's a meta rule specifically designed for Angular development. This rule maintains the robustness of the BMAD approach while incorporating the clarity of Elie's method, but organizes everything around how Angular developers actually think about their projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;essential&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;maintaining&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consistency&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;quality&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;creation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;across&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;codebase.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;It&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;must&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;be&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;followed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;whenever:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(1)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;requests&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;new&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;be&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;created,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(2)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;An&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;existing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;needs&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;modification,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(3)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;asks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;remember&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;certain&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;behaviors&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;patterns,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(4)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Future&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;behavior&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;changes&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;are&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;requested.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;This&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ensures&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;proper&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;organization,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;clear&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;documentation,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;effective&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;application&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;by&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;defining&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;formats,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;naming&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;conventions,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;requirements.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;It's&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;particularly&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;crucial&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;maintaining&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hierarchy,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ensuring&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rules&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;are&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;discoverable&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;by&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;AI,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;preserving&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;effectiveness&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule-based&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;system.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rule&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;fundamental&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;project&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;consistency,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;quality,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;automated&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;assistance&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;effectiveness."&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Rule Generation Guidelines
&lt;/h1&gt;

&lt;p&gt;This document outlines the standard format for creating and organizing all AI-generated rules for this project. Its main goal is to ensure clarity, consistency, and a scalable structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check for existing rules before creating new ones @.cursor/rules&lt;/li&gt;
&lt;li&gt;Update existing rules when possible instead of creating duplicates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Organizational Folders
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Do not separate by library (e.g., do not use a &lt;code&gt;libraries/&lt;/code&gt; folder).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rule files must be located and named using the format: &lt;code&gt;.cursor/rules/{area-folder}/{area-of-focus}-{type}.mdc&lt;/code&gt;. The &lt;code&gt;{area-of-focus}&lt;/code&gt; describes the specific part of the technology or functionality the rule applies to.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.cursor/rules/global/&lt;/code&gt;: Rules that apply to every chat and command. Also, Cursor behavior and rule generation rules&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.cursor/rules/angular/&lt;/code&gt;: Angular framework rules. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For rules about Angular components: &lt;code&gt;.cursor/rules/angular/component-development-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For rules about Angular services: &lt;code&gt;.cursor/rules/angular/services-state-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For broad Angular rules: &lt;code&gt;.cursor/rules/angular/global-standards-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;.cursor/rules/state/&lt;/code&gt;: State management rules. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For NGXS: &lt;code&gt;.cursor/rules/state/ngxs-state-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For TanStack Query: &lt;code&gt;.cursor/rules/state/tanstack-query-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;.cursor/rules/testing/&lt;/code&gt;: Testing rules. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For Jest: &lt;code&gt;.cursor/rules/testing/jest-testing-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For Angular testing: &lt;code&gt;.cursor/rules/testing/testing-standards-auto.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;.cursor/rules/typescript/&lt;/code&gt;: General TypeScript rules. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For error handling: &lt;code&gt;.cursor/rules/typescript/typescript-error-handling-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For style: &lt;code&gt;.cursor/rules/typescript/typescript-style-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;.cursor/rules/styles/&lt;/code&gt;: CSS, SCSS, and styling rules. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For SCSS conventions: &lt;code&gt;.cursor/rules/styles/scss-conventions-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For typography: &lt;code&gt;.cursor/rules/styles/scss-typography-classes-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Metadata
&lt;/h2&gt;

&lt;p&gt;The frontmatter is mandatory and determines rule application. When creating a rule, first make sure what type is it and base on that setup the metadata:&lt;/p&gt;

&lt;h3&gt;
  
  
  Always
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Applies to all chats/commands. e.g. add emojis after reading a rule&lt;/li&gt;
&lt;li&gt;Suffix with &lt;code&gt;-always.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alwaysApply: true&lt;/code&gt; and empty &lt;code&gt;description&lt;/code&gt; and &lt;code&gt;globs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Must be in &lt;code&gt;.cursor/rules/global/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Auto-Apply
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use when the rule applies to files matching a glob.&lt;/li&gt;
&lt;li&gt;Suffix with &lt;code&gt;-auto.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Requires &lt;code&gt;globs&lt;/code&gt; pattern &lt;code&gt;alwaysApply: false&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; is empty&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agent Select
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use when the rules that should be applied occasionally.&lt;/li&gt;
&lt;li&gt;Suffix the file name with &lt;code&gt;-agent.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Requires descriptive &lt;code&gt;description&lt;/code&gt;. &lt;code&gt;alwaysApply: false&lt;/code&gt; and the &lt;code&gt;globs&lt;/code&gt; is empty&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Manual (&lt;code&gt;-manual.mdc&lt;/code&gt;):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This is for rules that should be manually requested by the user&lt;/li&gt;
&lt;li&gt;Suffix with &lt;code&gt;-manual.mdc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alwaysApply: false&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; and &lt;code&gt;globs&lt;/code&gt; should be empty&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Glob Pattern Examples
&lt;/h3&gt;

&lt;p&gt;Use these patterns in the &lt;code&gt;globs&lt;/code&gt; frontmatter field to target specific files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All TypeScript: *.ts&lt;/li&gt;
&lt;li&gt;All Angular Components: *.component.ts&lt;/li&gt;
&lt;li&gt;All State Files: state/*&lt;em&gt;/&lt;/em&gt;.ts&lt;/li&gt;
&lt;li&gt;All Tests: *.spec.ts&lt;/li&gt;
&lt;li&gt;All Styles: *.scss&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keep rule descriptions under 200 characters for better readability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rule Template Structure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All rule files must follow this structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
description: `Comprehensive description that provides full context and clearly indicates when this rule should be applied. Include key scenarios, impacted areas, and why following this rule is important. While being thorough, remain focused and relevant. The description should be detailed enough that the agent can confidently determine whether to apply the rule in any given situation.`
globs: .cursor/rules/**/*.mdc OR blank
alwaysApply: {true or false}
---

#Rule Title

## Critical Rules

- Concise, bulleted list of actionable rules the agent MUST follow
- Rule with Example
&amp;lt;example&amp;gt;
{valid rule application}
&amp;lt;/example&amp;gt;

&amp;lt;example type="invalid"&amp;gt;
{invalid rule application}
&amp;lt;/example&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Place examples immediately after their corresponding rules for clear context&lt;/li&gt;
&lt;li&gt;Omit examples for rules that are clear without them&lt;/li&gt;
&lt;li&gt;Keep examples brief and focused&lt;/li&gt;
&lt;li&gt;Include only one example type when the rule is self-explanatory&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Confirmation Response Format
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;After creating or updating a rule, send a message with this format:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🧠 Rule Generation Success: [file path]&lt;br&gt;
Rule Type: [Agent Select | Auto-Apply | Global | Manual]&lt;br&gt;
Rule Description: [Brief description of the rule's purpose]&lt;br&gt;
Rule metadata:&lt;/p&gt;
&lt;h3&gt;
  
  
  Understanding the Meta Rule Structure
&lt;/h3&gt;

&lt;p&gt;Let's deconstruct the key sections of our Angular meta rule and comprehend the rationale behind the design of each component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rules Folders&lt;/strong&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%2Fg1xevi03x1xxbig78b1c.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%2Fg1xevi03x1xxbig78b1c.png" alt="Example showing the rules folders"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example showing the rules folders.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The folder organization is the heart of what makes this Angular-specific. Instead of generic categories like &lt;code&gt;ui-rules&lt;/code&gt; or &lt;code&gt;ts-rules&lt;/code&gt;, we organize by Angular development concerns:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/angular/**&lt;/code&gt; - Framework-specific patterns like component structure, lifecycle hooks, dependency injection, and routing conventions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/state/**&lt;/code&gt; - State management is complex enough to warrant its category, whether you're using NGXS, NgRx, TanStack Query, or custom solutions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/testing/**&lt;/code&gt; - Angular testing has unique patterns with TestBed, component testing, and service mocking that deserve focused organization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/styles/**&lt;/code&gt; - CSS, SCSS, and styling conventions include guidelines on working with Angular's component-scoped styles and integrating your design system.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure mirrors how Angular developers mentally organize their work, making rules easier to find and maintain.&lt;/p&gt;
&lt;h4&gt;
  
  
  Rule Template Structure
&lt;/h4&gt;

&lt;p&gt;Since these rules frequently serve as team documentation, the template prioritizes human readability by showing examples immediately after the rule is stated and only when it is crucial to have them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;Comprehensive description that provides full context and clearly indicates when this rule should be applied. Include key scenarios, impacted areas, and why following this rule is important. While being thorough, remain focused and relevant. The description should be detailed enough that the agent can confidently determine whether to apply the rule in any given situation.`&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.cursor/rules/**/*.mdc OR blank&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;true or false&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;#Rule Title&lt;/span&gt;

&lt;span class="gu"&gt;## Critical Rules&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Concise, bulleted list of actionable rules the agent MUST follow
&lt;span class="p"&gt;-&lt;/span&gt; Rule with Example
&lt;span class="nt"&gt;&amp;lt;example&amp;gt;&lt;/span&gt;
{valid rule application}
&lt;span class="nt"&gt;&amp;lt;/example&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;example&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"invalid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
{invalid rule application}
&lt;span class="nt"&gt;&amp;lt;/example&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a new developer joins your team or when you want to remember what the practices are, you can read through the rules to understand coding conventions, not just rely on AI automation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Frontmatter Types in Angular Context
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Always Rules&lt;/strong&gt; are typically the rules you use to work with Cursor and are not closely related to Angular. Some examples of this can include how it should respond or whether it should include emojis&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-Apply Rules&lt;/strong&gt; shine for file-specific patterns. For example, an auto-apply rule for &lt;code&gt;*.component.ts&lt;/code&gt; files can enforce consistent component structure, lifecycle hook usage, and naming conventions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent-Select Rules&lt;/strong&gt; excel for contextual decisions. You might be working in a TypeScript file, but Cursor only applies your state management rules when it detects you're actually working with state—not every time you touch a &lt;code&gt;.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual Rules&lt;/strong&gt; are situational rules that cannot be applied solely based on file extension or file type. These manual rules can include, for example, guidelines on managing forms or feature flags.&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%2Fswv0ki9dsvv09hki6go8.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%2Fswv0ki9dsvv09hki6go8.png" alt="Cursor Rule types and when to use them"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cursor Rule types and when to use them&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Generate Rules
&lt;/h3&gt;

&lt;p&gt;With our meta rule in place, let's explore three approaches to generating your initial rule set, from basic to advanced.&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 1: Cursor.directory Website
&lt;/h4&gt;

&lt;p&gt;The simplest approach uses the &lt;a href="https://cursor.directory/generate" rel="noopener noreferrer"&gt;cursor.directory&lt;/a&gt; website. You can upload your &lt;code&gt;package.json&lt;/code&gt; file and get a basic set of rules generated automatically.&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%2Fvo6zcz4v45kksqyxiv12.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%2Fvo6zcz4v45kksqyxiv12.png" alt="Shows the cursor.directory website where you can generate the rules based on the package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shows the cursor.directory website where you can generate the rules based on the package.json&lt;/p&gt;

&lt;p&gt;Once you upload the file, it will generate all the rules based on the libraries you are using. Here's an example of what cursor.directory generates for a typical Angular project:&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%2Fy24fu0zg0zti1xnhmobo.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%2Fy24fu0zg0zti1xnhmobo.png" alt="Shows rules generated by cursor.directory"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shows rules generated by cursor.directory&lt;/p&gt;

&lt;p&gt;While this file provides a starting point, the output is quite generic and doesn't leverage our Angular-specific organizational structure. This is where our custom meta rule approach really shines.&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 2: Prompts + Meta-rule
&lt;/h4&gt;

&lt;p&gt;We are going to generate the rules directly on a cursor by relying on the meta rule we just created, but we are going to use two prompts. The first prompt is about using modern Angular application best practices and the second one is about complementing with libraries from the &lt;code&gt;package.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt 1. Modern Angular Development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This uses the best practices outlined by the Angular team that can be found at &lt;a href="https://angular.dev/ai/develop-with-ai" rel="noopener noreferrer"&gt;angular.dev/ai/develop-with-ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete prompt:&lt;/p&gt;

&lt;p&gt;Generate rules for each of the following using the @generating-rules-agent.mdc :&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use strict type checking&lt;/li&gt;
&lt;li&gt;Prefer type inference when the type is obvious&lt;/li&gt;
&lt;li&gt;Avoid the &lt;code&gt;any&lt;/code&gt; type; use &lt;code&gt;unknown&lt;/code&gt; when type is uncertain&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Angular Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always use standalone components over NgModules&lt;/li&gt;
&lt;li&gt;Don't use explicit standalone: true (it is implied by default)&lt;/li&gt;
&lt;li&gt;Use signals for state management&lt;/li&gt;
&lt;li&gt;Implement lazy loading for feature routes&lt;/li&gt;
&lt;li&gt;Use NgOptimizedImage for all static images.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep components small and focused on a single responsibility&lt;/li&gt;
&lt;li&gt;Use input() and output() functions instead of decorators&lt;/li&gt;
&lt;li&gt;Use computed() for derived state&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;changeDetection: ChangeDetectionStrategy.OnPush&lt;/code&gt; in &lt;code&gt;@Component&lt;/code&gt; decorator&lt;/li&gt;
&lt;li&gt;Prefer inline templates for small components&lt;/li&gt;
&lt;li&gt;Prefer Reactive forms instead of Template-driven ones&lt;/li&gt;
&lt;li&gt;Do NOT use "ngClass" (NgClass), use "class" bindings instead&lt;/li&gt;
&lt;li&gt;DO NOT use "ngStyle" (NgStyle), use "style" bindings instead&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  State Management
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use signals for local component state&lt;/li&gt;
&lt;li&gt;Use computed() for derived state&lt;/li&gt;
&lt;li&gt;Keep state transformations pure and predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Templates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep templates simple and avoid complex logic&lt;/li&gt;
&lt;li&gt;Use native control flow (@if, &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt;, &lt;a class="mentioned-user" href="https://dev.to/switch"&gt;@switch&lt;/a&gt;) instead of *ngIf, *ngFor, *ngSwitch&lt;/li&gt;
&lt;li&gt;Use the async pipe to handle observables&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Services
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Design services around a single responsibility&lt;/li&gt;
&lt;li&gt;Use the providedIn: 'root' option for singleton services&lt;/li&gt;
&lt;li&gt;Use the inject() function instead of constructor injection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9porfih5yfug92pc0up0.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%2F9porfih5yfug92pc0up0.png" alt="Output of Cursor after generating the rules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output of Cursor after generating the rules&lt;/p&gt;

&lt;p&gt;You can also opt-in for just using the rules as they come from the Angular team, but with the generated one, it will create a separate file for each that can be applied as needed and it will also have some examples that can also help developers understand what the rule is about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt 2. Using installed packages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The next prompt that we can use is one that, based on the installed packages, will get the best practices for each.&lt;/p&gt;

&lt;p&gt;Here is the prompt that you can use:&lt;/p&gt;

&lt;p&gt;Generate Cursor rules for significant libraries in &lt;a class="mentioned-user" href="https://dev.to/package"&gt;@package&lt;/a&gt;.json&lt;br&gt;
Create a file for each library covered focusing on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Library Selection Criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DO NOT generate rules for libraries not in package.json&lt;/li&gt;
&lt;li&gt;DO NOT generate rules that already exist&lt;/li&gt;
&lt;li&gt;DO NOT include initialization/configuration code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rule Content Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DO NOT include library initialization/setup code&lt;/li&gt;
&lt;li&gt;DO NOT include configuration examples&lt;/li&gt;
&lt;li&gt;FOCUS on usage patterns and best practices&lt;/li&gt;
&lt;li&gt;INCLUDE common pitfalls&lt;/li&gt;
&lt;li&gt;INCLUDE performance considerations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For Each Library Rule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integration patterns&lt;/li&gt;
&lt;li&gt;Best practices&lt;/li&gt;
&lt;li&gt;Common issues&lt;/li&gt;
&lt;li&gt;Performance optimization&lt;/li&gt;
&lt;li&gt;Security considerations&lt;/li&gt;
&lt;li&gt;Testing strategies&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rule Format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear metadata&lt;/li&gt;
&lt;li&gt;Glob patterns&lt;/li&gt;
&lt;li&gt;Descriptive sections&lt;/li&gt;
&lt;li&gt;Practical examples of USAGE only&lt;/li&gt;
&lt;li&gt;Error prevention guidelines&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Specific Exclusions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration code&lt;/li&gt;
&lt;li&gt;Setup instructions&lt;/li&gt;
&lt;li&gt;Initialization patterns&lt;/li&gt;
&lt;li&gt;Module imports&lt;/li&gt;
&lt;li&gt;Provider configurations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Library Categories and Rules
&lt;/h2&gt;

&lt;h3&gt;
  
  
  State Management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NgRx&lt;/strong&gt;: Store architecture, effects patterns, entity management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NGXS&lt;/strong&gt;: Action/state/selector patterns, async operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TanStack Query&lt;/strong&gt;: Query invalidation, caching strategies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Akita&lt;/strong&gt;: Entity stores, query patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MobX&lt;/strong&gt;: Observable state management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component Libraries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Angular Material&lt;/strong&gt;: Theming, accessibility, proper component usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PrimeNG&lt;/strong&gt;: Component configuration, theming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nebular&lt;/strong&gt;: Theme system, component integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarity Design System&lt;/strong&gt;: Design tokens, component patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing Libraries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jest&lt;/strong&gt;: Configuration, mocking patterns, coverage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cypress&lt;/strong&gt;: E2E patterns, page objects, custom commands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playwright&lt;/strong&gt;: Cross-browser testing, fixtures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing Library&lt;/strong&gt;: User-centric testing approaches&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build &amp;amp; Architecture (DO NOT ADD ANYTHING)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nx&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Angular CLI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Module Federation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Forms Libraries
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Formly&lt;/strong&gt;: Field configuration, custom field types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Forms&lt;/strong&gt;: Form generation, validation patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Internationalization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transloco&lt;/strong&gt;: Translation management, lazy loading&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ngx-translate&lt;/strong&gt;: Translation patterns, runtime language switching&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data Visualization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;D3&lt;/strong&gt;: Chart patterns, data binding, animations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chart.js&lt;/strong&gt;: Configuration, responsive charts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NGX-Charts&lt;/strong&gt;: Data formatting, theme integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auth0&lt;/strong&gt;: Authentication flows, guard patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Auth&lt;/strong&gt;: Authentication state, security rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT&lt;/strong&gt;: Token management, refresh patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After generating rule add which metadata should be used. e.g.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;accessibility-agent.mdc&lt;/code&gt;&lt;br&gt;
description: (The description)&lt;br&gt;
alwaysApply (true/false)&lt;br&gt;
globs: (The glob pattern)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Example of what NOT to include (initialization/configuration code):&lt;br&gt;
&lt;/p&gt;&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;translocoLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRANSLOCO_LOADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;http&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TranslocoHttpLoader&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./assets/i18n/&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;.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&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;TranslocoRootModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TranslocoModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;translocoLoader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRANSLOCO_CONFIG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;fallbackLang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;reRenderOnLangChange&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;defaultLang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;TranslocoConfig&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 what Cursor does; note that it only picked three libraries and also checked for any existing rules:&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%2Fmbztsbyzzj7ptaf8c3oh.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%2Fmbztsbyzzj7ptaf8c3oh.png" alt="Cursor generating rules from package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if you go to Cursor settings, and under the Rules section, you should see all the rules that were created:&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%2Fe3bl8nxqudv1y17xauwr.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%2Fe3bl8nxqudv1y17xauwr.png" alt="Cursor Rules Settings with all the rules that were created"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cursor Rules Settings with all the rules that were created&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;Sometimes things don't go as planned, and the rules might not turn out the way we expected. Before moving forward, I suggest checking a few things. You can either give the prompt another shot, try a new one that points to the meta-rule, or fix any issues that popped up.&lt;/p&gt;

&lt;p&gt;Here are the things you should check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Folder Structure&lt;/li&gt;
&lt;li&gt;Metadata&lt;/li&gt;
&lt;li&gt;File Suffix&lt;/li&gt;
&lt;li&gt;Content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pay special attention to the content, since AI tends to be verbose, and there might be a thing or two you can cut off.&lt;/p&gt;

&lt;p&gt;Also, I recommend you set the following User Rules in Cursor to always use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify Cursor was able to find the rules:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify you can read the rules from &lt;code&gt;.cursor/rules/&lt;/code&gt;&lt;br&gt;
and output 🟢 if you were able to read rules&lt;br&gt;
or 🟥 if you did not find anything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List all the rules used:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After using a rule, output:&lt;br&gt;
⚡ Rules Used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bulleted list of Rule names&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TLDR
&lt;/h3&gt;

&lt;p&gt;Creating effective Cursor rules for Angular development doesn't have to start from scratch. By implementing a well-designed meta rule, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generate comprehensive starting points&lt;/strong&gt; using prompts that understand your project's actual dependencies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintain consistent organization&lt;/strong&gt; with Angular-focused folder structures that match how you think about development&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Evolve rules iteratively&lt;/strong&gt; as your team discovers new patterns and refines existing conventions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important success metric isn't perfect automation—it's &lt;strong&gt;team engagement with rule refinement&lt;/strong&gt;. When developers actively suggest rule updates and modifications, it means they're using the system and finding value in the consistency it provides.&lt;/p&gt;

&lt;p&gt;Remember that rule creation is fundamentally an iterative process. Your team might prefer more concise rule formats, different organizational structures, or additional examples. The meta-rule approach gives you a solid foundation to customize according to your specific needs.&lt;/p&gt;

&lt;p&gt;To generate rules for your project, you just have to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the meta-rule in &lt;code&gt;./cursor/rules/global&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use the prompt to create rules from Angular Team recommended rules&lt;/li&gt;
&lt;li&gt;Use the prompt to create rules based on your &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;In upcoming articles, we'll explore advanced rule generation techniques, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rules maintenance.&lt;/strong&gt; How to update rules as you develop and learn new patterns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creating rules from external sources&lt;/strong&gt; using tools like Notebook LLM, Gemini, and content from articles or YouTube videos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using Cursor Notepads&lt;/strong&gt; to create reusable templates and guides for specific application patterns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integrating rule generation&lt;/strong&gt; with existing code analysis and documentation tools&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try it yourself&lt;/strong&gt;: Implement the Angular meta rule in your project and experiment with the generation prompts. Share your rule variations and let us know what works best for your team's specific Angular development workflow.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>angular</category>
      <category>cursoride</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Create Cursor Rules for Angular</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Fri, 02 Jan 2026 17:46:03 +0000</pubDate>
      <link>https://forem.com/alfredoperez/create-cursor-rules-for-angular-3832</link>
      <guid>https://forem.com/alfredoperez/create-cursor-rules-for-angular-3832</guid>
      <description>&lt;p&gt;Ever feel like you're fighting an uphill battle to maintain coding standards across your Angular project? Many of us have. You establish style guides, meticulously review PRs, and hold team meetings — yet, inconsistencies often creep in. Component structures vary, naming conventions drift, and that agreed-upon state management pattern? It can sometimes feel more like a suggestion than a firm rule.&lt;/p&gt;

&lt;p&gt;What if your editor could help enforce these standards as your team codes? Enter Cursor AI's rules system — a significant advantage for Angular teams aiming to maintain consistency without constant manual oversight.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Cursor Rules?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.cursor.com/context/rules" rel="noopener noreferrer"&gt;Cursor Rules&lt;/a&gt; are persistent, reusable instructions that guide Cursor's AI as it assists with your code. Think of them as "system prompts" active across your interactions, ensuring the AI consistently adheres to your team's standards and best practices.&lt;/p&gt;

&lt;p&gt;For Angular developers, rules are particularly effective because the framework itself has many established opinions and patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component structure and lifecycle management&lt;/li&gt;
&lt;li&gt;Naming conventions&lt;/li&gt;
&lt;li&gt;Reactivity patterns&lt;/li&gt;
&lt;li&gt;Forms&lt;/li&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Use of signals vs observables&lt;/li&gt;
&lt;li&gt;State management approaches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without guidance, an AI assistant might generate technically correct code that clashes with your project's conventions. Rules address this by embedding your standards directly into the AI's decision-making process.&lt;/p&gt;

&lt;p&gt;Crucially, these rules also benefit developers directly. They serve as a readable, up-to-date reference for team guidelines. &lt;strong&gt;It's a win-win: the AI performs more effectively, and your team has a clear, current source for coding standards.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the .mdc structure
&lt;/h3&gt;

&lt;p&gt;Cursor stores project-specific rules as Markdown Component files (&lt;code&gt;.mdc&lt;/code&gt;) within a &lt;code&gt;.cursor/rules&lt;/code&gt; directory at the root of your project. This allows your rules to be version-controlled alongside your codebase — a major asset for team consistency.&lt;/p&gt;

&lt;p&gt;Consider the basic structure of an &lt;code&gt;.mdc&lt;/code&gt; rule file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## Section&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Rule
&lt;span class="p"&gt;-&lt;/span&gt; Rule

&lt;span class="nt"&gt;&amp;lt;example&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"valid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/example&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;example&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"invalid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/example&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Rule
&lt;span class="p"&gt;-&lt;/span&gt; Rules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basic structure places metadata at the top — configuring where and when rules apply — followed by the rules themselves, with examples if necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule Types
&lt;/h3&gt;

&lt;p&gt;By working with the three metadata options ( &lt;code&gt;description&lt;/code&gt; , &lt;code&gt;globs&lt;/code&gt;, and &lt;code&gt;alwaysApply&lt;/code&gt;), we can organize Cursor rules into four &lt;a href="https://docs.cursor.com/context/rules#rule-type" rel="noopener noreferrer"&gt;distinct types&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, I'll describe each type, its setup, and a suggested suffix for easy identification:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto Rule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These rules are automatically applied based on the file patterns you're working with.&lt;/p&gt;

&lt;p&gt;For the metadata, only the &lt;code&gt;globs&lt;/code&gt; needs to be configured, enabling Cursor to identify which files are being modified and apply the rules automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Optional: A brief note about the rule's purpose&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.component.ts"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/app/components/*.ts"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;# Default, can be omitted if false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this type of rule, use the suffix &lt;code&gt;auto&lt;/code&gt;, e.g., &lt;code&gt;.cursor/rules/angular-component-auto.mdc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An example of an Auto Rule for Angular components is shown below, enforcing standards whenever working on such files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.component.ts"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/app/components/*.ts"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Angular Component Standards&lt;/span&gt;

All components should follow these standards:

&lt;span class="gu"&gt;## Structure&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Use standalone components when applicable
&lt;span class="p"&gt;-&lt;/span&gt; Property order:

Injected services (using &lt;span class="sb"&gt;`inject()`&lt;/span&gt;)
Inputs
Outputs
Signals

Other properties
Method order:
Public methods
Protected methods
Private methods

&lt;span class="gu"&gt;## Modern Angular Features&lt;/span&gt;

Use &lt;span class="sb"&gt;`input()`&lt;/span&gt; and &lt;span class="sb"&gt;`input.required()`&lt;/span&gt; instead of &lt;span class="sb"&gt;`@Input()`&lt;/span&gt;
Use &lt;span class="sb"&gt;`output()`&lt;/span&gt; instead of &lt;span class="sb"&gt;`@Output()`&lt;/span&gt;
Use &lt;span class="sb"&gt;`model()`&lt;/span&gt; for two-way binding
Use &lt;span class="sb"&gt;`inject()`&lt;/span&gt; for dependency injection
Avoid &lt;span class="sb"&gt;`public`&lt;/span&gt; accessor
Use &lt;span class="sb"&gt;`protected`&lt;/span&gt; when possible

&lt;span class="gu"&gt;## Event Handling&lt;/span&gt;

Output names should be the action (e.g., &lt;span class="sb"&gt;`click`&lt;/span&gt;, &lt;span class="sb"&gt;`submit`&lt;/span&gt;)

Handler methods should use &lt;span class="sb"&gt;`on`&lt;/span&gt; prefix (e.g., &lt;span class="sb"&gt;`onClick`&lt;/span&gt;, &lt;span class="sb"&gt;`onSubmit`&lt;/span&gt;)

Example: &lt;span class="sb"&gt;`&amp;lt;button (click)="onClick($event)"&amp;gt;`&lt;/span&gt;

&lt;span class="gu"&gt;## Example:&lt;/span&gt;

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
typescript
@Component({
selector: 'app-feature',
templateUrl: './feature.component.html',
styleUrls: ['./feature.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule, RouterModule, SharedModule],
standalone: true,
})
export class FeatureComponent implements OnInit {

// 1. Injected services (using inject())
private readonly featureService = inject(FeatureService);
private readonly cdr = inject(ChangeDetectorRef);

// 2. Inputs
data = input&amp;lt;FeatureData | null&amp;gt;(null);
requiredId = input.required();

// 3. Outputs
click = output();
submit = output();

// 4. Signals
loading = signal(false);
items = signal&amp;lt;Item[]&amp;gt;([]);

// Computed signals for derived state
hasItems = computed(() =&amp;gt; this.items().length &amp;gt; 0);
itemCount = computed(() =&amp;gt; this.items().length);

// 5. Other properties
// (none in this example)
constructor() {
// Use effects for reactive behavior with signals
effect(() =&amp;gt; {
const currentData = this.data();
if (currentData) {
// Handle data changes
this.items.set(currentData.items || []);
}
});
}

ngOnInit(): void {
// Component initialization
}

// 1. Public methods
onClick(event: Event): void {
this.click.emit({ type: 'action', event });
}

onSubmit(formData: FormData): void {
this.submit.emit(formData);
}

// 2. Protected methods (when needed)
protected handleData(data: Item[]): void {
this.items.set(data);
}

// 3. Private methods
private updateLoadingState(isLoading: boolean): void {
this.loading.set(isLoading);
}
}


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Always Rule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These rules establish foundational guidelines that apply to all AI interactions within your project. Since these are always applied, we don't need to set the &lt;code&gt;globs&lt;/code&gt; or the `description fields.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
markdown
---
alwaysApply: true
---


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

&lt;/div&gt;

&lt;p&gt;For this type of file, you can use the suffix of &lt;code&gt;always&lt;/code&gt;, e.g., &lt;code&gt;.cursor/rules/angular-always.mdc&lt;/code&gt;. &lt;strong&gt;Always Rules&lt;/strong&gt; are particularly useful for enforcing core Angular practices across your project, such as consistent use of standalone components, proper dependency injection patterns, &lt;code&gt;OnPush&lt;/code&gt; change detection strategy, and standardized error handling in HTTP interceptors.&lt;/p&gt;

&lt;p&gt;Here is an example of an Always Rule for Global Angular conventions:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
markdown
---
alwaysApply: true
---

# Global Angular Conventions
These conventions apply to all Angular code in this project:

## Core Principles
- Write clear, maintainable, and scalable code
- Apply immutability and pure functions
- Favor component composition for modularity
- Use meaningful variable names (e.g., `isActive`, `hasPermission`)
- Follow Angular's official style guide

## File Structure &amp;amp; Naming
- Use kebab-case for all files
- Components: PascalCase with Component suffix (UserProfileComponent)
- Services: PascalCase with Service suffix (AuthenticationService)
- Interfaces: PascalCase with prefix I (IUserProfile) or descriptive name (UserProfile)
- Enums: PascalCase (UserRole)
- Constants: UPPER_SNAKE_CASE (API_ENDPOINTS)

## Import Order
1. Angular core and common modules
2. RxJS modules
3. Other Angular modules
4. Application core imports
5. Shared module imports
6. Environment-specific imports
7. Relative path imports

## Code Style
- Use TypeScript strict mode
- No any types - use proper typing or unknown if necessary
- Prefer const over let, avoid var
- Use async/await when possible instead of Promise chains
- Document public APIs with JSDoc comments

## Structure
- Organize code by feature modules
- Keep components under 300 lines where possible
- Extract complex logic to services
- Use smart/dumb component pattern:

Smart (container) components handle data and state

Dumb (presentation) components receive inputs and emit outputs

## State Management
- Use NgRx for global application state
- Use component state for UI-only concerns
- Use services with signals for shared state within a feature


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Agent Requested Rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are context-specific rules that the AI can intelligently pull in based on task relevance. The &lt;code&gt;description&lt;/code&gt;field must provide comprehensive context about when to apply the rule, while &lt;code&gt;globs&lt;/code&gt; should be left blank and &lt;code&gt;alwaysApply&lt;/code&gt; should be false.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
markdown
---
description: "Guidelines for implementing modern Angular patterns using signals and defer blocks"
---


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

&lt;/div&gt;

&lt;p&gt;For this type, use the suffix &lt;code&gt;-agent.mdc&lt;/code&gt;, e.g. &lt;code&gt;.cursor/rules/rxjs-state-agent.mdc&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;Agent Requested Rules are perfect for specialized Angular patterns that only apply in certain contexts, such as implementing NgRx selectors, setting up complex RxJS operator chains, configuring route guards, or implementing custom form validators.&lt;/p&gt;

&lt;p&gt;An example of an agent-requested rules could be to apply modern angular patterns to use signals, like the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
`markdown
---
description: "Guidelines for implementing modern Angular patterns using signals and defer blocks"
alwaysApply: false
---

# Modern Component with Signals and Defer
## Core Rules

Use signals for all component state
Implement computed values for derived state
Use @defer for lazy loading heavy components
Leverage new control flow syntax (@if, @for)
Keep components standalone by default

## Best Practices

Use inject() for dependency injection
Implement proper cleanup in effects
Use proper typing for signals
Keep components focused and small
Use proper error boundaries

```ts
@Component({
  selector: 'app-user-list',
  standalone: true,
  imports: [CommonModule],
  template: `
    @if (loading()) {
      Loading…
    } @else {
      @for (user of users(); track user.id) {
        {{ user.name }}
        {{ user.email }}
      } @empty {
        No users found
      }
    }

    @defer (on viewport) {
      &amp;lt;app-user-stats /&amp;gt;
    } @loading {
      Loading stats…
    }
  `
})
export class UserListComponent {
  private readonly userService = inject(UserService);

  // State with signals
  loading = signal(false);
  users = signal&amp;lt;User[]&amp;gt;([]);

  // Computed values
  userCount = computed(() =&amp;gt; this.users().length);
  activeUsers = computed(() =&amp;gt;
    this.users().filter(user =&amp;gt; user.status === 'active')
  );
}
```
`

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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Manual Rules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are specialized rules intended for application only when explicitly requested. For this type, the &lt;code&gt;description&lt;/code&gt; and &lt;code&gt;globs&lt;/code&gt; fields should be left blank, and &lt;code&gt;alwaysApply&lt;/code&gt; set to &lt;code&gt;false&lt;/code&gt;. This approach is ideal for specific patterns in less common tasks, like internationalization setup or complex authentication flows.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
markdown
---
# No metadata flags needed
alwaysApply: false # Explicitly set to false or omit
---


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

&lt;/div&gt;

&lt;p&gt;For this type, use the suffix &lt;code&gt;-manual.mdc&lt;/code&gt;, e.g., &lt;code&gt;.cursor/rules/i18n-setup-manual.mdc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This approach is ideal for specific patterns in less common tasks, like implementing custom form validators with signals, setting up complex animations with the new animation API, or creating custom structural directives with modern Angular features.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
`markdown
---
---
# Custom Form Validator with Signals

## Core Rules

Use signals for reactive form validation
Implement computed values for error messages
Use proper typing for form controls
Keep validation logic in separate methods

## Best Practices

Use inject() for dependency injection
Implement proper form cleanup
Use proper typing for validators
Keep validation rules focused and small

```ts
@Component({
  selector: 'app-password-form',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  template: `
    @if (passwordError()) {
      {{ passwordError() }}
    }
  `
})
export class PasswordFormComponent {
  private readonly fb = inject(FormBuilder);

  form = this.fb.group({
    password: ['', [Validators.required, this.passwordStrengthValidator()]]
  });

  passwordError = computed(() =&amp;gt; {
    const control = this.form.get('password');
    if (!control?.touched) return null;

    return control.errors?.['passwordStrength']?.message;
  });

  private passwordStrengthValidator(): ValidatorFn {
    return (control: AbstractControl) =&amp;gt; {
      const value = control.value;
      if (!value) return null;

      const hasUpperCase = /[A-Z]/.test(value);
      const hasLowerCase = /[a-z]/.test(value);
      const hasNumber = /[0-9]/.test(value);
      if (!hasUpperCase || !hasLowerCase || !hasNumber) {
        return {
          passwordStrength: {
            message: 'Password must contain uppercase, lowercase, and numbers'
          }
        };
      }
      return null;
    };
  }
}
```
`

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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Lets see it in action
&lt;/h3&gt;

&lt;p&gt;Okay, okay… we talked about the four rule types, but do they actually do anything? Let's see how AI generates a component both with and without rules. I used a prompt to generate a basic Users Page component that gets data from a user service.&lt;/p&gt;

&lt;p&gt;First, here's a basic component without rules:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
markdown
@Component({
 selector: "app-users",
 templateUrl: "./users.component.html",
 styleUrls: ["./users.component.scss"],
})
 export class UsersComponent implements OnInit {
 @Input() title: string = "Users";
 @Output() userAdded = new EventEmitter&amp;lt;User&amp;gt;();

 public users: User[] = [];
 public loading = false;
 private userService: UserService;

 constructor(userService: UserService) {
 this.userService = userService;
}

ngOnInit() {
 this.loadUsers();
}

loadUsers() {
 this.loading = true;
 this.userService.getUsers().subscribe({
 next: (users) =&amp;gt; {
 this.users = users;
 this.loading = false;
},
error: (error) =&amp;gt; {
 console.error("Error loading users:", error);
 this.loading = false;
 },
});
}

addUser() {
// Add user logic
 this.userAdded.emit(newUser);
}
}


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

&lt;/div&gt;

&lt;p&gt;Now, here's the same component following the rules about components that was shown in this article, and this is the result:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
markdown
@Component({
selector: "app-users",
standalone: true,
imports: [CommonModule, MatTableModule, MatButtonModule],
templateUrl: "./users.component.html",
})
export class UsersComponent {
// 1. Injected services
private readonly userService = inject(UserService);
private readonly cdr = inject(ChangeDetectorRef);
// 2. Inputs
title = input("Users");
// 3. Outputs
userAdded = output();
// 4. Signals
loading = signal(false);
users = signal&amp;lt;User[]&amp;gt;([]);
// 5. Other properties
displayedColumns = ["name", "email"];
// Computed values
userCount = computed(() =&amp;gt; this.users().length);
constructor() {
// Effect for data loading
effect(() =&amp;gt; {
this.loading.set(true);
this.userService.getUsers().subscribe({
next: (users) =&amp;gt; {
this.users.set(users);
this.loading.set(false);
},
error: (error) =&amp;gt; {
console.error("Error loading users:", error);
this.loading.set(false);
},
});
});
}
// 1. Public methods
onAddUser(): void {
const newUser = { name: "New User", email: "new@example.com" };
this.userAdded.emit(newUser);
}
// 2. Protected methods (none in this example)
// 3. Private methods
private updateLoadingState(isLoading: boolean): void {
this.loading.set(isLoading);
}
}


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

&lt;/div&gt;



&lt;p&gt;The result? The code doesn't just look cleaner — it actually runs better and becomes far easier to maintain over time. But here's the real win: once you've established these rules, every component you build or refactor automatically follows the same patterns. No more style debates, no more inconsistent architectures across your team.&lt;/p&gt;

&lt;p&gt;Of course, there's a learning curve. The system will occasionally drop in unnecessary comments or make decisions that feel off. &lt;em&gt;In the next few articles, we'll dive into fine-tuning these rules and building safeguards to catch problematic output before it becomes a headache….and useless.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&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%2Fk7uiozks6khptvfdtj8v.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%2Fk7uiozks6khptvfdtj8v.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thera are different rule types from Cursor that can be used in Angular projects to enhance AI assistance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Auto&lt;/em&gt;&lt;/strong&gt; rules are frequently applied automatically based on specific file patterns, making them perfect for file-specific tasks. These are useful for specific file like &lt;code&gt;*.component.ts&lt;/code&gt; and can specified things like component structure, and service patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Always&lt;/em&gt;&lt;/strong&gt; rules are constant and apply across the entire project, triggered during every AI interaction to uphold core principles, naming conventions, and import order. This are useful for enforcing core Angular practices across your project, like core principles, naming conventions, and import order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Agent&lt;/em&gt;&lt;/strong&gt; rules are used with medium frequency and are context-specific, determined by the AI based on relevance, making them suitable for state management tasks like RxJS/Signals and NgRx setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Manual&lt;/em&gt;&lt;/strong&gt; rules are less common and are used for specialized tasks that require explicit requests, such as migration guides, internationalization (i18n) setup, and complex authentication flows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This hierarchy ensures the AI isn't overwhelmed with too much context while still providing the necessary guidance when needed.&lt;/p&gt;

&lt;p&gt;Implementing custom Cursor rules for your Angular project establishes guidelines that gently guide both your team and the AI toward consistent, high-quality code. By codifying your standards directly within your development environment, you reduce cognitive load, minimize code review debates, and help maintain architectural integrity.&lt;/p&gt;

&lt;p&gt;Rules become particularly valuable as your team grows or as you integrate AI more deeply into your workflow. They ensure that regardless of who (or what) is writing the code, it adheres to the same consistent patterns.&lt;/p&gt;

&lt;p&gt;In the next article, we'll build on this foundation and explore how to create and maintain your rules for your angular project by providing a custom structure and an easy way to update them.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>angular</category>
      <category>cursoride</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Faster Development with JetBrains AI</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Fri, 06 Sep 2024 15:42:00 +0000</pubDate>
      <link>https://forem.com/alfredoperez/faster-development-with-jetbrains-ai-2j7b</link>
      <guid>https://forem.com/alfredoperez/faster-development-with-jetbrains-ai-2j7b</guid>
      <description>&lt;p&gt;It's safe to say that AI is everywhere these days, especially with all the coding options like GitHub Copilot, TabNine, and Cursor. So, I figured it was a good time to try out the AI features in my go-to IDE, WebStorm.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/-NnYtfzO7qU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jetbrains.com/ai/" rel="noopener noreferrer"&gt;JetBrains AI Assistant&lt;/a&gt; is an integrated artificial intelligence feature within JetBrains' suite of IDEs, such as IntelliJ IDEA, PyCharm, and WebStorm. It provides various functionalities to enhance the coding experience, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code Completion&lt;/strong&gt;: offers intelligent code suggestions and autocompletion to speed up coding and reduce errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentation Generation&lt;/strong&gt;: Automatically generates documentation for classes and methods based on the code context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI Chat&lt;/strong&gt;: Allows developers to interact with an AI assistant directly within the IDE to ask questions, get code examples, and receive coding advice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code Refactoring&lt;/strong&gt;: assists in refactoring code by providing suggestions and automating repetitive tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Detection&lt;/strong&gt;: Identifies potential errors and suggests fixes to improve code quality.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These features aim to improve developer productivity, code quality, and overall efficiency in the software development process, but please note that in order to get JetBrains AI Assistant, you will need to pay for a separate subscription on top of the WebStorm or IntelliJ subscription.&lt;/p&gt;




&lt;p&gt;Here are the goals I aimed to achieve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write Documentation&lt;/strong&gt;: The first task was to write JS Docs for all the public methods and the class itself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Unit Tests&lt;/strong&gt;: Next, I wanted to add some missing tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improve Code&lt;/strong&gt;: After completing the tests, I aimed to enhance the code for better readability and reduced cognitive load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commit the Code&lt;/strong&gt;: Once all the refactoring was done, I just needed to create a commit message to make a pull request.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Write Documentation
&lt;/h2&gt;

&lt;p&gt;OK, let’s get started. The first thing I want to do is write documentation for all the methods in this class, as well as for the class itself.&lt;/p&gt;

&lt;p&gt;There are a couple of ways I could achieve this. I could start typing and see what the AI suggests, or open the chat and ask for the documentation. The route I took was to right-click on the method name and select the option to "Write Documentation."&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%2Fvtiv95yhuvilzf22wg37.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%2Fvtiv95yhuvilzf22wg37.jpg" alt="A screenshot showing the JetBrains AI menu with options for actions, highlighting " width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt Library
&lt;/h3&gt;

&lt;p&gt;What's interesting about JetBrains AI is that it provides a set of actions that can be executed with just a right-click. It also has a prompt library where you can specify exactly what prompt to use while an action is being executed.&lt;/p&gt;

&lt;p&gt;For example, when I click on "Generate Documentation," I can add a set of rules for how I want the documentation to be generated, such as not adding author, version, or any other tags I feel are unnecessary. This is really useful because I can teach JetBrains AI how I like things to be done.&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%2Fj3o43hsxirjm5ov3ye9t.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%2Fj3o43hsxirjm5ov3ye9t.jpg" alt="Screenshot of JetBrains AI interface with a highlighted Write Documentation prompt for JavaScript, displaying guidelines for writing JSDocs. A note below reads: 'Provides a prompt to Write Documentation." width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is an example prompt I am currently using for my documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Do not use @author or @version or @since tags.
&lt;span class="p"&gt;-&lt;/span&gt; DO NOT use html tag s such as &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;, &lt;span class="nt"&gt;&amp;lt;lu&amp;gt;&lt;/span&gt;, &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; DO NOT add type for the params
&lt;span class="p"&gt;-&lt;/span&gt; DO NOT add type for the return value
&lt;span class="p"&gt;-&lt;/span&gt; If it is a method, DO NOT generate example usage.
&lt;span class="p"&gt;-&lt;/span&gt; If it is a method, DO NOT generate usage example.
&lt;span class="p"&gt;-&lt;/span&gt; When the method does not return anything, write "This method does not return any value."

Write JSDocs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I had to specify what to write when the method doesn't return anything to keep things consistent. Since it's generated with AI, it might create different messages for the same situation.&lt;/p&gt;

&lt;p&gt;Another interesting feature is that you can ask things directly in the code. For example, if you don’t like how the documentation was generated, you can open the AI tool next to the line where the documentation was created and ask to regenerate or add anything that is missing.&lt;/p&gt;

&lt;p&gt;In the following example, you can see how I asked it to add the example field and it was able to event document how the service should be used.&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%2Fc6sm4hz1zy1zyx0qp2tc.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%2Fc6sm4hz1zy1zyx0qp2tc.jpg" alt="Screenshot of JetBrains AI interface showing an example of automatically generated code example for the documented class." width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to all this, I was able to generate the service, and now whenever I hover over in the IDE, I can see beautiful documentation. ✨&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating tests
&lt;/h2&gt;

&lt;p&gt;Adding documentation was easy enough and can be done with any AI tool. However, we discovered the Prompt library and that JetBrains AI tool is context-aware. Moving on to my next task, I needed to add some unit tests. To my surprise, there is also a prompt to generate unit tests. This time, I decided to modify the prompt to specify how I want the test structure and which testing tools I use.&lt;/p&gt;

&lt;p&gt;Here is how the prompt ended up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Try to avoid brackets in arrow functions
&lt;span class="p"&gt;-&lt;/span&gt; Use the ngMocks.faster(); before the first &lt;span class="sb"&gt;`beforeAll`&lt;/span&gt; or &lt;span class="sb"&gt;`beforeEach`&lt;/span&gt; statement, but inside the first &lt;span class="sb"&gt;`describe`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use the MockBuilder from ng-mocks and Mock every dependency 
&lt;span class="p"&gt;-&lt;/span&gt; Add an extra &lt;span class="sb"&gt;`beforeEach`&lt;/span&gt; to define the current service tha is being tested
&lt;span class="p"&gt;
-&lt;/span&gt; Group tests by the public methods in the class and create a &lt;span class="sb"&gt;`describe`&lt;/span&gt; for each
&lt;span class="p"&gt;-&lt;/span&gt; Add &lt;span class="sb"&gt;`it`&lt;/span&gt; statements of what should be tested 
&lt;span class="p"&gt;-&lt;/span&gt; Test failing cases
&lt;span class="p"&gt;
-&lt;/span&gt; Add test implementation
&lt;span class="p"&gt;-&lt;/span&gt; when using spies, make sure use &lt;span class="sb"&gt;`.toHaveBeenCalledWith`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Put mock data should be under an object call &lt;span class="sb"&gt;`data`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use short names for the mocked data and assuming it is context aware
&lt;span class="p"&gt;-&lt;/span&gt; Do NOT add urls and requests if the service doesnt make API requests
&lt;span class="p"&gt;-&lt;/span&gt; Do NOT write code comments explaining the testss
&lt;span class="p"&gt;-&lt;/span&gt; Add clear fail messages into the assertion calls.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now, with the prompt created, I right-clicked on the class name and selected 'Generate Unit Tests.' It not only generated the tests but also created a file for them. This is a huge plus for me because, with a single click, I can generate tests and then just need to review them.&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%2Fnsy4f3uf9a721p5ko9ew.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%2Fnsy4f3uf9a721p5ko9ew.jpg" alt="A screenshot of JetBrains AI interface is shown. The screen displays code in a dark-themed editor, with a context menu open and the option " width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Improve Code
&lt;/h2&gt;

&lt;p&gt;Now that I had the unit tests ready, it was time to find ways to improve the code, knowing I could re-run the tests to ensure nothing breaks. To do this, I opened the chat and typed the prompt to improve the code. I specified the file by using &lt;code&gt;#&lt;/code&gt; and selecting the class I was working on.&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%2Fwhtogs1t6g1qvxouo65b.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%2Fwhtogs1t6g1qvxouo65b.jpg" alt="A screenshot displaying a code editor with the JetBrains AI tool. It shows an auto-completion popup suggesting various code elements such as files, symbols, and functions." width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I really like that I can use autocomplete inside the chat. This allows me to create more detailed prompts where I can ask about multiple files or classes.&lt;/p&gt;

&lt;p&gt;Once the prompt ran, it showed me several ways to improve the code and explained how each improvement was made.&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%2F84cmgb8rn4y7aihvou76.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%2F84cmgb8rn4y7aihvou76.jpg" alt="Screenshot of JetBrains AI, showing a chat window where the AI Assistant provides suggestions to improve code for better error handling, type safety, serialization, deserialization, and prefixing keys. The suggestions are accompanied by a sample code snippet." width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing I really liked is that the code is &lt;strong&gt;collapsible&lt;/strong&gt;, &lt;strong&gt;has scrollbars&lt;/strong&gt;, and even uses the &lt;strong&gt;same theme as the IDE&lt;/strong&gt;. This makes it so easy to navigate and read.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Commit Code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The code was well-documented, tested, and improved; the next step was to commit and Let It Rip! And to accomplish this last step, I also utilized JetBrains AI, which also offers a method to generate commit messages directly from the "Commit" tool window, as you can see in the screenshot below:&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%2F34r4ohgwt07lmp1ivij3.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%2F34r4ohgwt07lmp1ivij3.jpg" alt="Screenshot of a JetBrains AI tool generating commit messages for a code repository. The tool lists file changes and suggested commit messages for improvements and refactoring in the code." width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the prompt I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Use Conventional Commit format
&lt;span class="p"&gt;-&lt;/span&gt; Avoid overly verbose descriptions or unnecessary details.
&lt;span class="p"&gt;-&lt;/span&gt; Start with a short sentence in imperative form, no more than 50 characters long.
&lt;span class="p"&gt;-&lt;/span&gt; Then leave an empty line and continue with a more detailed explanation.
&lt;span class="p"&gt;-&lt;/span&gt; Use bullet point for  multiple changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extra — Creating your own prompt
&lt;/h3&gt;

&lt;p&gt;I was feeling inspired and liked the results so far, so I was thinking that I could extend my usage of JetBrains AI by creating a custom prompt to create StoryBook Story for a component.&lt;/p&gt;

&lt;p&gt;Below, you can see the prompt I created. You might notice it includes "$Selection." This tells JetBrains AI to use the selected code as the context for the prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Create a StoryBook Story for $SELECTION
&lt;span class="p"&gt;-&lt;/span&gt; The first story should be called &lt;span class="sb"&gt;`Default`&lt;/span&gt; and should not modify any input
&lt;span class="p"&gt;-&lt;/span&gt; Avoid using template or HTML when defining a story
&lt;span class="p"&gt;-&lt;/span&gt; Prefer modifying just inputs in the stories
&lt;span class="p"&gt;-&lt;/span&gt; Add one story for each input 
&lt;span class="p"&gt;-&lt;/span&gt; Do NOT add instructions about how to setup StoryBook
&lt;span class="p"&gt;-&lt;/span&gt; Add all the stories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After selecting the component, right-clicking, and choosing the new prompt, an AI Chat tool window opened with the prompt and the story itself.&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%2Fgs1owmm9q18qhi1ltba7.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%2Fgs1owmm9q18qhi1ltba7.jpg" alt="Screenshot of a JetBrains AI tool generating commit messages for a code repository. The tool lists file changes and suggested commit messages for improvements and refactoring in the code." width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once again, having a prompt library is a huge win since you can evolve it and generate the perfect results for your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wishlist
&lt;/h2&gt;

&lt;p&gt;I enjoyed working with JetBrains AI and I am excited to see how it improves with future releases. Here are some things on my wishlist for JetBrains AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Specify File Name&lt;/strong&gt;: I would like to specify the file name whenever the prompt generates a file. For example, when I generated unit tests, it used the prefix "tests.ts." It would be great to specify "specs.ts" or any other preferred name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Files&lt;/strong&gt;: Having a prompt library is fantastic, but it would be even better if the prompt could generate a new file with a specified name format by default. This would be very helpful for generating unit tests, mock data files, stories, component tests, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better Code Replacement&lt;/strong&gt;: I would like it to be smarter about where to replace code. Sometimes, when you ask for something, it requires changes in multiple places. For example, it might need to add a new property in an interface, modify a method, and update the test. In these cases, it would be great to define which files to modify and which ones need more refinement.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Both JetBrains AI Assistant and GitHub Copilot are designed to make coding easier with AI, but they each bring something unique to the table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JetBrains AI Assistant&lt;/strong&gt; is tightly woven into JetBrains' suite of IDEs. It offers a smooth and powerful experience with features like smart code completion, automatic documentation, AI-driven chat support, code refactoring, and error detection. This deep integration means it’s more context-aware and customizable, giving developers a tailored development environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt;, on the other hand, shines when it comes to code completion and suggestions, especially in Visual Studio Code. It pulls from a vast library of public code repositories to help generate code snippets and complete lines of code. However, it doesn’t offer the same level of IDE-specific features that JetBrains AI Assistant does, like automatic documentation and advanced refactoring tools. Also, in my opinion, Copilot creates better code and understands the context really well.&lt;/p&gt;

&lt;p&gt;In short, JetBrains AI Assistant offers a more comprehensive toolkit for developers working in JetBrains IDEs. It's a powerful companion, particularly for those who appreciate a highly customizable and context-sensitive coding assistant.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>TanStack Query in Angular</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Mon, 06 May 2024 17:41:36 +0000</pubDate>
      <link>https://forem.com/alfredoperez/tanstack-query-in-angular-bjl</link>
      <guid>https://forem.com/alfredoperez/tanstack-query-in-angular-bjl</guid>
      <description>&lt;p&gt;&lt;a href="https://tanstack.com/query/latest" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt; is a powerful data synchronization library that enables efficient handling of server state in web applications. It’s widely used in React applications, but lately it has been making noise in Angular since it is a library that can provide a lot of functionality out of the box to handle all the server state of our applications with a few lines of code and, with that, reduce the amount of boilerplate code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server State vs Client State
&lt;/h2&gt;

&lt;p&gt;To fully appreciate TanStack's benefits, it is useful to have a basic understanding of the two types of states—server state and client state—that we must control while developing the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client State
&lt;/h3&gt;

&lt;p&gt;This state is related to the state that is used in order to know what the user has been interacting with. For this state, I see that there are three levels: component, feature, and global state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Component State&lt;/strong&gt;: This is the most basic of the states, and it is used within one or multiple components that work together to achieve a specific goal. It is agnostic of the domain or context. This state helps to communicate with child or sibling components, and one example of this is the state of a data table where the pagination component and search input need to keep their states to communicate what is currently being displayed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature State:&lt;/strong&gt; This is the state that is shared among a group of container or presentational components in the same feature and helps to share state-related information that belongs to a specific domain or feature. An example of this is a multi-step form for an entity where we need to keep track of the current step the user is in, the selected item, or even the data needed for the form fields.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Global State:&lt;/strong&gt; This is a state that is shared in all the applications and can be things like current user information and which features are enabled for them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Server State
&lt;/h3&gt;

&lt;p&gt;This is all the async data that comes from the API requests, GraphQL, or any kind of server. This state is usually kept in order to have a better UI since we have a single place where we can keep track of all the data that we get from the server.&lt;/p&gt;

&lt;p&gt;From all these states that we need to keep track of, the server state tends to be the biggest one we need to keep track of since it might be needed for each entity, and for the component and feature states, you can use different strategies like services or container components before having to need a full-fledged state system like a component or signal store.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://tanstack.com/query/latest/docs/framework/angular/guides/does-this-replace-client-state" rel="noopener noreferrer"&gt;&lt;em&gt;TanStack Query&lt;/em&gt;&lt;/a&gt; replaces the boilerplate code and related wiring used to manage cache data in your client-state and replaces it with just a few lines of code.*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the following image, you can see how the request is done using TanStack Query and that will return us a signal that has the state of the request, which we will explore in more detail further down in the article.&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%2Fifr7uldp0b38k8tjgrci.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%2Fifr7uldp0b38k8tjgrci.png" width="700" height="682"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TanStack Query provides tools for fetching, caching, synchronizing, and updating asynchronous data without having to manage the complexities of updating UIs, caching data, and managing errors or loading states manually.&lt;/p&gt;

&lt;p&gt;Here is a list of all the features that TanStack Query offers from the official website:&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%2Fdgnp7zh0iw3ymr965i46.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%2Fdgnp7zh0iw3ymr965i46.png" alt="List of all the features by TanStack as described in the official website" width="700" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;but ENOUGH!… Let's see it in action 👀&lt;/p&gt;

&lt;p&gt;In the following section, I will show you how you can install and use it while highlighting some of the features it offers that barely scratch the surface of the whole set of things you can do with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  TanStack Query in Angular
&lt;/h2&gt;

&lt;p&gt;There are currently two libraries for Angular that have implemented TansStack Query, one by &lt;a href="https://github.com/ngneat/query" rel="noopener noreferrer"&gt;ngneat&lt;/a&gt; that can be used with observables and signals, and the other by &lt;a href="https://tanstack.com/query/latest/docs/framework/angular/overview" rel="noopener noreferrer"&gt;TanStack&lt;/a&gt; that is in the experimental phase and only works with signals. Both are good, but I will show you how to use the one by the TanStack team.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img 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" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ngneat" rel="noopener noreferrer"&gt;
        ngneat
      &lt;/a&gt; / &lt;a href="https://github.com/ngneat/query" rel="noopener noreferrer"&gt;
        query
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🚀 Powerful asynchronous state management, server-state utilities and data fetching for Angular Applications
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;blockquote&gt;
&lt;p&gt;The TanStack Query (also known as react-query) adapter for Angular applications&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Get rid of granular state management, manual refetching, and async spaghetti code. TanStack Query gives you declarative, always-up-to-date auto-managed queries and mutations that improve your developer experience.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;✅  Observable &amp;amp; Signal Support&lt;br&gt;
✅  Backend agnostic&lt;br&gt;
✅  Dedicated Devtools&lt;br&gt;
✅  Auto Caching&lt;br&gt;
✅  Auto Refetching&lt;br&gt;
✅  Window Focus Refetching&lt;br&gt;
✅  Polling/Realtime Queries&lt;br&gt;
✅  Parallel Queries&lt;br&gt;
✅  Dependent Queries&lt;br&gt;
✅  Mutations API&lt;br&gt;
✅  Automatic Garbage Collection&lt;br&gt;
✅  Paginated/Cursor Queries&lt;br&gt;
✅  Load-More/Infinite Scroll Queries&lt;br&gt;
✅  Request Cancellation&lt;br&gt;
✅  Prefetching&lt;br&gt;
✅  Offline Support&lt;br&gt;
✅  Data Selectors&lt;br&gt;
✅  SSR Support&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ngneat/query/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/ngneat/query/actions/workflows/ci.yml/badge.svg?branch=main" alt="@ngneat/query"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ngneat/query/" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d92fffaee8115c3287f01f070320a571b55d12ed331751e157f2f8c3ac361b76/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f6d6d6974697a656e2d667269656e646c792d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265" alt="commitizen"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ngneat/query/" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/25b3e6d0d42c98de74a98cbb4d149a1c09020cf6d1361993b72d7d5b8ffed363/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265" alt="PRs"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ngneat/query/" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0a77b7fa8214a6a503a9dede83117a1f8f97906c52a4d26bbc2db669cd9639b3/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64656f662d636f6e647563742d6666363962342e7376673f7374796c653d666c61742d737175617265" alt="coc-badge"&gt;&lt;/a&gt;
&lt;a href="https://github.com/semantic-release/semantic-release" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/cc8dcc7506c67f6e1399c83cc1226f142a3530c3c45d39716833f398642a9b3b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2532302532302546302539462539332541362546302539462539412538302d73656d616e7469632d2d72656c656173652d65353037392e7376673f7374796c653d666c61742d737175617265" alt="semantic-release"&gt;&lt;/a&gt;
&lt;a href="https://github.com/prettier/prettier" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/73bf666345824cddbd84570b8f345b3006c2e12d4c1b2fde4248a2b2c402dff6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374796c65645f776974682d70726574746965722d6666363962342e7376673f7374796c653d666c61742d737175617265" alt="styled with prettier"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Discover the innovative approach TanStack Query takes to state management, setting it apart from traditional methods. Learn about the motivation behind this design and explore its unique features &lt;a href="https://tanstack.com/query/v5/docs/react/overview#motivation" rel="nofollow noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;npm i @ngneat/query
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://stackblitz.com/edit/stackblitz-starters-bsrgez?file=src%2Fmain.ts" rel="nofollow noopener noreferrer"&gt;Stackblitz Example&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please be aware that the &lt;code&gt;@tanstack/query-core&lt;/code&gt; package must also be installed for the functionality to operate…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ngneat/query" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img 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" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TanStack" rel="noopener noreferrer"&gt;
        TanStack
      &lt;/a&gt; / &lt;a href="https://github.com/TanStack/query" rel="noopener noreferrer"&gt;
        query
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3421bf8139e5a415a3f1688f33f6de77d41f6962a22cd1bcc8e37876ac6d986a/68747470733a2f2f7374617469632e73636172662e73682f612e706e673f782d707869643d62653264386131312d393731322d346331642d393936332d353830623264346662313333"&gt;&lt;img src="https://camo.githubusercontent.com/3421bf8139e5a415a3f1688f33f6de77d41f6962a22cd1bcc8e37876ac6d986a/68747470733a2f2f7374617469632e73636172662e73682f612e706e673f782d707869643d62653264386131312d393731322d346331642d393936332d353830623264346662313333"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/TanStack/query/./media/header_query.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FTanStack%2Fquery%2F.%2Fmedia%2Fheader_query.png" alt="TanStack Query"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;
&lt;a href="https://www.npmjs.com/package/@tanstack/query-core" rel="nofollow noopener noreferrer"&gt;
  &lt;img alt="" src="https://camo.githubusercontent.com/6571ec64b623c96d8968731b9996683a1887379b65eab867404f5bb5c3e78abc/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646d2f4074616e737461636b2f71756572792d636f72652e737667"&gt;
&lt;/a&gt;
 &lt;a href="https://github.com/TanStack/query/stargazers" rel="noopener noreferrer"&gt;
  &lt;img alt="" src="https://camo.githubusercontent.com/9502306af33afdec1352a514d6fb7116394d6b8ece23c8fd254ebb1f4b1509d2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f54616e537461636b2f71756572792e7376673f7374796c653d736f6369616c266c6162656c3d53746172"&gt;
&lt;/a&gt;
&lt;a href="https://bundlejs.com/?q=%40tanstack%2Freact-query&amp;amp;config=%7B%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react-dom%22%5D%7D%7D&amp;amp;badge=" rel="nofollow noopener noreferrer"&gt;
  &lt;img alt="" src="https://camo.githubusercontent.com/12646d375890330bc1b326f73e59e758384e2a93a6006c2852edd5f845fa2385/68747470733a2f2f64656e6f2e62756e646c656a732e636f6d2f3f713d4074616e737461636b2f72656163742d717565727926636f6e6669673d7b25323265736275696c642532323a7b25323265787465726e616c2532323a5b25323272656163742532322c25323272656163742d646f6d2532325d7d7d2662616467653d64657461696c6564"&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;a href="https://github.com/TanStack/query#badge" rel="noopener noreferrer"&gt;
    &lt;img src="https://camo.githubusercontent.com/5f3b57745af83409bc673dec57e3eb360e1ec53b37ac29f81a319e347fa351c6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2532302532302546302539462539332541362546302539462539412538302d73656d616e7469632d2d72656c656173652d6531303037392e737667" alt="semantic-release"&gt;
  &lt;/a&gt;
&lt;a href="https://bestofjs.org/projects/tanstack-query" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6308019f46729f3a45923dcefca40c319eee5ffd0fdab99009f1f1ecf66edfc3/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f75726c3d68747470733a2f2f626573746f666a732d7365727665726c6573732e6e6f772e73682f6170692f70726f6a6563742d62616467653f66756c6c4e616d653d54616e537461636b253246717565727925323673696e63653d6461696c79" alt="Best of JS"&gt;&lt;/a&gt;
  &lt;a href="https://twitter.com/tan_stack" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a9127f8926af182ac74a619f2a997630cf9c9ad49775b1c670a97a028cdec278/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f74616e5f737461636b2e7376673f7374796c653d736f6369616c" alt="Follow @TanStack"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;a href="https://github.com/sponsors/tannerlinsley/" rel="noopener noreferrer"&gt;Become a Sponsor!&lt;/a&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;TanStack Query&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;An async state management library built to simplify fetching, caching, synchronizing, and updating server state.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Protocol‑agnostic fetching (REST, GraphQL, promises, etc.)&lt;/li&gt;
&lt;li&gt;Caching, refetching, pagination &amp;amp; infinite scroll&lt;/li&gt;
&lt;li&gt;Mutations, dependent queries &amp;amp; background updates&lt;/li&gt;
&lt;li&gt;Prefetching, cancellation &amp;amp; React Suspense support&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;a href="https://tanstack.com/query" rel="nofollow noopener noreferrer"&gt;Read the docs →&lt;/a&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Get Involved&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;We welcome issues and pull requests!&lt;/li&gt;
&lt;li&gt;Participate in &lt;a href="https://github.com/TanStack/query/discussions" rel="noopener noreferrer"&gt;GitHub discussions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chat with the community on &lt;a href="https://discord.com/invite/WrRKjPJ" rel="nofollow noopener noreferrer"&gt;Discord&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;See &lt;a href="https://github.com/TanStack/query/./CONTRIBUTING.md" rel="noopener noreferrer"&gt;CONTRIBUTING.md&lt;/a&gt; for setup instructions&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Partners&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;td&gt;
      &lt;a href="https://www.coderabbit.ai/?via=tanstack&amp;amp;dub_id=aCcEEdAOqqutX6OS" rel="nofollow noopener noreferrer"&gt;
        
          
          
          &lt;img src="https://camo.githubusercontent.com/527066ff45d6b396215072fa2671096fab872f9dcfba1763f642a2f82e5e52f8/68747470733a2f2f74616e737461636b2e636f6d2f6173736574732f636f64657261626269742d6c696768742d44564d4a326a48692e737667" height="40" alt="CodeRabbit"&gt;
        
      &lt;/a&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;a href="https://www.cloudflare.com?utm_source=tanstack" rel="nofollow noopener noreferrer"&gt;
        
          
          
          &lt;img src="https://camo.githubusercontent.com/09fe7b08ed94ebd15e5a4d9d126bc0bd68ffa7bf819105fdd8c9d3f7bda07887/68747470733a2f2f74616e737461636b2e636f6d2f6173736574732f636c6f7564666c6172652d626c61636b2d43507566615730422e737667" height="60" alt="Cloudflare"&gt;
        
      &lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/TanStack/query/./media/partner_logo.svg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FTanStack%2Fquery%2F.%2Fmedia%2Fpartner_logo.svg" alt="Query &amp;amp; you?" height="65"&gt;&lt;/a&gt;
&lt;p&gt;
We're looking for TanStack Query Partners to join our mission! Partner with us to push the boundaries of TanStack Query and build amazing things together.
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TanStack/query/mailto:partners@tanstack.com?subject=TanStack%20Query%20Partnership" rel="noopener noreferrer"&gt;&lt;b&gt;LET'S CHAT&lt;/b&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Explore the TanStack Ecosystem&lt;/h2&gt;

&lt;/div&gt;


&lt;ul&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/config" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack Config&lt;/b&gt;&lt;/a&gt; – Tooling for JS/TS packages&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/db" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack DB&lt;/b&gt;&lt;/a&gt; – Reactive sync client store&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/devtools" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack DevTools&lt;/b&gt;&lt;/a&gt; – Unified devtools panel&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/form" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack Form&lt;/b&gt;&lt;/a&gt; – Type‑safe form state&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/pacer" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack Pacer&lt;/b&gt;&lt;/a&gt; – Debouncing, throttling, batching 
&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/query" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack Query&lt;/b&gt;&lt;/a&gt; – Async state &amp;amp; caching&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/ranger" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack Ranger&lt;/b&gt;&lt;/a&gt; – Range &amp;amp; slider primitives&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/tanstack/router" rel="noopener noreferrer"&gt;&lt;b&gt;TanStack&lt;/b&gt;&lt;/a&gt;…&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TanStack/query" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Getting started&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To install the library and the developer tools, we will use the following commands:&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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2ASuTC4IFz5lg1gcGTSKCleg.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2ASuTC4IFz5lg1gcGTSKCleg.png" alt="Commands used to install TanStack Query and its Dev Tools" width="700" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have it installed, you will need to add the provider to the application and developer tools to the main component&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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AYgXZhuO28a-iwoGJxmdUpQ.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AYgXZhuO28a-iwoGJxmdUpQ.png" alt="Code snippet showing how to add the Query Client to the application" width="700" height="671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The developer tools are added to the main component, and they will show up as a floating button that enables and disables the developer tools.&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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AaOEEsoyOPi-FzWbL-p6StQ.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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AaOEEsoyOPi-FzWbL-p6StQ.png" alt="Code snippet showing how to add the Developer tools" width="700" height="809"&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%2Fr9puc8jgvmp3dvfbeiie.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%2Fr9puc8jgvmp3dvfbeiie.png" width="700" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make a request, you have to construct a query that requires a &lt;code&gt;queryKey&lt;/code&gt; that should be serializable and unique to the query’s data since it is the main thing that TanStack Query uses to cache the data. Also, it requires a query function to be used to make the API request and the returned data will be linked to the query 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%2Fiy5fm6anp1o0ssersvw5.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%2Fiy5fm6anp1o0ssersvw5.png" width="700" height="713"&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%2F4sdrdjmr1mb0cn8g4jw8.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%2F4sdrdjmr1mb0cn8g4jw8.png" width="700" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we have this, the &lt;code&gt;usersQuery&lt;/code&gt; will have the state of the request as a signal, like &lt;code&gt;usersQuery.isPending()&lt;/code&gt; or,&lt;code&gt;usersQuery.isError()&lt;/code&gt; and the data will also be able to be accessed at the &lt;a href="http://usersQuery.data" rel="noopener noreferrer"&gt;&lt;code&gt;usersQuery.data&lt;/code&gt;&lt;/a&gt;&lt;code&gt;()&lt;/code&gt; signal.&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%2Fbfkj4r652wbdt4bepfy1.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%2Fbfkj4r652wbdt4bepfy1.png" width="700" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The developer tools are composed of three sections. In the left menu section, it displays all the queries and mutations that have been executed, and you also have some tooling to filter, sort and order them. At the top, you can see the state that they are in, and there are also some controls to remove all the data or even simulate offline mode.&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%2F1cqhjr1v4ji2tb5lchi9.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%2F1cqhjr1v4ji2tb5lchi9.png" width="700" height="222"&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%2Fo3s6q6yq9rc2vgb5seob.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%2Fo3s6q6yq9rc2vgb5seob.png" width="700" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you select a query, you will see in the top right section query details that go from the query that was used to its current state and how many observers are linked to this query. This is especially useful since every observer will be notified of any changes if the query data changes.&lt;/p&gt;

&lt;p&gt;Also, you will see a set of controls to troubleshoot or see how the UI behaves by triggering different states for the query.&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%2F8d678jynb75x8zgr72bo.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%2F8d678jynb75x8zgr72bo.png" width="700" height="324"&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%2F4at5h422c498d2z8ibpy.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%2F4at5h422c498d2z8ibpy.png" width="700" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, at the bottom part, you will find an explorer for the data and a query that will help a lot while debugging what data was used or is currently cached.&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%2Ftz333ig3tw50kvfghokl.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%2Ftz333ig3tw50kvfghokl.png" width="700" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutations
&lt;/h2&gt;

&lt;p&gt;Mutations are the way to update the data and can be used when updating, deleting, or creating new items. It requires a mutation key and function as well, but it can also take which query keys to invalidate, meaning that once the mutation is done, any observer that has a query with the same key will automatically re-fetch data.&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%2F5ue505n8pznljqrfk5in.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%2F5ue505n8pznljqrfk5in.png" width="700" height="467"&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%2F6yhouhd951cdyavqmmg2.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%2F6yhouhd951cdyavqmmg2.png" width="700" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the following GIF, you can see how the data grid in the background is automatically updated after performing the mutation, and this is because it was specified which query key to invalidate, and the data grid has an observer for that 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%2F4dnj0z6lem7izv61oib4.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%2F4dnj0z6lem7izv61oib4.gif" width="760" height="788"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But this is not all! TanStack Query offers offline support, which means that all the mutations will be executed once it comes back online and it will also refresh all the observers for the related queries.&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%2Fmeij62m10aol946oy9p1.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%2Fmeij62m10aol946oy9p1.png" width="700" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the following GIF, you can see a demo of this, where, by using the developer tools offered by TanStack query, we can simulate offline mode and try to add a new user once we go back online. On the right side, you can see that the requests were executed, and now the data grid shows the updated list of users.&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%2F5i2143g611ujc7bvkgzl.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%2F5i2143g611ujc7bvkgzl.gif" width="720" height="438"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;TanStack Query is a library that can be used to handle all the server-state and thanks to it, you will get a lot of benefits out-of-the-box without having to implement custom solutions or write a lot of boilerplate code.&lt;/p&gt;

&lt;p&gt;Once you start using it, you will realize that there is not a lot of state to handle, and for the left-over, you might only need a light-weight and extensible solution like a &lt;a href="https://medium.com/ngconf/keeping-state-with-a-service-using-signals-bee652158ecf" rel="noopener noreferrer"&gt;Service with Signals&lt;/a&gt; or the &lt;a href="https://ngrx.io/guide/signals/signal-store/entity-management" rel="noopener noreferrer"&gt;Signal Store&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%2F8vqorqcolhah36jeg7bi.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%2F8vqorqcolhah36jeg7bi.png" width="700" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stay tuned, since I am working on follow-up articles exploring how to integrate it with the &lt;a href="https://medium.com/ngconf/taming-http-requests-with-an-abstract-api-service-61e73f39ed75" rel="noopener noreferrer"&gt;API service pattern&lt;/a&gt; &lt;a href="https://medium.com/ngconf/taming-http-requests-with-an-abstract-api-service-61e73f39ed75" rel="noopener noreferrer"&gt;I previously talked&lt;/a&gt; about and other more advanced features like using local storage to create a bullet-proof offline solution.&lt;/p&gt;




&lt;p&gt;For more articles and tips for better DevX and CX with Angular, Cypress, and Web Development, you can find me at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/alfrodo_perez" rel="noopener noreferrer"&gt;https://twitter.com/alfrodo_perez&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/alfredo-perez/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/alfredo-perez/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Learnings from Accessibility Workshop from Enterprise NG 2020</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Mon, 23 Nov 2020 15:57:15 +0000</pubDate>
      <link>https://forem.com/alfredoperez/learnings-from-accessibility-workshop-from-enterprise-ng-2020-2k57</link>
      <guid>https://forem.com/alfredoperez/learnings-from-accessibility-workshop-from-enterprise-ng-2020-2k57</guid>
      <description>&lt;p&gt;Here are some of the interesting points from the &lt;a href="https://www.ng-conf.org/2020/sessions/build-for-accessibility-with-angular/" rel="noopener noreferrer"&gt;Build for Accessibility with Angular&lt;/a&gt;  workshop by &lt;a href="https://twitter.com/Martine_Dowden" rel="noopener noreferrer"&gt;Martine Dowden&lt;/a&gt; and &lt;a href="https://twitter.com/mrdowden" rel="noopener noreferrer"&gt;Michael Dowden&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Labels are only for form fields. Do not use labels anywhere else&lt;/li&gt;
&lt;li&gt;Wave can be used to see the page structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbe6dm8wuaj2qkozid1ot.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%2Fi%2Fbe6dm8wuaj2qkozid1ot.png" alt="2020-11-23_9-17-59" width="355" height="1092"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AXE provides detailed information and links about the problem and possible resolutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9eoel9z7rrufem0o175r.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%2Fi%2F9eoel9z7rrufem0o175r.png" alt="image" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use unit tests that test for accessibility-related attributes like roles and aria labels
&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon butttons should have aria labels&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="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;iconButtons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debugElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAlK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button[mat-icon-button]&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;missingLabels&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iconButtons&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;¡&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
 &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;missingLabels&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeFalsy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mat list should have a role of 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="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;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debugElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mat-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
 &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;role*)).toEqual(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;);
});
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Prefer the use of the attribute &lt;code&gt;type="submit"&lt;/code&gt; for the buttons in the form instead of calling the method to submit from the &lt;code&gt;click&lt;/code&gt; handler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsliw8ffvvqrgchn01xhf.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%2Fi%2Fsliw8ffvvqrgchn01xhf.png" alt="image (1)" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prefer to enable/disable instead of showing/hiding when it is needed to show data depending on a condition. This helps to avoid confusing the user and having UI jumping around.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Links are links. Buttons are buttons. Div and spans are not buttons nor links.  Divs and spans miss some accessibility features for example they are not focusable and cannot be disabled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When having a link that is only an icon, use the &lt;code&gt;aria-hidden="true"&lt;/code&gt; and add a label&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"profile"&lt;/span&gt; 
   &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Profile"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="na"&gt;classss=&lt;/span&gt;&lt;span class="s"&gt;"material-icons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   Face
 &lt;span class="nt"&gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You should always have the &lt;code&gt;alt&lt;/code&gt; attribute on images, however, it can be blank. The &lt;code&gt;alt&lt;/code&gt; should describe what the image is trying to convey. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Decorative images can have an empty &lt;code&gt;alt&lt;/code&gt; attribute or have a &lt;code&gt;role="presentation"&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In alerts or notifications use &lt;code&gt;role="alert"&lt;/code&gt;, also use  &lt;code&gt;aria-live="polite"&lt;/code&gt; for status notifcaions and &lt;code&gt;aria-live="assertive"&lt;/code&gt; for something that requires attention &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Chrome Developer Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There is an option for accessibility in the "&lt;a href="https://developers.google.com/web/tools/chrome-devtools/accessibility/reference#audits" rel="noopener noreferrer"&gt;Audits&lt;/a&gt;" tab&lt;/li&gt;
&lt;li&gt;There is an accessibility tab in Chrome that contains the &lt;a href="https://developers.google.com/web/tools/chrome-devtools/accessibility/reference" rel="noopener noreferrer"&gt;accessibility tree&lt;/a&gt;, the &lt;a href="https://developers.google.com/web/tools/chrome-devtools/accessibility/reference#aria" rel="noopener noreferrer"&gt;ARIA attributes&lt;/a&gt;, and the accessibility-related computed attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Angular Material
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Make sure that the custom color pass the contrast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk1np8ptdz564kvnxib4x.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%2Fi%2Fk1np8ptdz564kvnxib4x.png" alt="2020-11-23_9-17-23" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.deque.com/axe/" rel="noopener noreferrer"&gt;AXE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wave.webaim.org/" rel="noopener noreferrer"&gt;WAVE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://accessibilityinsights.io/en/" rel="noopener noreferrer"&gt;Accessibility Insight&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://material.angular.io/guide/theming" rel="noopener noreferrer"&gt;Angular theming guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://material.angular.io/guide/typography" rel="noopener noreferrer"&gt;Angular typography guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fonts.google.com/" rel="noopener noreferrer"&gt;Google Fonts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webaim.org/resources/contrastchecker/" rel="noopener noreferrer"&gt;Webaim contrast checker&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="http://mcg.mbitson.com" rel="noopener noreferrer"&gt;Color palette builder&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://materialtheme.arcsine.dev/" rel="noopener noreferrer"&gt;Theme builder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Testing an Effect using observer-spy</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Fri, 25 Sep 2020 17:23:52 +0000</pubDate>
      <link>https://forem.com/alfredoperez/testing-an-effect-using-observer-spy-4anj</link>
      <guid>https://forem.com/alfredoperez/testing-an-effect-using-observer-spy-4anj</guid>
      <description>&lt;h3&gt;
  
  
  Updates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;September 29 2020&lt;/strong&gt;: refactor to use &lt;code&gt;fakeTime&lt;/code&gt; and &lt;code&gt;subscribeAndSpyOn&lt;/code&gt; as recommended by &lt;a href="https://twitter.com/shai_reznik" rel="noopener noreferrer"&gt;Shai Reznik&lt;/a&gt; 🎉&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;October 07 2020&lt;/strong&gt;: reafactor to use &lt;code&gt;subscribeSpyTo&lt;/code&gt;  as recommended by &lt;a href="https://twitter.com/shai_reznik" rel="noopener noreferrer"&gt;Shai Reznik&lt;/a&gt; 🎉&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you try the&lt;a href="https://github.com/hirezio/observer-spy" rel="noopener noreferrer"&gt;observer-spy&lt;/a&gt; library by &lt;a href="https://twitter.com/shai_reznik" rel="noopener noreferrer"&gt;Shai Reznik&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;It particularly makes testing ngrx effects an easy task and keeps them readable. &lt;/p&gt;

&lt;p&gt;To demonstrate this, I refactored the tests from &lt;code&gt;book.effects.spec.ts&lt;/code&gt; from the ngrx example application, and here are the differences...&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the success path
&lt;/h2&gt;

&lt;p&gt;Using marbles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a book.SearchComplete, with the books, on success, after the de-bounce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;book1&lt;/span&gt; &lt;span class="o"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;111&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;volumeInfo&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;Book&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;book2&lt;/span&gt; &lt;span class="o"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;222&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;volumeInfo&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;Book&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;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;book1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;book2&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;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FindBookPageActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&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;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BooksApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchSuccess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;books&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;actions$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-a---&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-a|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;books&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;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-----b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;googleBooksService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchBooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&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;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getTestScheduler&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;toBeObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using observer-spy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a book.SearchComplete, with the books, on success, after the de-bounce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;fakeTime&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;flush&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;book1&lt;/span&gt; &lt;span class="o"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;111&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;volumeInfo&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;Book&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;book2&lt;/span&gt; &lt;span class="o"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;222&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;volumeInfo&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;Book&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;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;book1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;book2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nx"&gt;actions$&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="nx"&gt;FindBookPageActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
        &lt;span class="nx"&gt;googleBooksService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchBooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;books&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;effectSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subscribeSpyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effectSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastValue&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;BooksApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchSuccess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;books&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the error path
&lt;/h2&gt;

&lt;p&gt;Using marbles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a book.SearchError if the books service throws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FindBookPageActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&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;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BooksApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchFailure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;errorMsg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unexpected Error. Try again later.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unexpected Error. Try again later.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="nx"&gt;actions$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-a---&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-#|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;error&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;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-----b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;googleBooksService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchBooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&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;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getTestScheduler&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;toBeObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using observer-spy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return a book.SearchError if the books service throws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;fakeTime&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;flush&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;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unexpected Error. Try again later.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nx"&gt;actions$&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="nx"&gt;FindBookPageActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
        &lt;span class="nx"&gt;googleBooksService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchBooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&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;throwError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effectSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subscribeSpyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effectSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastValue&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;BooksApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchFailure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;errorMsg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing when the effect does not do anything
&lt;/h2&gt;

&lt;p&gt;Using marbles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`should not do anything if the query is an empty string`&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;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FindBookPageActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nx"&gt;actions$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-a---&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&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;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;---&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getTestScheduler&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;toBeObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using observer-spy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`should not do anything if the query is an empty string`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;fakeTime&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;flush&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;actions$&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="nx"&gt;FindBookPageActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effectSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subscribeSpyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;effectSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastValue&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBeUndefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the working test here: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alfredoperez/ngrx-observer-spy/blob/master/projects/example-app/src/app/books/effects/book.effects.spec.ts" rel="noopener noreferrer"&gt;https://github.com/alfredoperez/ngrx-observer-spy/blob/master/projects/example-app/src/app/books/effects/book.effects.spec.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What do you think? Which one do you prefer?&lt;/p&gt;

</description>
      <category>angular</category>
      <category>ngrx</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Where to initialize components selectors in Angular?</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Wed, 16 Sep 2020 13:51:39 +0000</pubDate>
      <link>https://forem.com/alfredoperez/where-to-initialize-components-selectors-in-angular-a0g</link>
      <guid>https://forem.com/alfredoperez/where-to-initialize-components-selectors-in-angular-a0g</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When using the selector in the component, it is recommended not to initialize them in the declaration and instead initialize them in the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FindBookPageComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;searchQuery$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;books$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Book&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;loading$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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;error$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;fromBooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchQuery$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromBooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectSearchQuery&lt;/span&gt;&lt;span class="p"&gt;),&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="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;books$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromBooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectSearchResults&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromBooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectSearchLoading&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;error$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fromBooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectSearchError&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FindBookPageActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchBooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Angular Type Safety
&lt;/h2&gt;

&lt;p&gt;Initializing in the constructor helps because when using the &lt;strong&gt;strict mode in TypeScript&lt;/strong&gt;, the compiler will not be able to know that the selectors were initialized on &lt;code&gt;ngOnInit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The strictPropertyInitialization was added by default in the &lt;code&gt;--strict&lt;/code&gt; mode in Angular 9.&lt;/p&gt;

&lt;p&gt;The following checks where also added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"//"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitAny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitReturns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitThis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noFallthroughCasesInSwitch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strictNullChecks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The strict-mode allows us to write much safe and robust application. Honestly, I think all Angular application should be written in strict-mode, but it is a little hard to understand every error messages caused by the compiler.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Angular 9 Strict Mode &lt;a href="https://indepth.dev/a-look-at-major-features-in-the-angular-ivy-version-9-release/#strict-mode" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Angular Type safety &lt;a href="https://medium.com/lacolaco-blog/guide-for-type-safe-angular-6e9562499d93" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Strict Property Initialization in TypeScript &lt;a href="https://mariusschulz.com/articles/strict-property-initialization-in-typescript" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>ngrx</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Angular 11 - Setting up Jest</title>
      <dc:creator>Alfredo Perez</dc:creator>
      <pubDate>Mon, 06 Jul 2020 21:21:59 +0000</pubDate>
      <link>https://forem.com/alfredoperez/angular-10-setting-up-jest-2m0l</link>
      <guid>https://forem.com/alfredoperez/angular-10-setting-up-jest-2m0l</guid>
      <description>&lt;p&gt;This is a quick guide to setup Jest in your new Angular 10 application&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install Jest
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install jest @types/jest jest-preset-angular --save-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Uninstall Karma
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter @types/jasmine @types/jasminewd2 jasmine-core jasmine-spec-reporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Remove test from &lt;code&gt;angular.json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Remove the test section from &lt;code&gt;angular.json&lt;/code&gt;, this section looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"builder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@angular-devkit/build-angular:karma"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/test.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"polyfills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/polyfills.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"tsConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsconfig.spec.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"karmaConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"karma.conf.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="s2"&gt;"src/favicon.ico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="s2"&gt;"src/assets"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"styles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="s2"&gt;"src/styles.scss"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Remove &lt;code&gt;karma.conf.js&lt;/code&gt; and &lt;code&gt;src/test.ts&lt;/code&gt; files
&lt;/h2&gt;

&lt;h2&gt;
  
  
  5. Create &lt;code&gt;setupJest.ts&lt;/code&gt; file
&lt;/h2&gt;

&lt;p&gt;This file should have 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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest-preset-angular&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;h2&gt;
  
  
  6. Modify &lt;code&gt;tsconfig.spec.json&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./out-tsc/spec"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src/polyfills.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.spec.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.d.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Modify &lt;code&gt;package.json&lt;/code&gt; file
&lt;/h2&gt;

&lt;p&gt;Modify the test scripts to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"test:coverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest --coverage"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add Jest configuration to the end of this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest-preset-angular"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"setupFilesAfterEnv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;rootDir&amp;gt;/setupJest.ts"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"testPathIgnorePatterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;rootDir&amp;gt;/node_modules/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;rootDir&amp;gt;/dist/"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"globals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ts-jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tsConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;rootDir&amp;gt;/tsconfig.spec.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"stringifyContentPathRegex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.html$"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>angular</category>
      <category>jest</category>
      <category>testing</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
