<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gaëtan Redin</title>
    <description>The latest articles on DEV Community by Gaëtan Redin (@gaetanrdn).</description>
    <link>https://dev.to/gaetanrdn</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%2F698644%2F86d52bec-6b19-4383-a838-1811b70e1fef.jpeg</url>
      <title>DEV Community: Gaëtan Redin</title>
      <link>https://dev.to/gaetanrdn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gaetanrdn"/>
    <language>en</language>
    <item>
      <title>Angular multi translation files</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Fri, 23 Jun 2023 09:44:10 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/angular-multi-translation-files-469h</link>
      <guid>https://dev.to/gaetanrdn/angular-multi-translation-files-469h</guid>
      <description>&lt;p&gt;Done with ngx-translate&lt;/p&gt;

&lt;p&gt;I’ve been using &lt;a href="https://github.com/ngx-translate/core"&gt;&lt;em&gt;ngx-translate&lt;/em&gt;&lt;/a&gt; for as long as I can remember. I find it easier than the native Angular i18n solution.&lt;/p&gt;

&lt;p&gt;Translations is often complicated to maintain because the translation file grows as the application grows.&lt;/p&gt;

&lt;p&gt;My use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don’t want to have all my translations in one single file&lt;/li&gt;
&lt;li&gt;I want to know where to write my translations&lt;/li&gt;
&lt;li&gt;I want to keep a common file because there are still common wordings&lt;/li&gt;
&lt;li&gt;Bonus, I want to make that more readable, reviewable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Architecture
&lt;/h3&gt;

&lt;p&gt;This is how my assets are architectured to let the solution works. this can be done in an another way but I don’t variabilize the path to the common files (which can be done if you want to handle it).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- assets
  - ...
  - i18n
    - common
      - fr.json
      - en.json
      - ...
    - feature-1
      - fr.json
      - en.json
      - ...
    - feature-2
      - fr.json
      - en.json
      - ...
    - ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom loader
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/ngx-translate/core#write--use-your-own-loader"&gt;here&lt;/a&gt; is the doc about how to create a custom loader.&lt;/p&gt;

&lt;p&gt;Let’s read my suggestion for a custom loader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// model for a resource to load
export type Resource = { prefix: string; suffix: string };


export class MultiTranslateHttpLoader implements TranslateLoader {
  resources: Resources[];
  withCommon: boolean;

  constructor(
    private readonly http: HttpClient,
    { resources, withCommon = true }: { resources: Resource[], withCommon?: boolean }
  ) {
    this.resources = resources;
    this.withCommon = withCommon;
  }

  getTranslation(lang: string): Observable&amp;lt;Record&amp;lt;string, unknown&amp;gt;&amp;gt; {
    let resources: Resource[] = [...this.resources];

    if (this.withCommon) {
      // order matters! like this, all translations from common can be overrode with features' translations
      resources = [
        { prefix: './assets/i18n/common/', suffix: '.json' }, 
        ...resources
      ];
    }

    return forkJoin(resources.map((config: Resource) =&amp;gt; {
      return this.http.get&amp;lt;Record&amp;lt;string, unknown&amp;gt;&amp;gt;(`${config.prefix}${lang}${config.suffix}`);
    })).pipe(
      map((response: Record&amp;lt;string, unknown&amp;gt;[]) =&amp;gt; 
        mergeObjectsRecursively(response)),
    );
  }
}

export const mergeObjectsRecursively = 
    (objects: Record&amp;lt;string, unknown&amp;gt;[]): Record&amp;lt;string, unknown&amp;gt; {
  const mergedObject: Record&amp;lt;string, unknown&amp;gt; = {};

  for (const obj of objects) {
    for (const key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        if (typeof obj[key] === 'object' &amp;amp;&amp;amp; obj[key] !== null) {
          mergedObject[key] = mergeObjectsRecursively([mergedObject[key], obj[key]]);
        } else {
          mergedObject[key] = obj[key];
        }
      }
    }
  }

  return mergedObject;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to use it
&lt;/h3&gt;

&lt;p&gt;With common translations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TranslateModule.(forRoot|forChild)({
  loader: {
    provide: TranslateLoader,
    useFactory: (http: HttpClient): MultiTranslateHttpLoader =&amp;gt; {
      return new MultiTranslateLoader(http, {
        resources: [
          { prefix: './assets/i18n/feature-1/', suffix: '.json' },
          { prefix: './assets/i18n/feature-2/', suffix: '.json' },
          ...
        ],
      });
    },
    deps: [HttpClient],
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without common translations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TranslateModule.(forRoot|forChild)({
  loader: {
    provide: TranslateLoader,
    useFactory: (http: HttpClient): MultiTranslateHttpLoader =&amp;gt; {
      return new MultiTranslateLoader(http, {
        withCommon: false,
        resources: [
          { prefix: './assets/i18n/feature-1/', suffix: '.json' },
          { prefix: './assets/i18n/feature-2/', suffix: '.json' },
          ...
        ],
      });
    },
    deps: [HttpClient],
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// assets/i18n/feature-1/en.json

{
  "HELLO": "HELLO",
  "CIVILITIES": {
    "MR": "Mister",
    "MS": "Miss"
  }
}

// assets/i18n/feature-2/en.json
{
  "TITLE": "LONG TITLE",
}

// generated translations
{
  "HELLO": "HELLO",
  "CIVILITIES": {
    "MR": "Mister",
    "MS": "Miss"
  },
  "TITLE": "LONG TITLE",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Now you can split your translation files, you will see how it will be easier to read, maintain and review. Don’t forget to use the lazy loading feature (from Angular) and then you will load only translation files required for a specific route.&lt;/p&gt;

&lt;p&gt;If you need more details or want to give me your opinion let me a comment, don’t hezitate.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>ngxtranslate</category>
      <category>multitranslations</category>
    </item>
    <item>
      <title>From Angular 14 to Angular 16</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Wed, 24 May 2023 16:27:36 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/from-angular-14-to-angular-16-18k7</link>
      <guid>https://dev.to/gaetanrdn/from-angular-14-to-angular-16-18k7</guid>
      <description>&lt;p&gt;What I have changed in my coding approach&lt;/p&gt;

&lt;p&gt;From v14, The Angular team has certainly been busy. I work with Angular from the beginning of Angular 2. And I have never seen so many features in such a short amount of time.&lt;/p&gt;

&lt;p&gt;My last article was edited 7 months ago and it’s time to share with you how I have changed my daily coding approach since v14.&lt;/p&gt;

&lt;p&gt;In some cases, it will be a before / after comparison with the feature to show you quickly the benefits. It will allow you to understand how to update your code with examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Angular v14
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Standalone components
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my.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="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// my.module.ts&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;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MyComponent&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;AnotherStuff&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my.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="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AnotherStuff&lt;/span&gt;&lt;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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One less file to maintain and less code. Be careful, the standalone component must be imported &lt;strong&gt;&lt;em&gt;.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;this can also be applied to the &lt;em&gt;AppComponent,&lt;/em&gt; you “just” have to te rewrite your main.ts as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;importProvidersFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* the imported modules in the app.module file*/&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now there are many features you can provide without module, think to read the &lt;a href="https://angular.io/guide/standalone-components"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Typed Angular Forms
&lt;/h4&gt;

&lt;p&gt;So highly anticipated and at the same time, somewhat disappointing for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; ,&lt;/p&gt;

&lt;p&gt;we only worked with an &lt;em&gt;any&lt;/em&gt; type for all of our forms. It was a nightmare to maintain and to evolve.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="p"&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;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;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;// no type check&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;.(...);&lt;/span&gt; &lt;span class="c1"&gt;// no autocompletion&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// no type check every thing is ok...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt; ,&lt;/p&gt;

&lt;p&gt;all of these points are resolved but there is still a problem. I find the syntax a bit indigest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FormType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FormType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FormType&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;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="p"&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;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So why I find it indigest? Because our data models are not defined with Form[Control|Group|Array] types. it’s more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;lastName&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="c1"&gt;// no FormControl&lt;/span&gt;
  &lt;span class="nl"&gt;firstName&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="c1"&gt;// no FormControl&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So how to use the Data model with our FormGroup? Until now, no native solution, we have to use som hack.&lt;/p&gt;

&lt;p&gt;I choose to use an intermediate type:&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;type&lt;/span&gt; &lt;span class="nx"&gt;FormTyped&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not handle sub FormGroup or FormArray but in most of my cases it does the work. If you have any suggestion to improve this, don’t hezitate let me a comment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Type checking, autocompletion.&lt;/p&gt;

&lt;p&gt;If you want / need to stay like in the old way you can use the UntypedForm[Control|Group|Array|Builder].&lt;/p&gt;

&lt;h4&gt;
  
  
  Bind to protected component members
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; ,&lt;/p&gt;

&lt;p&gt;only public properties could be accessible from the HTML template. It has often been a bit frustrating for me to make some properties public just because of the template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{{ myProperty }}`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;myProperty&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt; ,&lt;/p&gt;

&lt;p&gt;we can really choose what to publicly expose from our component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`{{ myProperty }}`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;myProperty&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Angular v15
&lt;/h3&gt;

&lt;h4&gt;
  
  
  RouterTestingHarness
&lt;/h4&gt;

&lt;p&gt;Since &lt;a href="https://angular.io/api/router/testing/RouterTestingHarness"&gt;RouterTestingHarness&lt;/a&gt; has come, I find it easier to test routing, check guards calls and co.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello {{name}}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TestCmp&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&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;navigates to routed component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&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;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configureTestingModule&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;provideRouter&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&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;TestCmp&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;harness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;RouterTestingHarness&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activatedComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;harness&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigateByUrl&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="nx"&gt;TestCmp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activatedComponent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeInstanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TestCmp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I use a specific routes.spec.ts file for only testing routing. It makes tests more readable.&lt;/p&gt;

&lt;p&gt;Before, I often have seen projects without routing tests. Now, there is no excuse.&lt;/p&gt;

&lt;h4&gt;
  
  
  Simplify routes files and use Router API
&lt;/h4&gt;

&lt;p&gt;I don’t want to rewrite an app-routing.module.ts… I know you know what I mean. But here how to do now, bonus it’s a lazy load example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.routes.ts&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;appRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lazy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lazy/lazy.routes&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lazyRoutes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}];&lt;/span&gt;

&lt;span class="c1"&gt;// lazy.routes.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;Routes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;LazyComponent&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;./lazy.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lazyRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&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;LazyComponent&lt;/span&gt;&lt;span class="p"&gt;}];&lt;/span&gt;

&lt;span class="c1"&gt;// main.tous&lt;/span&gt;
&lt;span class="nx"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appRoutes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Directive composition API
&lt;/h4&gt;

&lt;p&gt;I have written two articles about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/angular-15-hostdirectives-287b"&gt;Angular 15: hostDirectives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/angular-15-hostdirectives-injectiontoken-kk0"&gt;Angular 15: hostDirectives + InjectionToken&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Functional router guards
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; ,&lt;/p&gt;

&lt;p&gt;we have to define our guard in a specific file like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;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="nx"&gt;AuthGuard&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;CanActivate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&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;authService&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;){};&lt;/span&gt;
  &lt;span class="nx"&gt;canActivate&lt;/span&gt;&lt;span class="p"&gt;(...):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;UrlTree&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn&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;authService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&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;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createUrlTree&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt; ,&lt;/p&gt;

&lt;p&gt;you simply can do it in your routes file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;canActivate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;UrlTree&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;authService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthService&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;authService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;createUrlTree&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you can extract it into a plain function, it depends on your needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;UrlTree&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;authService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthService&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;authService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;createUrlTree&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/contact&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;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;canActivate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;More readable, less code. Just what we love.&lt;/p&gt;

&lt;h4&gt;
  
  
  Self-closing tags
&lt;/h4&gt;

&lt;p&gt;It allows to streamline the HTML code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- before --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;app-my-component&amp;gt;&amp;lt;/app-my-component&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- now --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;app-my-component/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Angular v16
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Required inputs
&lt;/h4&gt;

&lt;p&gt;How to handle required inputs before Angular v16? You will find the answer in one of my previous article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/gaetanrdn/how-to-handle-required-inputs-in-angular-584o"&gt;How to Handle Required Inputs in Angular&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since v16, no hack is needed, even custom decorators are deprecated, you just have to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;required&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="nx"&gt;myProp&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Passing router data as component inputs
&lt;/h4&gt;

&lt;p&gt;I think this is one of my favourite feature. Why? Because we don’t need to have the ActivatedRoute dependency in our component, we don’t need to handle static or resolved route data, path parameters, matrix parameters, and query parameters no more.&lt;/p&gt;

&lt;p&gt;Before&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;activatedRoute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ActivatedRoute&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;hero$&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;Hero&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activatedRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;heroId&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getHero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heroId&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your component you just have to define the prop with the @Input decorator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heroId&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;hero$&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;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getHero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heroId&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;Don’t forget to add this in your routing config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appRoutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withComponentInputBinding&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your component is more generic and can be used in all cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I don’t talk about signal here. It’s a great feature but for me it will be very interesting from the v16.1 and the add of the transform feature for Inputs.&lt;/p&gt;

&lt;p&gt;As we can see, many features to make Angular easier, more accessible and more readable. But yes, I’m like you I’m waiting for all the signals feature. It seems we have to wait until the v17.&lt;/p&gt;

&lt;p&gt;If you need more details about a feature let me a comment, don’t hezitate.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>cleancode</category>
      <category>overview</category>
      <category>angular</category>
      <category>features</category>
    </item>
    <item>
      <title>Angular testing</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Wed, 01 Mar 2023 10:30:49 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/angular-testing-13p5</link>
      <guid>https://dev.to/gaetanrdn/angular-testing-13p5</guid>
      <description>&lt;p&gt;How to mock natively component dependencies&lt;/p&gt;

&lt;p&gt;Hey, I know there are many documentation about this but it seems to not be effective. Why? People does not read documentation… Every time I start a new mission, the developers don’t test their components or do it wrong. In the last case, they never mock their component dependencies and that’s a problem because it’s not a unit test anymore.&lt;/p&gt;

&lt;p&gt;I think a little reminder is necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context
&lt;/h3&gt;

&lt;p&gt;I’m developing a CancelButtonComponent. It’s a material button with a specific text and specific mat-button properties.&lt;/p&gt;

&lt;p&gt;I’m working on a standalone component.&lt;/p&gt;

&lt;p&gt;I’m using jest and snapshot testing to be sure that the genereted HTML is as expected.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'app-cancel-button',
  standalone: true,
  template: `&amp;lt;button mat-button color="primary"&amp;gt;Cancel&amp;lt;/button&amp;gt;`,
  imports: [MatButtonModule]
})
export class CancelButton {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a really simple component and here we just have to control the generated HTML.&lt;/p&gt;

&lt;p&gt;Here’s the spec file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('CancelButtonComponent', () =&amp;gt; {
  let component: CancelButtonComponent;
  let fixture: ComponentFixture&amp;lt;CancelButtonComponent&amp;gt;;

  beforeEach(async () =&amp;gt; {
    await TestBed.configureTestingModule({
      imports: [CancelButtonComponent],
    }).compileComponents();

    fixture = TestBed.createComponent(CancelButtonComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () =&amp;gt; {
    expect(fixture.nativeElement).toMatchSnapshot();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I don’t mock anything.&lt;/p&gt;

&lt;p&gt;That’s the generated snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CancelButtonComponent should create 1`] = `
&amp;lt;div
  id="root0"
&amp;gt;
  &amp;lt;button
    class="mdc-button mat-mdc-button mat-primary mat-mdc-button-base"
    color="primary"
    mat-button=""
  &amp;gt;
    &amp;lt;span
      class="mat-mdc-button-persistent-ripple mdc-button__ripple"
    /&amp;gt;
    &amp;lt;span
      class="mdc-button__label"
    &amp;gt;
      Cancel
    &amp;lt;/span&amp;gt;
    &amp;lt;span
      class="mat-mdc-focus-indicator"
    /&amp;gt;
    &amp;lt;span
      class="mat-ripple mat-mdc-button-ripple"
      matripple=""
    /&amp;gt;
    &amp;lt;span
      class="mat-mdc-button-touch-target"
    /&amp;gt;
  &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t think this snapshot is really relevant. I don’t care about MatButton specific css classes, I don’t care about all generated spans and that make a big snapshot just for one line of HTML to test. Snapshot is a part of your code it MUST be reviewed.&lt;/p&gt;

&lt;p&gt;Now, let’s do better with a mock.&lt;/p&gt;

&lt;p&gt;Here’s a simple mock for the MatButton:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: '[mat-button]',
  standalone: true,
  template: ` &amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;`,
})
class MatButtonMock {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use the projection () to let the button’s text appear in the snapshot.&lt;/p&gt;

&lt;p&gt;And that’s how to use it in the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;beforeEach(async () =&amp;gt; {
    await TestBed.configureTestingModule({
      imports: [CancelButtonComponent],
    })
      .overrideComponent(CancelButtonComponent, {
        remove: {
          imports: [MatButtonModule],
        },
        add: {
          imports: [MatButtonMock],
        },
      })
      .compileComponents();

    ...
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We just tell to TestBed to remove the import of MatButtonModule for CancelButtonComponent and to replace it with our MatButtonMock.&lt;/p&gt;

&lt;p&gt;Let’s see the snapshot now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CancelButtonComponent should create 1`] = `
&amp;lt;div
  id="root0"
&amp;gt;
  &amp;lt;button
    color="primary"
    mat-button=""
  &amp;gt;
    Cancel
  &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s so better. I see only the specifities of my CancelButtonComponent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;usage of a mat-button&lt;/li&gt;
&lt;li&gt;set the color to primary&lt;/li&gt;
&lt;li&gt;set the text to “Cancel”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s more readable, it’s more reviewable and it’s more relevant.&lt;/p&gt;

&lt;p&gt;Angular offers other methods to override a component in a test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;overrideComponent&lt;/li&gt;
&lt;li&gt;overridePipe&lt;/li&gt;
&lt;li&gt;overrideDirective&lt;/li&gt;
&lt;li&gt;overrideModule&lt;/li&gt;
&lt;li&gt;overrideProviders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I put here the full spec file if you need it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('CancelButtonComponent', () =&amp;gt; {
  let component: CancelButtonComponent;
  let fixture: ComponentFixture&amp;lt;CancelButtonComponent&amp;gt;;

  beforeEach(async () =&amp;gt; {
    await TestBed.configureTestingModule({
      imports: [CancelButtonComponent],
    })
      .overrideComponent(CancelButtonComponent, {
        remove: {
          imports: [MatButtonModule],
        },
        add: {
          imports: [MatButtonMock],
        },
      })
      .compileComponents();

    fixture = TestBed.createComponent(CancelButtonComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () =&amp;gt; {
    expect(fixture.nativeElement).toMatchSnapshot();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Never mind, you are using jest, Karma/jasmine or another testing tool. You always must mock your dependencies (components, services, pipes…) to avoid to be impacted by a third party in your test. And also because we are speaking about unit test.&lt;/p&gt;

&lt;p&gt;If you need more examples or other testing use case, let me know in comment. I will try to help you.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/jest-and-angular-install-fg3"&gt;Install Jest for Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-table-of-contents-d24232940dd4"&gt;Angular for everyone: All about it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mock</category>
      <category>standalone</category>
      <category>components</category>
      <category>angular</category>
    </item>
    <item>
      <title>Angular 15: hostDirectives + InjectionToken</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sun, 27 Nov 2022 16:54:36 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/angular-15-hostdirectives-injectiontoken-kk0</link>
      <guid>https://dev.to/gaetanrdn/angular-15-hostdirectives-injectiontoken-kk0</guid>
      <description>&lt;p&gt;How to avoid duplications and override directive’s inputs value with injection token&lt;/p&gt;

&lt;p&gt;Hey, as you know, Angular 15 has been come with a new great feature: hostDirectives.&lt;/p&gt;

&lt;p&gt;Since Angular 15 release, I have made some refacto in my angular project and I found this use case: I extend a directive which have an input to handle css classes. In the subsclass, I override the input property and set it a default value. I wanted to remove the extends and find a way to use only the new concept of hostDirectives. And Actually, I found a way which result of the combination of hostDirectives and injection token.&lt;/p&gt;

&lt;p&gt;Let’s go for the demo.&lt;/p&gt;

&lt;p&gt;Here is a directive which is used to be extended to add some css classes :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Directive({
  selector: '[typography]',
  standalone: true,
  host: {
    '[class.headline-1]': 'typography === "headline-1"',
    ...
    '[class.headline-6]': 'typography === "headline-6"',
    '[class.subtitle-1]': 'typography === "subtitle-1"',
    '[class.subtitle-2]': 'typography === "subtitle-2"',
    '[class.body-1]': 'typography === "body-1"',
    '[class.body-2]': 'typography === "body-2"',
    '[class.button-typo]': 'typography === "button"',
    '[class.caption]': 'typography === "caption"',
  },
})
export class TypographyDirective {
  @Input('rcTypography') public typography!:
    | 'headline-1'
    | 'headline-2'
    | 'headline-3'
    | 'headline-4'
    | 'headline-5'
    | 'headline-6'
    | 'subtitle-1'
    | 'subtitle-2'
    | 'body-1'
    | 'body-2'
    | 'button'
    | 'caption';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is how it was used before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'my-selector',
  standalone: true,
  template: `...`,
})
export class SubClassComponent extends TypographyDirective {
  public override readonly typography = 'body-2';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why? because I wanted to use the advantage of the directive directly on my component and to not rewrite the css classes handling.&lt;/p&gt;

&lt;p&gt;It’s ok, it works, it’s readable.&lt;/p&gt;

&lt;p&gt;But what if I need to extend a more functionnal class? I have to use mixins ? yes ok I know it’s possible too. But it needs to write more code each time.&lt;/p&gt;

&lt;p&gt;A simple way will be to use the combination of hostDirectives metadata and an injection token.&lt;/p&gt;

&lt;p&gt;Let’s do it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// the injection token
export const TYPOGRAPHY_TOKEN: InjectionToken&amp;lt;TypographyDirective['typography']&amp;gt; = new InjectionToken&amp;lt;
  TypographyDirective['typography']
&amp;gt;('TYPOGRAPHY_TOKEN');

// the updated base directive
@Directive({
  selector: '[typography]',
  standalone: true,
  host: {
    '[class.headline-1]': 'typography === "headline-1"',
    ...
    '[class.headline-6]': 'typography === "headline-6"',
    '[class.subtitle-1]': 'typography === "subtitle-1"',
    '[class.subtitle-2]': 'typography === "subtitle-2"',
    '[class.body-1]': 'typography === "body-1"',
    '[class.body-2]': 'typography === "body-2"',
    '[class.button-typo]': 'typography === "button"',
    '[class.caption]': 'typography === "caption"',
  },
})
export class TypographyDirective {
  @Input('rcTypography') public typography:
    | 'headline-1'
    | 'headline-2'
    | 'headline-3'
    | 'headline-4'
    | 'headline-5'
    | 'headline-6'
    | 'subtitle-1'
    | 'subtitle-2'
    | 'body-1'
    | 'body-2'
    | 'button'
    | 'caption' 
    | null = inject(TYPOGRAPHY_TOKEN, { optional: true });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The property will be valued by the Input value of by the token value by default. Here’s the updated subclass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'my-selector',
  standalone: true,
  template: `...`,
   hostDirectives: [
    {
      directive: TypographyDirective,
    },
  ],
  providers: [
    {
      provide: TYPOGRAPHY_TOKEN,
      useValue: 'body-2',
    },
  ],
})
export class SubClassComponent {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et voila! My subclass is free to extend another class, the final code is still readable, it’s perfect. Hmm, wait, what if I want to allow to override the typography property with an input ?&lt;/p&gt;

&lt;p&gt;Okay, you just have to indicate the input property like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'my-selector',
  standalone: true,
  template: `...`,
   hostDirectives: [
    {
      directive: TypographyDirective,
      inputs: ['typography']
    },
  ],
  providers: [
    {
      provide: TYPOGRAPHY_TOKEN,
      useValue: 'body-2',
    },
  ],
})
export class SubClassComponent {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and call it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;my-selector ... [typography]="'subtitle-2'"&amp;gt;&amp;lt;/my-selector&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if the injection token is valued, the input value will have the final word.&lt;/p&gt;

&lt;p&gt;I hope this will help someone to simplify his code et to write more readable code.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>cleancode</category>
      <category>components</category>
      <category>directives</category>
      <category>angular</category>
    </item>
    <item>
      <title>Angular 15: hostDirectives</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Thu, 24 Nov 2022 10:41:15 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/angular-15-hostdirectives-287b</link>
      <guid>https://dev.to/gaetanrdn/angular-15-hostdirectives-287b</guid>
      <description>&lt;p&gt;How to avoid duplications and to keep access to properties&lt;/p&gt;

&lt;p&gt;Hey, as you know, Angular 15 has been come with a new great feature: hostDirectives.&lt;/p&gt;

&lt;p&gt;There are already many articles about it but I never read something about how to keep access to the directives properties yet. I will focus on an explicit example that will all know: disabled state.&lt;/p&gt;

&lt;p&gt;Let’s start with an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: '...',
  host: {
    ...
    '[class.my-disabled-class]': 'disabled',
    '[attr.disabled]': 'disabled || null',
    '[attr.aria-disabled]': 'disabled || null',
    ...
  },
  template: `...`
})
class MyComponent {
  @Input() public disabled = false;

  // many other stuffs
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This little piece of code is often duplicated. Sometimes, people use mixins to be DRY but you still have to repeat the host part.&lt;/p&gt;

&lt;p&gt;So how to do better?&lt;/p&gt;

&lt;p&gt;First, we will create the DisabledState directive like as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Directive({
  selector: '[myDisabledState]',
  standalone: true, // available since Angular 14
  host: {
    '[attr.disabled]': 'disabled || null',
    '[attr.aria-disabled]': 'disabled || null',
    '[class.rc-disabled]': 'disabled',
  },
})
export class DisabledStateDirective {
  @Input() public disabled = false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, we can simplify our component like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: '...',
  host: {
    ...
  },
  hostDirectives: [{
    directive: DisabledStateDirective,
    inputs: ['disabled'],
  }],
  template: `...`
})
class MyComponent {
  // many other stuffs
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More readable right? And also, better separation of concerns. One point, I loose the access to the disabled property. How to know in my component if it’s disabled or not? Thanks to the great Angular, it’s really easy to do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: '...',
  host: {
    ...
  },
  hostDirectives: [{
    directive: DisabledStateDirective,
    inputs: ['disabled'],
  }],
  template: `...`
})
class MyComponent {
  private readonly _disabledState: DisabledStateDirective = inject(DisabledStateDirective, { self: true });

  public myFunction(): void {
    if (this.disabledState.disabled) {
      ...
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can just use the inject method to get the DisabledStateDirective , we use the self: true to simplify the search to it.&lt;/p&gt;

&lt;p&gt;Et voilà (I’m still french sorry).&lt;/p&gt;

&lt;p&gt;I hope this little example will help someone to simplify his code et to write more readable code.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>hostdirectives</category>
      <category>refactoring</category>
      <category>components</category>
    </item>
    <item>
      <title>Development and Mikado</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sun, 31 Jul 2022 14:18:09 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/development-and-mikado-4gel</link>
      <guid>https://dev.to/gaetanrdn/development-and-mikado-4gel</guid>
      <description>&lt;p&gt;When I chose the Mikado method&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8zzf0uU_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/640/1%2AHFl48kKpKKD0iNdX5CuRlg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8zzf0uU_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/640/1%2AHFl48kKpKKD0iNdX5CuRlg.jpeg" alt="" width="640" height="409"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image par &lt;a href="%E2%80%9Dhttps://pixabay.com/fr/users/wilhei-883152/?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=742769%22"&gt;Willi Heidelbach&lt;/a&gt; de &lt;a href="%E2%80%9Dhttps://pixabay.com/fr//?utm_source=link-attribution&amp;amp;utm_medium=referral&amp;amp;utm_campaign=image&amp;amp;utm_content=742769%22"&gt;Pixabay&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Context
&lt;/h3&gt;

&lt;p&gt;During a front-end project in a client (in Angular), we got new directives for our project. One consequence of this was to migrate a lot of components into libraries.&lt;/p&gt;

&lt;p&gt;My first reaction was “oh my god, it will take a loooooooong time”.&lt;/p&gt;

&lt;p&gt;My second reaction was “Ok let’s go but how to do that without breaking the applications”&lt;/p&gt;

&lt;p&gt;My third reaction was “How to do that without disturbing too much all the team”.&lt;/p&gt;

&lt;p&gt;After some researches, I found an article about the Mikado method and it was the perfect solution for my situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mikado method
&lt;/h3&gt;

&lt;p&gt;Proposed by &lt;a href="https://twitter.com/ellnestam"&gt;Ola Ellnestam&lt;/a&gt; and &lt;a href="https://twitter.com/danielbrolund"&gt;Daniel Brolund&lt;/a&gt; in 2012, this method allows to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visualize the goal of our modification and the steps to reach it&lt;/li&gt;
&lt;li&gt;lead our modification with little steps which always produce little green commit that can be merged and delivered.&lt;/li&gt;
&lt;li&gt;keep a clean commit’s history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a generic representation of a Mikado diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YGpMWnYv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/421/1%2Ajn8h0tYnOiHZ4IikzXMzqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YGpMWnYv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/421/1%2Ajn8h0tYnOiHZ4IikzXMzqg.png" alt="" width="421" height="301"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Mikado diagram&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation for my use case
&lt;/h3&gt;

&lt;p&gt;So it’s time to start the migration but from where to start? Well a good first step would be to initialize the target library if it does’nt exist. No impact, quick review from the team that’s perfect.&lt;/p&gt;

&lt;p&gt;Now, you have to choose one component to migrate into this library.&lt;/p&gt;

&lt;p&gt;Our Mikado diagram looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b0bkitDY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/151/1%2A2jlXw0Shq0f2whTyrLEWHg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b0bkitDY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/151/1%2A2jlXw0Shq0f2whTyrLEWHg.png" alt="" width="151" height="61"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You start to move it inside the library. But wait this component depends on a model:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NZTeul64--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/151/1%2AvhEBMxjDUbBoYXrq_Y4-xA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NZTeul64--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/151/1%2AvhEBMxjDUbBoYXrq_Y4-xA.png" alt="" width="151" height="181"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s revert all of our modifications. Why? Well the method recommends to revert the modification to still be in a stable compilation context. Remember one objective of the method is to only produce green commit (all tests passed, no compilation errors…).&lt;/p&gt;

&lt;p&gt;Now move the model in the appropriate library. Once it’s done, your code is compiling, all tests and lint passed. You can opened a merge request and merge this modification. It will be fast to review it, the impact on the team development will be probably minimal. That’s perfect.&lt;/p&gt;

&lt;p&gt;Let’s go back to the migration of our &lt;em&gt;ContactFormComponent.&lt;/em&gt; You move the code and… This component depends on 3 other components:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kWm2jJKG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AKatSl7yf9fW4CzyaHhHx4A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kWm2jJKG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AKatSl7yf9fW4CzyaHhHx4A.png" alt="" width="571" height="191"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s revert all of our modifications (again). Let’s focus on the first one &lt;em&gt;MobilePhoneField.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Move it. But wait this one depends on a validator which is also a dependency of &lt;em&gt;HomePhoneField.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4-bcoAkK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AvpmuQELO03kwvTGDNW3gfQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4-bcoAkK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AvpmuQELO03kwvTGDNW3gfQ.png" alt="" width="571" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 4&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s revert all of our modifications (again and again). Move the validator and open a merge request then merge it. Now ou diagram is like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DfTD5YVv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2A7qox6G0cmhJZdk4olya7fg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DfTD5YVv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2A7qox6G0cmhJZdk4olya7fg.png" alt="" width="571" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 4 bis&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Move the &lt;em&gt;MobilePhoneField&lt;/em&gt; and only it even if the &lt;em&gt;HomePhoneField&lt;/em&gt; has no other dependency. Keep your commit small. Open a merge request then merge it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FP0F_3ah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2ADOXaBL4EQYTL4HVX3KdcCg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FP0F_3ah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2ADOXaBL4EQYTL4HVX3KdcCg.png" alt="" width="571" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 5&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Keep this logic until the end, one step, one commit, one merge request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HUzh8-Qo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AkTZETzjd3Gu4v0y4XqoAFQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HUzh8-Qo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AkTZETzjd3Gu4v0y4XqoAFQ.png" alt="" width="571" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 6&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0iJXyWXi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AGQevuwHXqCLtjeVgorf4Xw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0iJXyWXi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AGQevuwHXqCLtjeVgorf4Xw.png" alt="" width="571" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 7&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AxIRoQIa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AdowXkc_Gm6uzcnZRhvTJqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AxIRoQIa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2AdowXkc_Gm6uzcnZRhvTJqw.png" alt="" width="571" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 8&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RBjF5QGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2A4Q7Vf8cSZfCOUZt-2-txng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RBjF5QGa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/571/1%2A4Q7Vf8cSZfCOUZt-2-txng.png" alt="" width="571" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step 9&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Et voilà, you have done with this &lt;em&gt;ContactFormComponent&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This method can be a bit frustrating because of the lot of revert we can do. But keep in mind a good developer is lazy but not too much. The quality must never be neglected. Thanks to this method, your client will thank you to break nothing, your teamates will also thank you to not be a bulldozer which push large commit that can break their developments. More a commit is large, more it takes time to review, more you can get conflicts and more your risk to keep an instable merge request.&lt;/p&gt;

&lt;p&gt;This method is really perfect for handling code legacy, refactoring and migration. It’s unappropriate tobug fix.&lt;/p&gt;

&lt;p&gt;thanks for reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/nx-pnpm-netlify-2232-temp-slug-5092669"&gt;Nx + Pnpm + Netlify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/how-to-handle-required-inputs-in-angular-584o"&gt;How to Handle Required Inputs in Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-implement-controlvalueaccessor-just-once-3e97eb16a0f7"&gt;Angular ControlValueAccessor once for all&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-table-of-contents-d24232940dd4"&gt;Angular for everyone: All about it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>refactoring</category>
      <category>development</category>
      <category>mikado</category>
    </item>
    <item>
      <title>Nx + Pnpm + Netlify</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sat, 30 Jul 2022 17:02:35 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/nx-pnpm-netlify-1n62</link>
      <guid>https://dev.to/gaetanrdn/nx-pnpm-netlify-1n62</guid>
      <description>&lt;p&gt;How to deploy an application from a mono repo&lt;/p&gt;

&lt;h3&gt;
  
  
  Context
&lt;/h3&gt;

&lt;p&gt;Hey, you know when you want to develop an app or a website, there is a time to deploy it.&lt;/p&gt;

&lt;p&gt;For several months, I only work with Nx workspace. No need, to explain here all the benefits Nx can bring you but I invit you to visit the &lt;a href="https://nx.dev/"&gt;web site&lt;/a&gt;. In short, it allows you to handle several apps and libs inside the same workspace. You can develop Angular, React and Node projects still in the same workspace.&lt;/p&gt;

&lt;p&gt;Last week, I decided to use &lt;a href="https://pnpm.io/"&gt;pnpm&lt;/a&gt; instead of &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;. I find it so faster. There is no word to define the pleasure to make an install with it.&lt;/p&gt;

&lt;p&gt;Today, I want to deploy one of my application thanks to &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;. I heard good things about it.&lt;/p&gt;

&lt;p&gt;But, netlify does not work natively with pnpm and there some tricks to know for mono repos. I will try to help you to do this. To accomplish this, I suppose you already have a mono repo with an app to deploy and you have create an account on &lt;a href="https://www.netlify.com/"&gt;netlify&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The steps
&lt;/h3&gt;

&lt;p&gt;I guess, you have an architecture as following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- apps
    - my-first-app
    - my-second-app
    - ...
- libs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I suppose you want to deploy &lt;em&gt;my-first-app.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First, you need to add a script into your package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// package.json
"scripts": {
    "build:my-first-app": "nx build my-first-app --outputPath=apps/my-first-app/dist",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will corresponds to the standard dist folder for netlify. This needs to be merged on your main branch (or the branch you decide to deploy).&lt;/p&gt;

&lt;p&gt;So go to netlify &lt;a href="https://app.netlify.com/"&gt;web site&lt;/a&gt; and go to the deploy tab link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4yh1Tf9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AE0w8Tv__I7EeacZmRokoPw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4yh1Tf9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AE0w8Tv__I7EeacZmRokoPw.png" alt="" width="880" height="34"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to deploy settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gqY97dUl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aah8MZrw4l5wycSvCeeoGyA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gqY97dUl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aah8MZrw4l5wycSvCeeoGyA.png" alt="" width="880" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to Build settings and click on Edit settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xB9gYACT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AO1c8iuyQaikUnj7-IwpArA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xB9gYACT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AO1c8iuyQaikUnj7-IwpArA.png" alt="" width="880" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill with the information like below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AEhvXaBG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ATLajE7sOYRqvPAqtm9hEDw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AEhvXaBG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ATLajE7sOYRqvPAqtm9hEDw.png" alt="" width="880" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Put this in the Build command field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm run build:my-first-app || ( npm install pnpm &amp;amp;&amp;amp; pnpm run build:my-first-app )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click on save. Et voilà (I’m french I can say that too). Trigger a new deploy and it will be ok.&lt;/p&gt;

&lt;p&gt;I hope this could help someone.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/test-your-storybook-5amh"&gt;Test your Storybook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/jest-and-angular-install-fg3"&gt;Install Jest for Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/typescript-function-overloads-1eeo"&gt;TypeScript Function Overloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-table-of-contents-d24232940dd4"&gt;Angular for everyone: All about it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>pnpm</category>
      <category>netlify</category>
      <category>nx</category>
    </item>
    <item>
      <title>Test your Storybook</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sat, 12 Feb 2022 13:52:26 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/test-your-storybook-5amh</link>
      <guid>https://dev.to/gaetanrdn/test-your-storybook-5amh</guid>
      <description>&lt;p&gt;Storybook for Angular&lt;/p&gt;

&lt;h3&gt;
  
  
  Context
&lt;/h3&gt;

&lt;p&gt;Nx 13 — Angular 13 — Storybook 6.5.0-alpha.36&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;More your application grows more you risk to introduce bugs. In a recent project, we have to kind of tests: Unit tests (so many) and some integration tests. We work incrementally and add features during sprints and rework older ones with more functional rules. Even if we test all devs with unit test, there is a moment that you realize you couldn’t test everything with unit testing.&lt;/p&gt;

&lt;p&gt;We have develop a Storybook which contains all our components / directives. Sometimes, we check some of it during a development and Ho surprise, it’s broken :-(. Yet All our unit tests are okay…&lt;/p&gt;

&lt;h3&gt;
  
  
  How?
&lt;/h3&gt;

&lt;p&gt;I was looking for a solution to introduce easily some functional tests without testing the interactions with the backend. Our goal was only to test functionally our HMI.&lt;/p&gt;

&lt;p&gt;I already heard talking about &lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt; and after doing some research I found a perfect solution for us. &lt;a href="https://nx.dev/"&gt;Nx&lt;/a&gt; offer the possibility to include an e2e application with Cypress configuration for testing our Storybook (Miracle).&lt;/p&gt;

&lt;p&gt;I install all the requirements and start to write some functional tests. What a surprise, some tests failed whereas all our unit tests and integration tests works… As a lead dev, it was first a deception, because it means I didn’t guarantee the quality of our application. But, on second thought, what a joy to see I can still learn and discover new things! I’m sure many of us have already be in this situation. So let’s quit chatting, this is how to do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;I suppose you already have a Nx — Angular project with Storybook. Here we just talk about Cypress installation for Storybook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nx generate [@nrwl/storybook](http://twitter.com/nrwl/storybook):cypress-project --name={your_app_name} --linter=eslint --no-interactive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My app is called “storybook” and that’s the result of the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nx generate [@nrwl/storybook](http://twitter.com/nrwl/storybook):cypress-project --name=storybook --linter=eslint --no-interactive
UPDATE package.json
CREATE apps/storybook-e2e/cypress.json             
CREATE apps/storybook-e2e/src/fixtures/example.json
CREATE apps/storybook-e2e/src/support/commands.ts  
CREATE apps/storybook-e2e/src/support/index.ts     
CREATE apps/storybook-e2e/tsconfig.json            
CREATE apps/storybook-e2e/project.json             
UPDATE workspace.json                              
CREATE apps/storybook-e2e/.eslintrc.json           
UPDATE nx.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the package.json , we can see two more dev dependencies: @nrwl/cypress and cypress , logic.&lt;/p&gt;

&lt;p&gt;In the storybook-e2e/tsconfig.json , we have this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "sourceMap": _false_,
    "outDir": "../../dist/out-tsc",
    "allowJs": _true_,
    "types": ["cypress", "node"]
  },
  "include": ["src/ **/*.ts", "src/** /*.js"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing special, except the type “cypress”.&lt;/p&gt;

&lt;p&gt;Now let’s have a look into the storybook-e2e/project.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "root": "apps/storybook-e2e",
  "sourceRoot": "apps/storybook-e2e/src",
  "projectType": "application",
  "targets": {
    "e2e": {
      "executor": "@nrwl/cypress:cypress",
      "options": {
        "cypressConfig": "apps/storybook-e2e/cypress.json",
        "devServerTarget": "storybook:storybook"
      },
      "configurations": {
        "ci": {
          "devServerTarget": "storybook:storybook:ci"
        }
      }
    },
    "lint": {
      "executor": "@nrwl/linter:eslint",
      "outputs": ["{options.outputFile}"],
      "options": {
        "lintFilePatterns": ["apps/storybook-e2e/**/*.{js,ts}"]
      }
    }
  },
  "tags": [],
  "implicitDependencies": ["storybook"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the target server is defined with our storybook app "devServerTarget":"storybook:storybook:ci" which is launch in ci mode. So there is an implicit dependency with our Storybook app "implicitDependencies": ["storybook"] .&lt;/p&gt;

&lt;p&gt;You already can to try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nx e2e {your_app_name}-e2e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will failed with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can't run because no spec files were found.

We searched for any files inside of this folder:

...\apps\storybook-e2e\src\integration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, we don’t have write tests yet. If you don’t know how to write cypress tests, you can read the documentation &lt;a href="https://www.cypress.io/"&gt;here&lt;/a&gt; which is perfect.&lt;/p&gt;

&lt;p&gt;This commands is perfect for the testing in terminal. But some of us prefer to see it in a real window. To open cypress and execute the tests on demand, I suggest you to create another cypress.json like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// cypress.local.json
{
  "fileServerFolder": ".",
  "fixturesFolder": "apps/storybook-e2e/src/fixtures",
  "integrationFolder": "apps/storybook-e2e/src/integration",
  "modifyObstructiveCode": _false_,
  "supportFile": "apps/storybook-e2e/src/support/index.ts",
  "pluginsFile": _false_,
  "video": _true_,
  "videosFolder": "../../dist/cypress/apps/storybook-e2e/videos",
  "screenshotsFolder": "../../dist/cypress/apps/storybook-e2e/screenshots",
  "chromeWebSecurity": _false_,
  "baseUrl": "http://localhost:4400" // set the url of your local Storybook
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this command into your package.json :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"test:storybook:local": "cypress open --browser ~\\chrome-win\\chrome.exe -C apps/storybook-e2e/cypress.local.json"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I use chromium as browser and I specify my local cypress config.&lt;/p&gt;

&lt;p&gt;Now you can launch your local storybook and your local cypress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nx run storybook:storybook
npm run test:storybook:local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will fail until you will have write some tests.&lt;/p&gt;

&lt;p&gt;To help writing test for Storybook, I suggest you to install the following &lt;a href="https://github.com/NicholasBoll/cypress-storybook"&gt;library&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;npm i -D cypress-storybook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then import the helper commands into the support index file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// apps/storybook-e2e/src/support/index.ts
import 'cypress-storybook/cypress'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then import that in the storybook preview file, it help storybook to understand the cypress commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// apps/storybook/.storybook/preview.js
import 'cypress-storybook/angular'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At last, in your test file what you have to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('MyComponent', () =&amp;gt; {
    beforeEach(() =&amp;gt; {
      cy.visitStorybook();
      cy.loadStory('title-of-my-story', 'MyStoryName');
    });

    it('my test', () =&amp;gt; {
    ...
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the documentation, it’s recommended to wrap the cy.visitStorybook(); in a before and not a beforeEach but when I tried, it does not reset the Story in form cases between each test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Now you will be more armed to defeat bugs! No excuse, particularly if you use Nx which helps a lot for the configuration. I suggest you to write some &lt;a href="https://docs.cypress.io/api/cypress-api/custom-commands#Parent-Commands"&gt;commands&lt;/a&gt; to simplify writing tests like a fillForm command which get a fixture and a form tag for example. It will make your test more readable.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn more
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/story-for-an-angular-directive-v2-d0p-temp-slug-1683146"&gt;Story for an Angular Directive V2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/story-for-a-component-with-content-projection-1ifg"&gt;Story for a Component With Content Projection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/jest-and-angular-install-fg3"&gt;Install Jest for Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-table-of-contents-d24232940dd4"&gt;Angular for everyone: All about it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>storybook</category>
      <category>angular</category>
      <category>nx</category>
      <category>cypress</category>
    </item>
    <item>
      <title>Story for an Angular Directive V2</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sat, 12 Feb 2022 12:33:20 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/story-for-an-angular-directive-v2-3jbk</link>
      <guid>https://dev.to/gaetanrdn/story-for-an-angular-directive-v2-3jbk</guid>
      <description>&lt;h4&gt;
  
  
  Storybook and Angular
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ynzCMPtW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/564/1%2AUVhoCifbPsKuiwgKnTMGTA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ynzCMPtW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/564/1%2AUVhoCifbPsKuiwgKnTMGTA.png" alt="" width="564" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I have a bit more experience about making stories for Angular. I would like to share with you how I write a story for an Angular directive. I have already written an article when I started with storybook. I will make here some adjustments.&lt;/p&gt;

&lt;p&gt;Context: Angular 13, StoryBook 6.5.0-alpha.36&lt;/p&gt;

&lt;h3&gt;
  
  
  First draft
&lt;/h3&gt;

&lt;p&gt;I presume you already have installed storybook, if you don’t just go &lt;a href="https://storybook.js.org/docs/angular/get-started/install"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s use this button directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// button.directive.ts
@Directive({
  selector: 'button[groButton]',  
host: {
    _class_: 'gro-button',
    '[class.gro-small]': 'size === "small"',
    '[class.gro-medium]': 'size === "medium"',
    '[class.gro-large]': 'size === "large"',
    '[class.gro-outlined]': 'outlined',
    '[class.gro-primary]': 'color === "primary"',
    '[class.gro-accent]': 'color === "accent"',
    '[class.gro-warn]': 'color === "warn"',
  },
})
_export class_ ButtonDirective {
  @Input()
  _public_ size: 'small' | 'medium' | 'large' = 'medium';

  @Input()
  @CoerceBoolean()
  _public_ outlined?: _boolean_;

  @Input()
  _public_ color: 'primary' | 'accent' | 'warn' = 'primary';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know there is a debate about using a directive or a component for button element. But that’s not the point here :-)&lt;/p&gt;

&lt;p&gt;Now, this is the minimal story example for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// button.stories.ts
_import_ { _Meta_, _Story_ } _from_ '@storybook/angular';
_import_ { ButtonDirective } _from_ './button.directive';

_export default_ {
  title: 'atoms/button',
  component: ButtonDirective,
} _as Meta_&amp;lt;ButtonDirective&amp;gt;;

_const_ template: _Story_&amp;lt;ButtonDirective&amp;gt; = (args: ButtonDirective) =&amp;gt; ({
  props: {
    size: args.size,
    outlined: args.outlined,
    color: args.color,
  },
  template: `&amp;lt;button groButton [size]="size" [outlined]="outlined" [color]="color"&amp;gt;Click&amp;lt;/button&amp;gt;`,
});

_export const_ Default = template.bind({});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this stories you could test all cases for the button directive.&lt;/p&gt;

&lt;p&gt;I suggest you to have a look on the &lt;em&gt;“props”&lt;/em&gt; properties for the template. Personally, I use the &lt;a href="https://storybook.js.org/addons/@storybook/addon-docs"&gt;addon-docs&lt;/a&gt; which works with &lt;a href="https://compodoc.app/"&gt;compodoc&lt;/a&gt;. It allows to extract the documentation in the “Docs” tab. When you use it, if you set the &lt;em&gt;“props”&lt;/em&gt; properties like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;props: {
  ...args
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get some troubles. In fact, all properties of your directive/component will be overrode, not only the &lt;em&gt;@Input&lt;/em&gt; properties. This can lead to some unexpected behaviors. So, you should prefer to only specify the properties you want to set.&lt;/p&gt;

&lt;p&gt;That’s what you get with this story:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FUpC8DKr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/827/1%2AYf2FPZYHTO-LCwAEVUboQw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FUpC8DKr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/827/1%2AYf2FPZYHTO-LCwAEVUboQw.png" alt="" width="827" height="840"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Story
&lt;/h3&gt;

&lt;p&gt;Now, let’s improve this story with some special addons offered by storybook.&lt;/p&gt;

&lt;h4&gt;
  
  
  addon-controls
&lt;/h4&gt;

&lt;p&gt;Thanks to this addon, we can play with our components/directives from a graphical UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .storybook/main.js
module.exports = {
  stories: [],
  addons: [
    '@storybook/addon-docs',
    '@storybook/addon-essentials', // check this line
  ],
  core: {
    builder: 'webpack5',
  },
  webpackFinal: (config) =&amp;gt; {
    _return_ config;
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have a look on the &lt;em&gt;argTypes&lt;/em&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// button.stories.ts
_export default_ {
  title: 'atoms/button',
  component: ButtonDirective,  
argTypes: {
    color: { // directive input name
      control: { type: 'inline-radio' },
    },
    size: { // directive input name
      control: { type: 'inline-radio' },
    },
  },
} _as Meta_&amp;lt;ButtonDirective&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to this, that’s what we get in our Storybook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gOL5GG0d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/740/1%2ABQ_9rqAIyQNg8a3wlmyzWA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gOL5GG0d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/740/1%2ABQ_9rqAIyQNg8a3wlmyzWA.png" alt="" width="740" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In all recent version of Storybook, this addon is a part of &lt;a href="https://storybook.js.org/docs/react/essentials/introduction"&gt;essentials&lt;/a&gt; and is installed by default.&lt;/p&gt;

&lt;p&gt;If you want to get more information about it go &lt;a href="https://storybook.js.org/addons/@storybook/addon-controls/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  addon-jest
&lt;/h4&gt;

&lt;p&gt;I use &lt;a href="https://jestjs.io/fr/"&gt;Jest&lt;/a&gt; to test my code. It’s sometime useful knowing what is tested without going in the code. For this, you have to install the &lt;a href="https://storybook.js.org/addons/@storybook/addon-jest/"&gt;addon-jest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(yarn|npm) (add|install) -D @storybook/addon-jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the addon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .storybook/main.js
module.exports = {
  stories: [],
  addons: [
    '@storybook/addon-docs',
    '@storybook/addon-essentials',
    '@storybook/addon-jest', // check here
  ],
  core: {
    builder: 'webpack5',
  },
  webpackFinal: (config) =&amp;gt; {
    _return_ config;
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In package.json file add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// don't forget to add .jest-test-results.json to .gitignore
"scripts": {
  "test:generate-output": "jest --json --outputFile=.jest-test-results.json"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add test at the global level of your Storybook:&lt;br&gt;
&lt;/p&gt;

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

import { withTests } from '@storybook/addon-jest';

import results from '../.jest-test-results.json';

export const decorators = [
  withTests({
    results,
  }),
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_export default_ {
  title: 'atoms/button',
  component: ButtonDirective,
  parameters: {
    jest: ['button.directive.spec'], // check here
  },
  argTypes: {
    color: {
      control: { type: 'inline-radio' },
    },
    size: {
      control: { type: 'inline-radio' },
    },
  },
} _as Meta_&amp;lt;ButtonDirective&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will now can see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PBW-7mKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AU80TuVRM7AxaRknDmAQyyQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PBW-7mKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AU80TuVRM7AxaRknDmAQyyQ.png" alt="" width="880" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s the minimal setup I can suggest you. There are a lot of other addons that you can check &lt;a href="https://storybook.js.org/addons/"&gt;here&lt;/a&gt;. All of them cannot be used for Angular but it’s always noticed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Now you can have a nice Storybook, and more you can improve it with a lot of other addons. In a next chapter, I will show you how to test your Storybook. Why? Because Storybook is really powerful but when you add code into your applications/libraries you couldn’t know if you have broken something…&lt;/p&gt;

&lt;p&gt;To be following…&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn More
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/jest-and-angular-install-fg3"&gt;Install Jest for Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/how-to-undo-a-git-commit-36lp"&gt;How to Undo a GIT Commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/typescript-function-overloads-1eeo"&gt;TypeScript Function Overloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-table-of-contents-d24232940dd4"&gt;Angular for everyone: All about it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>addons</category>
      <category>storybook</category>
      <category>angular</category>
      <category>directives</category>
    </item>
    <item>
      <title>Typescript decorator to handle Unsupported operations</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sun, 19 Dec 2021 12:39:38 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/typescript-decorator-to-handle-unsupported-operations-1i0d</link>
      <guid>https://dev.to/gaetanrdn/typescript-decorator-to-handle-unsupported-operations-1i0d</guid>
      <description>&lt;h4&gt;
  
  
  Simplify your code
&lt;/h4&gt;

&lt;h3&gt;
  
  
  Context
&lt;/h3&gt;

&lt;p&gt;Typescript: 4.3.5&lt;/p&gt;

&lt;p&gt;In my Angular project, I have a generic class for CRUD services. This class defines all available api calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_type IdentifierType_ = _string_ | _number_;

@Injectable()
_export abstract class_ CrudService {
  _protected_ _url!: _string_;

  _protected constructor_(_protected readonly_ _httpClient: HttpClient) {}

  _public_ get&amp;lt;ReturnType&amp;gt;(
    identifier: _IdentifierType_  
): Observable&amp;lt;_Nullable_&amp;lt;ReturnType&amp;gt;&amp;gt; {
    ...
  }

  _public delete_(identifier: _IdentifierType_): Observable&amp;lt;_boolean_&amp;gt; {
    _..._
  }

  _public_ post&amp;lt;ReturnType, BodyType = _Partial_&amp;lt;ReturnType&amp;gt;&amp;gt;(
    body: BodyType
  ): Observable&amp;lt;_Nullable_&amp;lt;ReturnType&amp;gt;&amp;gt; {
    _..._
  }

  _public_ getAll&amp;lt;ReturnType&amp;gt;(
    filters?: _Record_&amp;lt;_string_, _string_ | _number_&amp;gt;
  ): Observable&amp;lt;_Nullable_&amp;lt;ReturnType[]&amp;gt;&amp;gt; {
    ...
  }

  _public_ put&amp;lt;BodyType&amp;gt;(body: BodyType): Observable&amp;lt;_boolean_&amp;gt; {
    ...
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is usefull when you have the chance to work with a backend api which has the same behavior for all entities. But it’s not always the case…&lt;/p&gt;
&lt;h3&gt;
  
  
  Problems
&lt;/h3&gt;

&lt;p&gt;Here’s some cases which corresponds to my project reality, probably you will find here some similarities with your own projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An entity cannot be created / deleted, data comes from a reference base or from a configuration base (list of localities, list of products which are not handle by your application…).&lt;/li&gt;
&lt;li&gt;An entity cannot be updated / partially updated, you work in a system which needs to historize all&lt;/li&gt;
&lt;li&gt;Or simplier, just because your backend api does not offer this possibilities yet. You work on a project from scratch and with iterations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So how to avoid wasting time to debug or to understand in which case you are when something wrong happened ?&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;The first approach will probably be to throw an error in the sub classes’ methods like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Injectable()
_export class Person_CrudService extends CrudService {
  _protected_ _url = '/persons';

  _public override delete_(identifier: _number_): Observable&amp;lt;_boolean_&amp;gt; {
    _throw new Error('Unsupported Operation');_
  }

  _public override_ post&amp;lt;Person&amp;gt;(body: Person): Observable&amp;lt;_Nullable_&amp;lt;Person&amp;gt;&amp;gt; {
    _throw new Error('Unsupported Operation');_
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here we forbidden the access to delete and post operations. We need to rewrite the method’s definitions and to throw an error inside both. Now let’s imagine to do that for each entity… As I’m a bit Lazy and I don’t find it elegant, I chose another way: A typescript class decorator.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;It simply takes the list of operations which must throw the expected error.&lt;/p&gt;

&lt;p&gt;Type corresponds to the class and allows to get autocompletion when you type the operation name(s).&lt;/p&gt;

&lt;p&gt;Here’s the corresponding example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Injectable()
_@UnsupportedOperations&amp;lt;Person_CrudService&amp;gt;('delete', 'post')  
_export class Person_ CrudService extends CrudService {
  _protected_ _url = '/persons';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, when you write this, you save time and some lines of code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;When something to do is boring, not elegant or take to much time then you should probably find a better way to do it. Maybe this solution will not please everyone but I and my team think that’s effective.&lt;/p&gt;

&lt;p&gt;Thanks for reading, feel free to comment. See you&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn More
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/how-to-handle-required-inputs-in-angular-584o"&gt;How to Handle Required Inputs in Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/typescript-function-overloads-1eeo"&gt;TypeScript Function Overloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-table-of-contents-d24232940dd4"&gt;Angular for everyone: All about it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-un-decorator-pour-forcer-le-type-bool%C3%A9en-%C3%A0-une-propri%C3%A9t%C3%A9-32931c79163"&gt;A decorator to coerce Boolean properties&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>simplify</category>
      <category>decorators</category>
      <category>errors</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Flutter: Rex d’une première Expérience</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sun, 05 Dec 2021 17:21:34 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/flutter-rex-dune-premiere-experience-1b65</link>
      <guid>https://dev.to/gaetanrdn/flutter-rex-dune-premiere-experience-1b65</guid>
      <description>&lt;h4&gt;
  
  
  Pourquoi et comment?
&lt;/h4&gt;

&lt;h3&gt;
  
  
  Contexte
&lt;/h3&gt;

&lt;p&gt;Je suis développeur web depuis quelques années maintenant et avec une grosse appétence front-end. Comme beaucoup, je suis très curieux et j’ai soif d’apprendre. L’ennui et la monotonie sont deux choses qui me terrifies.&lt;/p&gt;

&lt;p&gt;Il y’a quelques temps, j’ai entendu parler de Flutter. Un nouveau framework développé par Google. Et là ma première réaction fut:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pourquoi Google sort un autre framework, alors qu’on dispose déjà d’Angular?!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bref, je me dis qu’il faut que je creuse le sujet. Je vais donc sur le &lt;a href="https://flutter.dev/"&gt;site officiel&lt;/a&gt; et je commence à lire, à me documenter et à décortiquer toute la doc.&lt;/p&gt;

&lt;p&gt;Je comprends donc que l’idée n’est pas de remplacer Angular mais de proposer un framework capable de répondre à une problématique :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Comment développer une application accessible quelque soit la plateforme (web, iOS, Android)?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ni une ni deux ça me donne envie d’essayer tellement ça à l’air simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Projet
&lt;/h3&gt;

&lt;p&gt;Durant le premier confinement, j’ai monté une équipe de collègues qui comme moi se sont retrouvés en inter-contrat. On avait développé une application web permettant de visualiser sur une map les clients de notre employeur ainsi que les collègues en mission.&lt;/p&gt;

&lt;p&gt;Voici donc mon sujet de projet : essayer de faire une application mobile répondant à ce besoin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rex
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Installation
&lt;/h4&gt;

&lt;p&gt;Franchement, il suffit de suivre la &lt;a href="https://docs.flutter.dev/get-started/install/windows"&gt;doc&lt;/a&gt; et c’est très simple et très clair. Personnellement, j’utilise IntelliJ sous Windows et ça a été très rapide.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On télécharge le SDK ou on clone le repo git de flutter&lt;/li&gt;
&lt;li&gt;Petite mise à jour du path Windows pour faire connaître les commandes spécifiques&lt;/li&gt;
&lt;li&gt;Hum on n’oublie pas de redémarrer 😏&lt;/li&gt;
&lt;li&gt;Android studio est facultatif si vous n’avez pas dans l’objectif de dev pour Android&lt;/li&gt;
&lt;li&gt;IDE: personnellement, j’utilise IntelliJ et il suffit d’installer Deux plugins (Dart et Flutter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voilà on est prêt à commencer 😁&lt;/p&gt;

&lt;h4&gt;
  
  
  Premier devs
&lt;/h4&gt;

&lt;p&gt;Je crée mon nouveau projet via IntelliJ (Flutter Application). Je découvre un code démo assez basique. J’en profite pour comprendre un peu le fonctionnement, les imbrications de widgets et surtout la syntaxe.&lt;/p&gt;

&lt;p&gt;Je commence par essayer de composer ma structure de page. Il m’en faut 4 en tout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Une concernant les collaborateurs&lt;/li&gt;
&lt;li&gt;Une concernant les clients&lt;/li&gt;
&lt;li&gt;Une permettant de créer des missions (lien entre un collègue et un client)&lt;/li&gt;
&lt;li&gt;Et enfin une pour la map&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je me rends compte que pour ne pas partir trop de travers il faut que je me documente un peu.&lt;/p&gt;

&lt;p&gt;Je choisis donc de lire tous les &lt;a href="https://docs.flutter.dev/cookbook"&gt;cookbooks&lt;/a&gt; de la documentation Flutter. Ça a été un régal, tout à l’air simple et facile à mettre en place. J’ai rarement vu une doc aussi bien faite et complète.&lt;/p&gt;

&lt;p&gt;Cependant, je me rend vite compte que je m’emballe un peu. Et oui comment je vais stocker mes données? Je n’ai pas forcément envie de mettre quelque chose de trop complexe en place. Très vite je trouve plein de solutions. Une retiens mon attention: &lt;a href="https://pub.dev/packages/localstore/score"&gt;localstore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;C’est compatible avec toutes les plateformes c’est simple à utiliser donc parfait je prends. Je peux donc créer mon petit modèle de données.&lt;/p&gt;

&lt;p&gt;Mon petit côté pointilleux reviens à la charge. Hem et les tests unitaires? Ben oui en Angular tu fais du TDD donc là on fait quoi? Certes cette fois je n’avais pas envie de faire du TDD mais j’ai quand même mis en place des TUs. Encore une fois la &lt;a href="https://docs.flutter.dev/testing"&gt;doc&lt;/a&gt; est vraiment très bien faite.&lt;/p&gt;

&lt;p&gt;Enfin un dernier point à aborder, qu’est ce qu’on utilise pour la map? Là y a beaucoup de choix, des solutions plus ou moins payantes. Je me suis tourné vers &lt;a href="https://pub.dev/packages/flutter_map"&gt;flutter_map&lt;/a&gt;. Une solution compatible avec toutes les plateformes et gratuite.&lt;/p&gt;

&lt;p&gt;Après quelques semaines (oui je fais ça sur mon temps perso et je suis papa donc pas beaucoup de temps pour faire ça) je finis une V1 de mon appli et là quelle fierté 😁. C’est fonctionnel et sur mon iPhone ça s’utilise très bien.&lt;/p&gt;

&lt;p&gt;Vous trouverez le repo git &lt;a href="https://github.com/GaetanRdn/where_my_coworkers"&gt;ici&lt;/a&gt; et vous pouvez jouer avec l’application &lt;a href="https://gaetanrdn.github.io/where_my_coworkers/"&gt;ici&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour s’en servir en mode web app mobile, pour iOS il suffit d’ouvrir Safari, d’appuyer sur l’icône pour afficher les options de la page puis de cliquer sur « sur l’écran d’accueil ».&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Ce fut une excellente expérience, je me vraiment amusé. Flutter est vraiment facile à prendre en main et simplifie beaucoup de comportements communs. Il fournit une sacré liste de widgets qui peut être enrichie avec d’autres librairies.&lt;/p&gt;

&lt;p&gt;Quelques bémols:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;J’ai été déconcerté par l’absence de «protected» on est soit en privé doit en public.&lt;/li&gt;
&lt;li&gt;La syntaxe un peu trop flex à mon goût, je suis plutôt pro typescript très strict.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je ne suis pas dupe, mon code n’est sûrement pas parfait et peut être amélioré. Mais mon but était de découvrir et ça le fait. C’est également pour ça que j’ai choisi GitHub pages pour déployer l’appli, simple, rapide et répond à mon besoin.&lt;/p&gt;

&lt;p&gt;J’ai hâte en tout cas d’avoir une nouvelle idée de projet mobile que cette fois j’essayerai de pousser jusqu’au Store iOS. Affaire à suivre…&lt;/p&gt;

&lt;p&gt;Un grand merci pour le temps passé à me lire. C’est la première fois que je fais un article en français. Je veux bien un retour là dessus en plus du reste 🙂&lt;/p&gt;

</description>
      <category>programming</category>
      <category>flutter</category>
      <category>rex</category>
      <category>maps</category>
    </item>
    <item>
      <title>Story for a Component With Content Projection</title>
      <dc:creator>Gaëtan Redin</dc:creator>
      <pubDate>Sun, 03 Oct 2021 12:08:46 +0000</pubDate>
      <link>https://dev.to/gaetanrdn/story-for-a-component-with-content-projection-1ifg</link>
      <guid>https://dev.to/gaetanrdn/story-for-a-component-with-content-projection-1ifg</guid>
      <description>&lt;h4&gt;
  
  
  Storybook and Angular components
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt; : Angular 12, StoryBook 6.3.9&lt;/p&gt;

&lt;p&gt;Hey, I started to use Storybook and I would Like to share my experience. Here’s the use case. I have a simple component which only use content projection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: "adr-label",
  template: `&amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;`,
})
_export class_ LabelComponent {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the associated story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_export default_ {
  title: "atoms/forms/label",
  component: LabelComponent,
  decorators: [**componentWrapperDecorator** (LabelComponent)],
} _as Meta_;

_const_ BasicTemplate: _Story_&amp;lt;LabelComponent&amp;gt; = (args) =&amp;gt; ({
  moduleMetadata: { declarations: [InputDirective] },
  **template** : `&amp;lt;adr-label&amp;gt;{{ ngContent }}&amp;lt;/adr-label&amp;gt;`,
  props: { ...args },
});

_export const_ Default = BasicTemplate.bind({});
Default.args = {
  **ngContent** : "Un label",
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The specificity here is to use a &lt;a href="https://storybook.js.org/docs/react/writing-stories/decorators"&gt;&lt;em&gt;componentWrapperDecorator&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt; It allows to wrap our component and to pass it some extras like in my case a text content.&lt;/p&gt;

&lt;p&gt;You just have to define a template like in BasicTemplate to emulate the content projection and using it like an input.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;Default&lt;/em&gt; args, I add an &lt;em&gt;ngContent&lt;/em&gt; arg to set the content of my label. I choose this name “ngContent” because it appears in the controls list and I find it consistent to let know that’s a content projection in this way.&lt;/p&gt;

&lt;p&gt;You can see the result here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gaetanrdn.github.io/atomic-design/?path=/story/atoms-forms-label--default"&gt;Storybook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can access to the full code here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/GaetanRdn/atomic-design/tree/master/src/components/atoms/forms/label"&gt;atomic-design/src/components/atoms/forms/label at master · GaetanRdn/atomic-design&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn More
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/story-for-an-angular-directive-11p4"&gt;Story for an Angular Directive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/logger-decorator-47ob"&gt;Logger decorator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gaetanrdn/jest-and-angular-install-fg3"&gt;Install Jest for Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@redin.gaetan/angular-for-every-one-table-of-contents-d24232940dd4"&gt;Angular for everyone: All about it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>howto</category>
      <category>storybook</category>
      <category>contentprojection</category>
    </item>
  </channel>
</rss>
