<?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: Stephen Cooper</title>
    <description>The latest articles on DEV Community by Stephen Cooper (@scooperdev).</description>
    <link>https://dev.to/scooperdev</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%2F204984%2F3a4e5c39-7a0d-4462-b590-2cba48fc2e00.png</url>
      <title>DEV Community: Stephen Cooper</title>
      <link>https://dev.to/scooperdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scooperdev"/>
    <language>en</language>
    <item>
      <title>Test that every MutationObserver is disconnected to avoid memory leaks</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Thu, 22 Aug 2024 11:30:46 +0000</pubDate>
      <link>https://dev.to/scooperdev/test-that-every-mutationobserver-is-disconnected-to-avoid-memory-leaks-2fkp</link>
      <guid>https://dev.to/scooperdev/test-that-every-mutationobserver-is-disconnected-to-avoid-memory-leaks-2fkp</guid>
      <description>&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" rel="noopener noreferrer"&gt;MutationObserver&lt;/a&gt; is a useful mechanism to watch for changes in the DOM and respond to them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The MutationObserver interface provides the ability to watch for changes being made to the DOM tree.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is an example that watches for a theme class change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUpThemeClassObservers&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;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getThemeClass&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fireStylesChangedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;themeChanged&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;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eGridDiv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;attributes&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;attributeFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// we must disconnect otherwise "this" will not be GC'd&lt;/span&gt;
        &lt;span class="c1"&gt;// causing a memory leak&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if you forget to disconnect you could be exposing yourself to memory leaks depending on what is accessed from within the &lt;code&gt;MutationObserver&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;Wouldn't it be great to have a test that can validate that we disconnect our observers?&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic Validation for Code
&lt;/h2&gt;

&lt;p&gt;It turns out that it is possible to validate that every &lt;code&gt;MutationObserver&lt;/code&gt; that is observing the DOM is also disconnected. (You may need more than one test if you have to exercise different code paths in order to setup the MutationObservers)&lt;/p&gt;

&lt;p&gt;The idea is to Mock the global MutationObserver with sub mocks for its &lt;code&gt;observe&lt;/code&gt; and &lt;code&gt;disconnect&lt;/code&gt; methods. Before the mock is returned we record it in an array so that we can validate all instances at the end of the test run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mutation Observers Disconnected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;originalMutationObserver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;allMockedObservers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&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;mutationObserverMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MutationCallback&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mockImplementation&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="na"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="na"&gt;takeRecords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nx"&gt;allMockedObservers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mock&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;mock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Ensure we can restore the real MutationObserver after the test&lt;/span&gt;
        &lt;span class="nx"&gt;originalMutationObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MutationObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mutationObserverMock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MutationObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originalMutationObserver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;observer always disconnected after destroy&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createGrid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Perform some actions if required to exercise the code paths&lt;/span&gt;
        &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allMockedObservers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeGreaterThan&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="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mock&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;allMockedObservers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way we can validate that each MutationObserver, setup during the test, is also disconnected at the end of the test.&lt;/p&gt;




&lt;p&gt;Stephen Cooper - Senior Developer at &lt;a href="https://ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt;&lt;br&gt;
Follow me on X &lt;a href="https://x.com/SCooperDev" rel="noopener noreferrer"&gt;@ScooperDev&lt;/a&gt; &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>testing</category>
    </item>
    <item>
      <title>Supporting Circularly Referenced Mapped Types in Typescript</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Fri, 22 Sep 2023 10:03:27 +0000</pubDate>
      <link>https://dev.to/scooperdev/supporting-circularly-referenced-mapped-types-in-typescript-4825</link>
      <guid>https://dev.to/scooperdev/supporting-circularly-referenced-mapped-types-in-typescript-4825</guid>
      <description>&lt;p&gt;Recursive structures are very common across many applications but they can pose a big challenge to Typescript. While in our real data the recursion may only go a few levels, Typescript does not know this which can prose big problems for custom types. Just &lt;a href="https://www.google.com/search?q=Type+of+property+circularly+references+itself+in+mapped+type" rel="noopener noreferrer"&gt;search&lt;/a&gt; for the following error and see all the results!&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;Type&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="nx"&gt;circularly&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt; &lt;span class="nx"&gt;itself&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;mapped&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; 


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

&lt;/div&gt;

&lt;p&gt;In the remainder of this post I will share how I resolved this error for a type called &lt;code&gt;NestedFieldPaths&amp;lt;TData&amp;gt;&lt;/code&gt; that is a key part of the &lt;a href="https://ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt; library. &lt;/p&gt;

&lt;h2&gt;
  
  
  Context for Error
&lt;/h2&gt;

&lt;p&gt;In AG Grid when configuring the grid that are two main parts, columns and rowData. The most important part of the column is the &lt;code&gt;field&lt;/code&gt; property which describes which property from the row data should be displayed. The &lt;code&gt;field&lt;/code&gt; property can access deeply nested properties via a dot separated string.&lt;/p&gt;

&lt;p&gt;So if the user provides the &lt;code&gt;Person&lt;/code&gt; interface to &lt;code&gt;ColDef&amp;lt;Person&amp;gt;&lt;/code&gt; the valid field properties will be as follows: &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;age&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;address.firstLine&lt;/code&gt; and &lt;code&gt;address.city&lt;/code&gt;.&lt;/p&gt;

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

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;firstLine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;city&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We achieve this via a generic type helper called &lt;code&gt;NestedFieldPaths&amp;lt;TData&amp;gt;&lt;/code&gt; which extracts all the possible paths from the provided interface. This works great and brought a new level of type checking an DX via auto completion for the &lt;code&gt;field&lt;/code&gt; property.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursive Interface Issue
&lt;/h2&gt;

&lt;p&gt;This works great until you update the &lt;code&gt;Person&lt;/code&gt; interface to also include a &lt;code&gt;child: Person&lt;/code&gt; property making the interface recursive.&lt;/p&gt;

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

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// interface refers to itself recursively&lt;/span&gt;
    &lt;span class="nl"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Person&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;Pass this to &lt;code&gt;NestedFieldPaths&amp;lt;Person&amp;gt;&lt;/code&gt; and instead of getting all the possible paths, i.e &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;child.value&lt;/code&gt;, &lt;code&gt;child.child.value&lt;/code&gt; and so on and you will get the following error.&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;Type&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;children&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;circularly&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt; &lt;span class="nx"&gt;itself&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;mapped&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This prevents the use of recursive interfaces with the &lt;code&gt;NestedFieldPaths&lt;/code&gt;. Which makes people sad (&lt;a href="https://github.com/ag-grid/ag-grid/issues/6741#issuecomment-1590574851" rel="noopener noreferrer"&gt;Github Issue&lt;/a&gt;). So what can we do about this?&lt;/p&gt;

&lt;h3&gt;
  
  
  Potential Solutions
&lt;/h3&gt;

&lt;p&gt;There are a number of unsatisfactory approaches such as not supporting recursive interfaces, or forcing the user to do some trickery to the interface that they provide to make it less recursive. &lt;/p&gt;

&lt;p&gt;We cannot get round the fact that Typescript has to bail at some point so that it does not get stuck in an infinite loop. But is there something that we can do to make a type practically useable?&lt;/p&gt;

&lt;p&gt;Best of both worlds is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;support a fixed number of recursions &lt;/li&gt;
&lt;li&gt;beyond this give the user freedom to do whatever they like&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We definitely want to be able to support these interfaces as they correctly explain the data that users want to display with AG Grid. So how can we do it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Count Recursion Levels and exit at a given depth
&lt;/h2&gt;

&lt;p&gt;The idea is to count how deep we have recursed and at a given level, say 6, bail from the recursion and let the rest of the path match anything. This last part is key because as soon as you set an arbitrary limit someone is going to actually specify a path that goes beyond that limit and we do not want to prevent that.&lt;/p&gt;

&lt;p&gt;The first typing tool that we need is to be able to count levels of recursion. We can achieve this with the following type structure. &lt;/p&gt;

&lt;p&gt;We first define a union of numbers that are within the depth level that we support. For this type we will go 7 levels deep so have the digits up to 7.&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;// Union of numbers within our depth limit&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Digit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We then construct an array of those same digits but crucially where their index in the array is one less then their own value. So for example at index &lt;code&gt;1&lt;/code&gt; we have the value &lt;code&gt;2&lt;/code&gt; and at index &lt;code&gt;2&lt;/code&gt; we have &lt;code&gt;3&lt;/code&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;// Array where each value is one greater than its index in the array&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NextDigit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STOP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You can see how by using our current digit we can index the &lt;code&gt;NextDigit&lt;/code&gt; array to get the next value. Putting these together we can setup the &lt;code&gt;Inc&amp;lt;T&amp;gt;&lt;/code&gt; type utility which will give us the next digit. &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;// Type to get the number from the index in the array&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Inc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Digit&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;NextDigit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STOP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 


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

&lt;/div&gt;

&lt;p&gt;But how do we stop? Well you can see that the last element in the &lt;code&gt;NextDigit&lt;/code&gt; type is the string &lt;code&gt;STOP&lt;/code&gt; which clearly is not a &lt;code&gt;Digit&lt;/code&gt;. We can use this along with a check in &lt;code&gt;Inc&amp;lt;T&amp;gt;&lt;/code&gt; to ensure that we stop changing the value returned from &lt;code&gt;Inc&lt;/code&gt; and we will be able to pick a different path based on the return type of &lt;code&gt;STOP&lt;/code&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;ShouldStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Inc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STOP&lt;/span&gt;&lt;span class="dl"&gt;'&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;For our use case stopping the recursion means letting the user type any string path after the given path. The key part of our type to achieve this is as follows:&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;NestedPath&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prefix&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TDepth&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="nx"&gt;TValue&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;
        &lt;span class="c1"&gt;// Return any to allow any further string to be appended&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Prefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt; &lt;span class="nx"&gt;TDepth&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STOP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NestedFieldPaths&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TDepth&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This type checks if the current &lt;code&gt;TValue&lt;/code&gt; is an object and if it is then it checks if the current &lt;code&gt;TDepth&lt;/code&gt; = &lt;code&gt;STOP&lt;/code&gt;. If it is then we append &lt;code&gt;any&lt;/code&gt; to the string literal type. If we have not reached &lt;code&gt;STOP&lt;/code&gt; yet it will recurse on the child properties of &lt;code&gt;TValue&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Note that we are not incrementing &lt;code&gt;TDepth&lt;/code&gt; in this type because the recursion happens across two interfaces. The higher level interface of &lt;code&gt;NestedFieldPaths&lt;/code&gt; is responsible for calling &lt;code&gt;Inc&amp;lt;TDepth&amp;gt;&lt;/code&gt; when it calls &lt;code&gt;NestedPath&lt;/code&gt;. In this way each level of the input type will increment depth by one.&lt;/p&gt;


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

&lt;p&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NestedFieldPaths&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TDepth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TKey&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;StringOrNumKeys&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;br&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;TKey&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span class="s2"&amp;gt;&lt;/code&gt;&lt;/span&gt;&lt;br&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NestedPath&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TKey&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;TKey&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span class="s2"&amp;gt;&lt;/code&gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Inc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TDepth&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="nx"&gt;StringOrNumKeys&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Recursive Support for Nested Field Paths&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;By introducing this counter and stop logic we can now support recursive interfaces with the desired goals of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;support a fixed number of recursions &lt;/li&gt;
&lt;li&gt;beyond this limit give the user freedom to do whatever they like&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Autocompletion clearly shows the depth value kicking in and stopping the recursion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnycav8zrsl6kp1298zmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnycav8zrsl6kp1298zmi.png" alt="Auto complete with limited recursion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;p&gt;One potential downside of this approach is that if you have a nested object with &lt;em&gt;no&lt;/em&gt; recursion, that goes beyond our depth limit, then those deeper levels will no longer be autocompleted. This is a trade off that we have decided to accept as we hope that nesting beyond 6 levels should be less common then having recursive interfaces.&lt;/p&gt;

&lt;p&gt;The only other consideration is that you cannot increase the depth limit arbitrarily as you can still cause Typescript to bail on you if it thinks the type is getting too complex. This is why we have set a limit of 7, as in testing we found going above this caused the following error.&lt;/p&gt;


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

&lt;p&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="nx"&gt;TS2589&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="nx"&gt;instantiation&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;excessively&lt;/span&gt; &lt;span class="nx"&gt;deep&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;possibly&lt;/span&gt; &lt;span class="nx"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Playground&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;If you want to experiment this type yourself you can find it in the following &lt;a href="https://www.typescriptlang.org/play?ts=4.3.5#code/C4TwDgpgBAIglgczsKBeKBGKAfKAmHKAZkIBZCBWQgNkIHZCAOQgTgG4AoUSKAOQgAeweEhToA2hgA0+GURmkZFGdRl0ZAcgDKAFQDyABQ0BdTt2gBJAHYBjADw6AfGig6og4BCsATAM6xEZCgAfj4PEWRxHWMoAC4obX0jNigucGgtYAAnOCsEPSzeAFcAWwBpCBBfBz0AIwArZ3QAa0qAewAzVzr6qAAyKAAKX2zchEIrUtqILIBKM3SwkYhvAwBDYAALBwA1NYAbIogZAyyIDrgBdyEvPygRnLyZHT3DiH5l7wBhTbh972eMAgYC2TVSUAhrleR2unh8-jaDQgNmAHEh6JCUAABgASADep3OlwAvgA6fGuIEgzaw27+RKGDSYtZWEBxJaebwAMTgEH+6y21ReByOz2h7wgnx+fwBlOBoOJWLRGIh8SsEAAbjNOIIwG0sihzFAvm19kCOjy+d4HDANmsXCyQGKRdB0I6wTpbcB7R46VBHZiPpzLfyNpshV61s63jIMM41ZrtRxdfrDYsgysQ6swxG7Q7WdGYW6C3LqS4AAxgvHKiFRCps3JQTKPfKFUr13PexzGWI1jG4Qaeu11yoxX3wqEuzG4vE6euK9nqrVzPvo3AZ7NbG3DuejmQz3cgRWF45Qaz2T3yzaORycYniZtjArFcqVTtrbucDgcAD0ACo-zRP9XEWYA2n9BA1lyEZwT-H8OFyTwsg6NYbGgE0zXObdvXzJ1JzeXDnGrdELitYJ4gw80s2wqN8KOW8OGJb9EJmFC0LPAAlNoAHdIygYioCsNYSggeIHjGKRlTWBBRMEqYkwhGxfn+eILC43i81wSYSmmLI72-Gw2isGDDP2CjTXNFw8SgUiVI0R0tjGAB9AB1ZBNic3g2icnR0g0JjDOMlBTP8czMI6Ow1J4yNHHEGIJGVASIVs7x4nsmSNGVYlJIhH8f2NFlaR8f1-H2IzxjWfwQDaIooG4lkUETKwoDgLo1iKMCoEMkowH2CBgDgIz7jAsB-G4-VmjGRKUrSpSZVJOb-gW5TvGW+bFtWja1qWratqEkT-I4UxvxTA0WqsJC2MsSN1P4vs4FS4aWxyyERN8XxpNk8Snj7EYNiKXw0rOepkU5JlcA0M49QNFZwYSNYwDALI2i1bwNBkcFIQ2s4rFUm6eLiwgih8Il1W8fTApM01fCwMLzUi-HuNi+KoHERK+xmhJsa8TKIWy6beRUhIHsO47mIu1jUMsdTIywAT9q+0YfohT61XkvTlQ2+Ikq6laFbEpWEE4Pn9I4SngtNPA6fOKybMF1L7NZRy8lc9zPO83zIEO82uupghrYiqKNO9ONCYSiEdc59KIF5qB+YjqPdr14SY6Y0wgA" rel="noopener noreferrer"&gt;Ts Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Would love to know if you find this useful or if you spot any issues that I have missed!&lt;/p&gt;

</description>
      <category>typescript</category>
    </item>
    <item>
      <title>Generate array of all an interface's keys with Typescript</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Mon, 24 Jul 2023 21:09:34 +0000</pubDate>
      <link>https://dev.to/scooperdev/generate-array-of-all-an-interfaces-keys-with-typescript-4hbf</link>
      <guid>https://dev.to/scooperdev/generate-array-of-all-an-interfaces-keys-with-typescript-4hbf</guid>
      <description>&lt;p&gt;When working with a large and complex code base like &lt;a href="https://ag-grid.com/"&gt;AG Grid&lt;/a&gt; it is very easy to miss updating certain parts of the code base. &lt;/p&gt;

&lt;p&gt;For example, we have code generators that depend on having accurate lists of interface property names. In other words given a Typescript &lt;code&gt;interface&lt;/code&gt; how can you produce an array that contains every property and ensure it stays up to date. &lt;/p&gt;

&lt;p&gt;There are three approaches you might take to keeping this array of properties accurate:&lt;/p&gt;

&lt;p&gt;1:  Add a BIG comment reminding devs to update another part of the codebase.&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;// IF YOU UPDATE THE COLDEF INTERFACE REMEMBER TO ALSO UPDATE...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2: Delve deep into the world of ASTs to extract / generate the list based off the code file.&lt;/p&gt;

&lt;p&gt;3: Use Typescript to enforce the link between &lt;code&gt;ColDef&lt;/code&gt; properties and a manually maintained list.&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;// See below for an explanation&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ColDefObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. Add a BIG COMMENT
&lt;/h2&gt;

&lt;p&gt;The first approach is very simple and works initially. However, as the team grows, or you get forgetful it can still be easily missed. This is true no matter how big and attention grabbing you try to make your comment. So it's probably best not to go for this approach. Secondly, its litters the code base with unsightly comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Use an AST Parser and code Generator
&lt;/h2&gt;

&lt;p&gt;The second approach requires a lot more work! Yes it will ensure the property list is perfectly accurate, but now you have to maintain a code parser and manage additional build complexity. While this isn't beyond the realms of possibility, (we have a few of these in AG Grid) it is definitely not an easy task. We can safely say that this approach is totally overkill for what we are trying to solve, especially with the next option.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use Typescript to enforce the updates
&lt;/h2&gt;

&lt;p&gt;The third option really is the best of both worlds. There is no need for a big comment because your Typescript build will fail if the property list is out of sync. Secondly, we don't need to write a code generator, we have a perfectly good human developer who can fix the issue in seconds.&lt;/p&gt;

&lt;p&gt;What makes this very easy is that they will get autocompletion and a clear description of what property they need to add to the list. This approach is both simple to maintain and also is 100% reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Typescript
&lt;/h2&gt;

&lt;p&gt;So how can we use Typescript to achieve our desired result? As a reminder we want to have a complete array of all the property names defined on the &lt;code&gt;ColDef&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;It is worth clarifying we want an actual array of property names that we can use in our code as opposed to just having the list of possible properties in Type land. This is why &lt;code&gt;(keyof ColDef)[]&lt;/code&gt; alone is not sufficient.&lt;/p&gt;

&lt;p&gt;First off lets show a cut down version of the &lt;code&gt;ColDef&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;columnGroupShow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;toolPanelClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;valueGetter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ValueGetterParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see we have a mixture of different property types including primitive types as well as objects and functions.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;ColDef&lt;/code&gt; interface we can create a new type that will enable us to type an object from which we can extract all the &lt;code&gt;ColDef&lt;/code&gt; keys from. We can do this using the Typescript &lt;a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"&gt;Record&lt;/a&gt; utility 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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ColDefObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This type enforces that the object has every property from the &lt;code&gt;ColDef&lt;/code&gt; interface defined and set to &lt;code&gt;undefined&lt;/code&gt;. As we do not care about the actual values we give all the properties the &lt;code&gt;undefined&lt;/code&gt; type. &lt;/p&gt;

&lt;p&gt;With this we can produce the following &lt;code&gt;colDefProperties&lt;/code&gt; object using autocompletion in our IDE thanks to Typescript.&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;colDefProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColDefObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;columnGroupShow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;toolPanelClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;valueGetter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then to produce the complete list of &lt;code&gt;ColDef&lt;/code&gt; property names we can then pass this to &lt;code&gt;Object.keys&lt;/code&gt;. We cast back to &lt;code&gt;(keyof ColDef)[]&lt;/code&gt; as otherwise &lt;code&gt;ALL_PROPERTIES&lt;/code&gt; will be typed as &lt;code&gt;string[]&lt;/code&gt; which is not so helpful to us.&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;ALL_PROPERTIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;colDefProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="p"&gt;)[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;A bit of time spent finding the correct Typescript type can avoid the need to manually maintain code and ensure consistency without the need for complex code generators.&lt;/p&gt;

</description>
      <category>typescript</category>
    </item>
    <item>
      <title>Use AG Grid with Observables as cell values</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Thu, 05 Jan 2023 10:23:23 +0000</pubDate>
      <link>https://dev.to/ag-grid/use-ag-grid-with-observables-as-cell-values-89f</link>
      <guid>https://dev.to/ag-grid/use-ag-grid-with-observables-as-cell-values-89f</guid>
      <description>&lt;p&gt;In this guest post by &lt;a href="https://ngehlert.com/" rel="noopener noreferrer"&gt;Nicolas Gehlert&lt;/a&gt;, origianlly published &lt;a href="https://developapa.com/ag-grid-cell-observables/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, learn a clean approach to working with Observable data in Angular.&lt;/p&gt;

&lt;p&gt;Many thanks again to our user Nicolas for sharing his findings with us!&lt;br&gt;
&lt;/p&gt;


&lt;p&gt;AG Grid does support observables as data input, but only one dimensional. If you have an observable that contains other observables the values cannot be displayed.  &lt;/p&gt;

&lt;p&gt;In this post I want to show you scenarios where this might be useful and how you can display those values properly with a loading indicator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Example&lt;/li&gt;
&lt;li&gt;Custom cell renderer&lt;/li&gt;
&lt;li&gt;Loading overlay&lt;/li&gt;
&lt;li&gt;Sorting&lt;/li&gt;
&lt;li&gt;Clipboard&lt;/li&gt;
&lt;li&gt;CSV export&lt;/li&gt;
&lt;li&gt;Excel export&lt;/li&gt;
&lt;li&gt;Pitfalls&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Let me start of by saying, I'm aware that AG Grid is perfectly capable to handle (one dimensional) observables and for all the basic examples out there you probably don't need the approach that is described here.This is useful if you fetch data from different endpoints and they vastly differ in either the purpose or the loading duration.&lt;/p&gt;

&lt;p&gt;One real life example where I used this a lot is if you fetch base data of business objects from one endpoint. Those are properties like name and description. But from another endpoint you fetch some reporting information. The latter is drastically slower in performance and usually takes more than 5 seconds.&lt;/p&gt;

&lt;p&gt;The idea is to already display the information that is quickly available, while showing a loading indicator for the information that takes longer to fetch.&lt;/p&gt;

&lt;p&gt;In this post I will take the very common AG Grid example with car information and simulate a loading for the car costs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;I'm going to reference this in the very beginning. Everything described in this post can be found in action in this &lt;br&gt;
&lt;a href="https://stackblitz.com/github/ngehlert/ag-grid-observables?file=src%2Fapp%2Fapp.component.ts" rel="noopener noreferrer"&gt;stackblitz&lt;/a&gt; or this &lt;br&gt;
&lt;a href="https://github.com/ngehlert/ag-grid-observables" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
But it probably still makes sense to check each chapter step by step to follow the thought process.&lt;/p&gt;
&lt;h2&gt;
  
  
  Base setup
&lt;/h2&gt;

&lt;p&gt;In my example I will use Angular as a framework. But the concept works equally well for others like react or vue. Our base column definition is going to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;columnDefs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ITableEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;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="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;car.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;car.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;car.make&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;car.make&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pricingData&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ITableEntry&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;make&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="nx"&gt;pricingData&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Custom cell renderer
&lt;/h2&gt;

&lt;p&gt;When we provide some data to our grid that follows the interface from above we will get the following result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqw9091lofglfcf38scn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqw9091lofglfcf38scn.png" alt="Sample image that shows that observables are not resolved"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result is actually expected, because AG Grid tries to display a string representation of the given object in the DOM and is not actually trying to resolve this observable.&lt;/p&gt;

&lt;p&gt;We are going to create a custom cell renderer component that does this for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grid-text-renderer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;span&amp;gt;{{value|async}}&amp;lt;/span&amp;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;changeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnPush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgGridTextCellRendererComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererAngularComp&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererParams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;agInit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;agInit&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="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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part here is the &lt;code&gt;public value&lt;/code&gt; property to render the value in the template. The params assignment and the &lt;code&gt;refresh&lt;/code&gt; implementation are just my defaults to be consistent and performant for all custom cell renderer components.  &lt;/p&gt;

&lt;p&gt;The parameter &lt;code&gt;params: ICellRendererParams&lt;/code&gt; will get the cell value as defined in our column definition by the &lt;code&gt;field&lt;/code&gt; property. You can also use a &lt;code&gt;valueGetter&lt;/code&gt; to return an observable. However things like &lt;code&gt;valueFormatter&lt;/code&gt; won't work. If you need a custom format you need to implement it in your cell renderer component. You can also use &lt;code&gt;cellRendererParams&lt;/code&gt; on the column definition to pass additional properties or values to your component.&lt;/p&gt;

&lt;p&gt;As a last step we need to define in our column definition to actually use this 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="nl"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pricingData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;cellRenderer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AgGridTextCellRendererComponent&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;For more information on custom cell renderers check out the &lt;a href="https://www.ag-grid.com/angular-data-grid/component-cell-renderer/" rel="noopener noreferrer"&gt;AG Grid docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our result should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zi67o38rb3u3r8b1rs5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zi67o38rb3u3r8b1rs5.png" alt="Image that displays the resolved values from the observable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see the observables are properly resolved and the real number value is being displayed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading overlay
&lt;/h2&gt;

&lt;p&gt;We want to display a loading indicator to show to the user that the value for the cell is currently being loaded and he just needs to wait. There are different approaches how you can do this. You can extend the custom cell renderer we build in the previous step, and add a loading overlay that you trigger with &lt;code&gt;*ngIf&lt;/code&gt; and hide it once the observable is resolved. But keep in mind, if you build multiple custom cell renderer for different observable purposes you need to implement this behavior in all of them.&lt;/p&gt;

&lt;p&gt;Let me show you another AG Grid feature that is really awesome and pretty much made for cases like this. I'm talking about &lt;code&gt;cellClassRules&lt;/code&gt; on the column definition (&lt;a href="https://www.ag-grid.com/angular-data-grid/cell-styles/#cell-class-rules" rel="noopener noreferrer"&gt;see docs&lt;/a&gt;).  &lt;/p&gt;

&lt;p&gt;The base idea is that you can add/remove classes to each cell depending on the result of a function. For easier maintainability I've added this to the &lt;code&gt;defaultColDef&lt;/code&gt; in my &lt;code&gt;gridOptions&lt;/code&gt; so I don't have to redefine it for every column where I need this, but it is just automatically available. You can also add it to the regular column definition if you prefer.  &lt;/p&gt;

&lt;p&gt;Now let's talk code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;forkJoin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getDefaultColumnDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_isServerSideRowModel&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ColDef&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;loadingStateByEntry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&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;&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="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&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;&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;updateTableState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CellClassParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CellClassParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;loadingStateByEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refreshCells&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;suppressFlash&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;rowNodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cellClassRules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grid-table-cell-loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CellClassParams&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;=&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="na"&gt;mapEntry&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="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;loadingStateByEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapEntry&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mapEntry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Handle observables&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;loadingStateByEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateTableState&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateTableState&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="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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a map &lt;code&gt;loadingStateByEntry&lt;/code&gt; that represents the current loading state and whether an observable is already resolved or not. We use the reference of the observable as key and the loading state &lt;code&gt;true|false&lt;/code&gt; as value. The benefit of using the reference here, is if multiple rows share the same observable reference it is only added once.  &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;updateTableState&lt;/code&gt; function is used to update the table state and re-trigger the &lt;code&gt;cellClassRules&lt;/code&gt; evaluation. Everything from the observable happens outside of any ngZone or grid change detection. The loading state is set to false in our map and with the grid api we refresh our current cell.&lt;/p&gt;

&lt;p&gt;The key &lt;code&gt;'grid-table-cell-loading'&lt;/code&gt;in the &lt;code&gt;cellClassRules&lt;/code&gt; object is a css class name that will be added to the cell itself whenever the function provided as value is returning true.  &lt;/p&gt;

&lt;p&gt;First we need to check whether there is already an loading entry for our observable reference. Afterwards an &lt;code&gt;isObservable&lt;/code&gt; check to ensure &lt;br&gt;
we only trigger the next code if it is really an observable. &lt;/p&gt;

&lt;p&gt;Now we can just subscribe to the observable - we are using the &lt;code&gt;first()&lt;/code&gt; pipe to immediately unsubscribe and not create any memory leaks - &lt;br&gt;
and call our &lt;code&gt;updateTableState&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Last but not least we need to add a global style. In my case I'm just using a simple circle that spins around, but feel free to change it to a bar, &lt;br&gt;
hourglass or something more fancy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.grid-table-cell-loading&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-top-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;cornflowerblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-right-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;cornflowerblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spinner&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.grid-table-cell-loading&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;spinner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;360deg&lt;/span&gt;&lt;span class="p"&gt;);}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we should see a beautiful loading indicator while the observable is still loading and as soon as a value is resolved &lt;br&gt;
the loading indicator will disappear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdqqdzmbdlkcq2w5pd5wd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdqqdzmbdlkcq2w5pd5wd.gif" alt="Image to demonstrate the loading indicator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tip: If you want to use a different spinner style or want to adjust the styles let the function for the cell class rule return true.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sorting
&lt;/h2&gt;

&lt;p&gt;Feature wise we can stop after the previous chapter. The next couple of sections are just needed if you use the given feature.&lt;/p&gt;

&lt;p&gt;The sorting of AG Grid is not going to work, because the grid has no idea how to sort objects by reference. But AG Grid - like always - &lt;br&gt;
has an awesome option to help us. We can use the &lt;code&gt;postSortRows&lt;/code&gt; api for control over the sorted rows (&lt;a href="https://www.ag-grid.com/angular-data-grid/row-sorting/#post-sort" rel="noopener noreferrer"&gt;see docs&lt;/a&gt;)  &lt;/p&gt;

&lt;p&gt;The base idea is to resolve all observables for the current sorted column and afterwards trigger the grid sort again to let is sort properly.&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;postSortRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PostSortRowsParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skipNextPostSort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;skipNextPostSort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;columnStates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ColumnState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getColumnState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;columnStates&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;observables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEachNode&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;node&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nf"&gt;storeResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;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;observables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;forkJoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observables&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/**
             * If we re-sort after all observable data for a column sort is resolved we don't want
             * to execute postSort again (infinite loop)
             */&lt;/span&gt;
            &lt;span class="nx"&gt;skipNextPostSort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refreshClientSideRowModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sort&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core logic is in the &lt;code&gt;params.api.forEachNode&lt;/code&gt;. We want to iterate each row and see if the current value is an observable. If it is, we want to subscribe and store the given value with a little helper function I will explain in a second.  &lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;forkJoin&lt;/code&gt; we want to wait for all observables to be resolved and then re-sort the grid again. To store the values I'm using a &lt;code&gt;WeakMap&lt;/code&gt; (to prevent any memory issues) and store the value by observable reference.&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;valueByObservable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&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="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;storeResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&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;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;,):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;valueByObservable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getStoredResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&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;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;valueByObservable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we implement everything so far the result would still not work because AG Grid is still trying to sort by the reference.So we actually need a way to tell AG Grid to use our resolved value instead of the reference. On the column definition there is a &lt;code&gt;comparator&lt;/code&gt; property that allows us to specify a custom sort comparator function&lt;br&gt;
(&lt;a href="https://www.ag-grid.com/angular-data-grid/row-sorting/#custom-sorting" rel="noopener noreferrer"&gt;see docs&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getObservableNumberComparator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;valueB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nodeA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nodeB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isInverted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;valueB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_nodeA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_nodeB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueB&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getStoredResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getStoredResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;valueA&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;valueB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comparator function is very simple in this case. We just check if the provided values are actually observables and then fetch the result from our &lt;code&gt;WeakMap&lt;/code&gt; from the previous step.  &lt;/p&gt;

&lt;p&gt;If we update our column definition to include the &lt;code&gt;comparator&lt;/code&gt; it looks like this and we can finally sort our price column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;colId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pricingData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;cellRenderer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AgGridTextCellRendererComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;comparator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getObservableNumberComparator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Clipboard
&lt;/h2&gt;

&lt;p&gt;Same story as before. If you just try to use the clipboard copy out of the box you will get &lt;code&gt;[object Object]&lt;/code&gt; copied into your clipboard. Like for everything else you can imagine there is a neat little property on the &lt;code&gt;gripdOptions&lt;/code&gt; called &lt;code&gt;processCellForClipboard&lt;/code&gt; that allows us to hook into the process before the value is passed to the clipboard (&lt;a href="https://www.ag-grid.com/angular-data-grid/clipboard/#processing-individual-cells" rel="noopener noreferrer"&gt;see docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A simple check whether the given value is actually an observable and then checking our &lt;code&gt;WeakMap&lt;/code&gt; for the real value and returning this instead of the &lt;br&gt;
reference.&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;processCellForClipboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProcessCellForExportParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getStoredResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Keep in mind, this will only save your value to the clipboard if the value has already been resolved. If you try this during the loading, the &lt;br&gt;
result will just be an empty string.&lt;/p&gt;
&lt;h2&gt;
  
  
  CSV export
&lt;/h2&gt;

&lt;p&gt;Very similar as clipboard export. I'm just gonna link &lt;a href="https://www.ag-grid.com/angular-data-grid/csv-export/#reference-CsvExportParams-processCellCallback" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; and show the proper &lt;code&gt;gridOptions&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;defaultCsvExportParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProcessCellForExportParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getStoredResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Excel export
&lt;/h2&gt;

&lt;p&gt;Ok this is almost too easy… see &lt;a href="https://www.ag-grid.com/angular-data-grid/excel-export-customising-content/" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; and check out the &lt;code&gt;gridOptions&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;defaultExcelExportParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProcessCellForExportParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getStoredResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this is only logic you have in your export functions and you actually use all three of them you can extract this logic into a separate function and use this to avoid copy paste.&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="nf"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProcessCellForExportParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getStoredResolvedObservableValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then your &lt;code&gt;gridOptions&lt;/code&gt; are more readable:&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;processCellForClipboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;defaultCsvExportParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="nx"&gt;defaultExcelExportParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processCellCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pitfalls
&lt;/h2&gt;

&lt;p&gt;In this section I just try to document issues I or others had in the past. If you have anything else to add, please let me know.&lt;/p&gt;

&lt;h3&gt;
  
  
  DOM Virtualisation / observables are not triggered
&lt;/h3&gt;

&lt;p&gt;If we build everything properly, the observables are only resolved if they are visible - only then a &lt;code&gt;.subscribe()&lt;/code&gt; is being called. Be aware that all observables will be resolved upfront if you turn off row- or column virtualisation &lt;br&gt;
(&lt;a href="https://www.ag-grid.com/angular-data-grid/dom-virtualisation/" rel="noopener noreferrer"&gt;see docs&lt;/a&gt;)&lt;br&gt;&lt;br&gt;
And on the other hand don't be surprised if some observables are not triggered if they are currently not visible in the viewport.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP requests are executed multiple times
&lt;/h3&gt;

&lt;p&gt;If you are using HTTP requests for your observables you most like will notice that requests are now done multiple times. For most HTTP client implementations every separate &lt;code&gt;.subscribe()&lt;/code&gt; call will actually do a separate request. We are now doing this for example the view in the template, the sorting and the export.  &lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;shareReplay&lt;/code&gt; pipe (&lt;a href="https://www.ag-grid.com/angular-data-grid/dom-virtualisation/" rel="noopener noreferrer"&gt;see docs&lt;/a&gt;) to prevent this and all &lt;br&gt;
&lt;code&gt;.subscribe()&lt;/code&gt; calls will refer to the same HTTP request. Same concept applies if your original observable is very computation heavy. It's better to use the &lt;code&gt;shareReplay&lt;/code&gt; pipe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Huge tables and sorting
&lt;/h3&gt;

&lt;p&gt;If each cell uses a different observable and you have a grid with thousands of rows, be aware that if you sort for an observable column that &lt;strong&gt;every&lt;/strong&gt; observable gets resolved. It might make sense to disable the sorting in such a case. &lt;/p&gt;

&lt;h3&gt;
  
  
  Row model type
&lt;/h3&gt;

&lt;p&gt;This post was written for the client side row model. Other row models like server side, viewport or infinite (&lt;a href="https://www.ag-grid.com/angular-data-grid/row-models/#row-model-comparisons" rel="noopener noreferrer"&gt;see docs&lt;/a&gt; might not work or need additional configuration effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Streamed observables
&lt;/h3&gt;

&lt;p&gt;This approach currently works best for observables that are completed after the first result. From my experience that was all the use cases I had. Theoretically this is also possible for observables that emit multiple values, however you probably need to adjust couple of things. Find a better &lt;code&gt;unsubscribe&lt;/code&gt; logic in the sort function to replace the &lt;code&gt;first()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Alternatively you can also just create a new observable and re-assign it to the field in your grid data. The big advantage is that you get the loading indicator displayed again if you start a new request/loading time. &lt;/p&gt;

</description>
      <category>angular</category>
      <category>aggrid</category>
    </item>
    <item>
      <title>Does Angular Support Generic Component Types?</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Tue, 20 Dec 2022 16:49:14 +0000</pubDate>
      <link>https://dev.to/angular/does-angular-support-generic-component-types-4fkm</link>
      <guid>https://dev.to/angular/does-angular-support-generic-component-types-4fkm</guid>
      <description>&lt;p&gt;You may be familiar with writing &lt;a href="https://www.typescriptlang.org/docs/handbook/2/generics.html" rel="noopener noreferrer"&gt;Typescript Generics&lt;/a&gt; like &lt;code&gt;Grid&amp;lt;IRowData&amp;gt;&lt;/code&gt; but how does this work for Angular components? We can't provide generic types to our template selectors so how does Angular support generic components?&lt;/p&gt;

&lt;p&gt;In this article we will explain how Angular does it, but also how you can help the Angular Compiler be more accurate in complex components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typescript Generics
&lt;/h2&gt;

&lt;p&gt;Typescript &lt;a href="https://www.typescriptlang.org/docs/handbook/2/generics.html" rel="noopener noreferrer"&gt;Generics&lt;/a&gt; are a very powerful feature. Through generics we are able to customise existing Interfaces to a specific use case. For example, you might have a grid component to display rows of data. By making the grid component generic users can provide their own interface that represents their row data and have that interface used throughout the grid components' properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we want a Generic Component?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the technical details of generic components I think it's important that we understand why we want to have generic support for our components.&lt;/p&gt;

&lt;p&gt;Let's say we have a grid component that displays items and fires an event when users select a row. Without Generics we have to specify the type of the row data as &lt;code&gt;any&lt;/code&gt; because we want to re-use this grid component with different data sets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GridComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;onSelection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;We would use this &lt;code&gt;GridComponent&lt;/code&gt; like so in our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-car-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;app-grid [rowData]="rowData" (onSelection)="onRowSelected($event)"&amp;gt;&amp;lt;/app-grid&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CarDataComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Row data of cars to provide to the grid&lt;/span&gt;
    &lt;span class="nl"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="c1"&gt;// Callback when a row is selected&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onRowSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This all works as expected but what if we made a mistake in our code and defined the selection callback with an event of type &lt;code&gt;IPerson&lt;/code&gt; instead of &lt;code&gt;ICar&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onRowSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IPerson&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="c1"&gt;// ERROR: Function definition expects IPerson but it will be called with ICar!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our component does not complain as there is no link between the type of &lt;code&gt;rowData&lt;/code&gt; and &lt;code&gt;onRowSelected&lt;/code&gt;. Both &lt;code&gt;IPerson&lt;/code&gt; and &lt;code&gt;ICar&lt;/code&gt; are assignable to &lt;code&gt;any&lt;/code&gt; so there is no error but we have a bug that might only be spotted at runtime.&lt;/p&gt;

&lt;p&gt;However, if our component used generic types then Angular could warn us that the two properties do not match and we could fix the issue immediately. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Generic component
&lt;/h2&gt;

&lt;p&gt;You make a component generic by providing a generic type parameter within angle brackets as part of the class declaration: &lt;code&gt;GridComponent&amp;lt;TData&amp;gt;&lt;/code&gt;. You then use the generic type &lt;code&gt;TData&lt;/code&gt; throughout your class where ever that type should be used. &lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;GridComponent&lt;/code&gt; that looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({...})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GridComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="na"&gt;onSelection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&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;Note how we define &lt;code&gt;TData&lt;/code&gt; as our generic type and then use that in place of the &lt;code&gt;any&lt;/code&gt; type we previously had for &lt;code&gt;rowData&lt;/code&gt; and the selection callback. This has the desired effect of linking the type of &lt;code&gt;rowData&lt;/code&gt; with the &lt;code&gt;onSelection&lt;/code&gt; EventEmitter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a Generic Component
&lt;/h2&gt;

&lt;p&gt;If we were writing plain Typescript we would create the &lt;code&gt;GridComponent&lt;/code&gt; component and apply the generic type of &lt;code&gt;ICar&lt;/code&gt; as &lt;code&gt;new GridComponent&amp;lt;ICar&amp;gt;&lt;/code&gt;. Then all the generic properties would use the &lt;code&gt;ICar&lt;/code&gt; type in place of &lt;code&gt;TData&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myGrid&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;GridComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// type is ICar[]&lt;/span&gt;
&lt;span class="nx"&gt;myGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// type is EventEmitter&amp;lt;ICar&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;myGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSelection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this is &lt;em&gt;not&lt;/em&gt; how we generally create components in Angular. Instead we place the selector in our template and set the Input and Outputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;app-grid&lt;/span&gt; 
  &lt;span class="na"&gt;[rowData]=&lt;/span&gt;&lt;span class="s"&gt;"carData"&lt;/span&gt; 
  &lt;span class="na"&gt;(onSelection)=&lt;/span&gt;&lt;span class="s"&gt;"carSelected($event)"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/app-grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The key point is this: unlike &lt;code&gt;JSX&lt;/code&gt;, you cannot explicitly provide the generic type to the component selector!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, the following is &lt;em&gt;not&lt;/em&gt; valid 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="nt"&gt;&amp;lt;app-grid&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="na"&gt;ICar&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; [rowData]="carData" &amp;gt;&lt;span class="nt"&gt;&amp;lt;/app-grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You cannot provide generic types to an Angular component explicitly, but that doesn't stop Angular supporting generic components via another mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do generic components work?
&lt;/h2&gt;

&lt;p&gt;As we cannot specify the generic type explicitly, Angular has to infer it from the types of the Input and Outputs that we bind to the component. Let's step through this to explain how it works.&lt;/p&gt;

&lt;p&gt;Firstly, the component author specifies the generic type, &lt;code&gt;TData&lt;/code&gt;, and uses it for one or more properties.&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GridComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="na"&gt;onSelection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&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;The user of the component types their properties in the app 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="nx"&gt;carData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They then bind these to the component via an Input, in this case &lt;code&gt;rowData&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;app-grid&lt;/span&gt; &lt;span class="na"&gt;[rowData]=&lt;/span&gt;&lt;span class="s"&gt;"carData"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point the Angular compiler knows that the property &lt;code&gt;carData&lt;/code&gt; has the type &lt;code&gt;ICar[]&lt;/code&gt; and this has been assigned to the Input &lt;code&gt;rowData&lt;/code&gt; which has the type &lt;code&gt;TData[]&lt;/code&gt;. It then infers that the generic type &lt;code&gt;TData&lt;/code&gt; should be set to &lt;code&gt;ICar&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Using this information the compiler then applies the correct type to the &lt;code&gt;onSelection&lt;/code&gt; Output. That type then becomes &lt;code&gt;EventEmitter&amp;lt;ICar&amp;gt;&lt;/code&gt; and the compiler can validate that the types of &lt;code&gt;rowData&lt;/code&gt; and &lt;code&gt;onSelection&lt;/code&gt; are consistent.&lt;/p&gt;

&lt;p&gt;Now if we make a mistake in the configuration, and provide incompatible types to multiple generic properties, we will get a build error. This is great and just what we wanted!&lt;/p&gt;

&lt;p&gt;You may now be wondering why we said that Angular doesn't support generic types fully. It certainly seems like it does from this example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact of Inference
&lt;/h2&gt;

&lt;p&gt;In most cases when working with Generic components in Angular everything will work as expected. However, if the component gets more complex, in its use of the generic type parameter, then you may start to see a breakdown / widening in type inference. &lt;/p&gt;

&lt;p&gt;This happens because type inference in Typescript has to ensure it is accurate across every code path. When Typescript cannot narrow the type specified in every code path you may see that the generic type is inferred as &lt;code&gt;any&lt;/code&gt;. At this point you will stop getting template type errors because &lt;code&gt;any&lt;/code&gt; matches any type. &lt;/p&gt;

&lt;p&gt;In our example above this means the error about providing &lt;code&gt;IPerson&lt;/code&gt; to the selection event would just disappear.&lt;/p&gt;

&lt;p&gt;In these situations we need to start applying new techniques to help improve the developer experience with generics in Angular. We will do this by tweaking how we define our component's generic types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Breakdown of Inference
&lt;/h3&gt;

&lt;p&gt;To give a concrete example of the breakdown in inference we can look at the &lt;code&gt;ag-grid-angular&lt;/code&gt; component from &lt;a href="https://ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt;. This component is generic with respect to row data. It is defined in the following way with many properties omitted for brevity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ag-grid-angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgGridAngular&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;columnDefs&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;defaultColDef&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="na"&gt;rowSelected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowSelectedEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;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;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RowSelectedEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you write the following code with the &lt;code&gt;ag-grid-angular&lt;/code&gt; component you might expect that the generic type would be correctly inferred as &lt;code&gt;ICar&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;carData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nl"&gt;defaultColDef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ag-grid-angular&lt;/span&gt;
 &lt;span class="na"&gt;[rowData]=&lt;/span&gt;&lt;span class="s"&gt;"carData"&lt;/span&gt;
 &lt;span class="na"&gt;[defaultColDef]=&lt;/span&gt;&lt;span class="s"&gt;"defaultColDef"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ag-grid-angular&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it is not. Instead you will see that the generic type is &lt;code&gt;any&lt;/code&gt;. This means that there will be no template type checking across properties.&lt;/p&gt;

&lt;p&gt;After a bit of investigation it is possible to find the cause of the issue. When defining the &lt;code&gt;defaultColDef&lt;/code&gt; the type used is &lt;code&gt;ColDef&lt;/code&gt; with no generic type instead of &lt;code&gt;ColDef&amp;lt;ICar&amp;gt;&lt;/code&gt;. This means that the default generic type of &lt;code&gt;any&lt;/code&gt; is used. This &lt;code&gt;any&lt;/code&gt; is one of the possible types for the &lt;code&gt;TData&lt;/code&gt; generic type in the component preventing further narrowing of the &lt;code&gt;TData&lt;/code&gt; type to &lt;code&gt;ICar&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enforce Generic Parameters
&lt;/h3&gt;

&lt;p&gt;One solution to ensure that generic types are correctly inferred is to enforce that your users supply them to every interface.&lt;/p&gt;

&lt;p&gt;This can be enforced by not providing a default type. So instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You would define the interface as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are tradeoffs with this approach though. For AG Grid this would have resulted in a major breaking change for all Typescript users. They suddenly would have been forced to update every type declaration to include a generic property. We did not feel this would be well received. &lt;/p&gt;

&lt;p&gt;If you are in the different position of creating a new component from scratch, then maybe you could consider not supplying a default type as this will lead to more accurate and consistent type inference in the long run.&lt;/p&gt;

&lt;p&gt;But what if you need to provide a default &lt;code&gt;any&lt;/code&gt; type?&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Code Inference in Angular
&lt;/h2&gt;

&lt;p&gt;At this point we need to take a deeper look at how inference for Angular components works. It turns out that it is similar to the following static code structure.&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;static&lt;/span&gt; &lt;span class="nx"&gt;typeCtor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&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;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GridComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rowData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onSelection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;GridComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us a useful mechanism for experimenting with different generic setups without the need to run the Angular compiler over our component in a template. This means you could validate inference for a component in an easily shareable environment like TS Playground. &lt;/p&gt;

&lt;p&gt;You can use this approach to experiment with your generic types to find those that give the best developer experience based on common use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Influencing Type Inference
&lt;/h3&gt;

&lt;p&gt;This next section covers the specific changes made to the AG Grid component to improve its generic type support. We are trying to prevent the use of the &lt;code&gt;ColDef&lt;/code&gt; interface with no type parameters from leading to a breakdown in inference of the &lt;code&gt;TData&lt;/code&gt; generic type.&lt;/p&gt;

&lt;p&gt;The way we do this is to introduce a second generic parameter, &lt;code&gt;TColDef&lt;/code&gt; that is derived from the first: &lt;code&gt;TColDef extends ColDef&amp;lt;TData&amp;gt;&lt;/code&gt;. This is entirely for the purpose of changing how inference works and has no other implications.&lt;/p&gt;

&lt;p&gt;Our component definition changes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- class AgGridAngular&amp;lt;TData = any&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ class AgGridAngular&amp;lt;TData = any, TColDef extends ColDef&amp;lt;TData&amp;gt; = ColDef&amp;lt;any&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we update our &lt;code&gt;columnDefs&lt;/code&gt; Input to use this new derived generic parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- @Input() columnDefs?: ColDef&amp;lt;TData&amp;gt;[];
&lt;/span&gt;&lt;span class="gi"&gt;+ @Input() columnDefs?: TColDef[];
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We provide a default value of &lt;code&gt;ColDef&amp;lt;any&amp;gt;&lt;/code&gt; to &lt;code&gt;TColDef&lt;/code&gt;, so that we do not changed the interface requirements of &lt;code&gt;AgGridAngular&lt;/code&gt; in case the component is used in a ViewChild selector. &lt;/p&gt;

&lt;p&gt;However, as most users will only define the component in their template with &lt;code&gt;ag-grid-angular&lt;/code&gt; this change will likely be invisible to them. They do not have to worry about the extra generic type parameter as they never specified them in the first place.&lt;/p&gt;

&lt;p&gt;On the surface you may not expect this to change anything as you are simply redefining the &lt;code&gt;columnDefs&lt;/code&gt; type as part of the generic parameters. We haven't actually changed any types here. However, this relocation has the effect of separating which properties Typescript tries to infer to the same type. The result for the &lt;code&gt;AgGridAngular&lt;/code&gt; component is that the &lt;code&gt;columnDefs&lt;/code&gt; property has a stronger influence on the inferred type of &lt;code&gt;TData&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now let's show the impact of this change in real application code.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Improved Inference Results
&lt;/h2&gt;

&lt;p&gt;To see the result of this change we will show the exact same code with two different versions of AG Grid. (As this is all about the types I have omitted the actual implementation and just show the typings)&lt;/p&gt;

&lt;p&gt;We define our properties and provide the &lt;code&gt;ICar&lt;/code&gt; interface as the generic parameter to &lt;code&gt;columnDefs&lt;/code&gt; and &lt;code&gt;rowData&lt;/code&gt;. We don't provide a generic parameter to &lt;code&gt;defaultColDef&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we simulate an error by setting the generic parameter to  &lt;code&gt;IPerson&lt;/code&gt; for the &lt;code&gt;onRowSelected&lt;/code&gt; callback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;columnDefs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICar&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;defaultColDef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColDef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;rowData$&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;ICar&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//This should result in an error as IPerson != ICar&lt;/span&gt;
&lt;span class="nf"&gt;onRowSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowSelectedEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IPerson&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then assign these to the component as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ag-grid-angular&lt;/span&gt;
  &lt;span class="na"&gt;[columnDefs]=&lt;/span&gt;&lt;span class="s"&gt;"columnDefs"&lt;/span&gt; 
  &lt;span class="na"&gt;[defaultColDef]=&lt;/span&gt;&lt;span class="s"&gt;"defaultColDef"&lt;/span&gt;
  &lt;span class="na"&gt;[rowData]=&lt;/span&gt;&lt;span class="s"&gt;"rowData$ | async"&lt;/span&gt;
  &lt;span class="na"&gt;(rowSelected)=&lt;/span&gt;&lt;span class="s"&gt;"onRowSelected($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ag-grid-angular&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you were to use AG Grid v28.0 this code would compile as the generic parameter fallsback to &lt;code&gt;any&lt;/code&gt; as can be seen in this screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freor2w9q6lldhxxjnfyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freor2w9q6lldhxxjnfyd.png" alt="IDE Code showing generic type inferred as any" width="446" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, with our updated typings in the latest versions of AG Grid, this same code will result in a compile error as expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dnojez0iok2tw47zz31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dnojez0iok2tw47zz31.png" alt="Build error: ICar and IPerson not compatible" width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now also can see how the generic parameter has been correctly inferred along with improved IDE error highlighting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foraowaj51tg3xi9b7rwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foraowaj51tg3xi9b7rwl.png" alt="IDE Code showing correct inference of TData generic type" width="572" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is great news because it means we have been able to improve the typing experience for AG Grid users without forcing them to add generic parameters to all their existing code.&lt;/p&gt;

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

&lt;p&gt;While in many cases Angular will correctly infer your component types, when it does not, I hope that by sharing this knowledge you will be in a stronger position to provide hints to the Angular compiler to get it back on track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;My thanks go to Alex Rickabaugh &lt;a href="https://twitter.com/synalx" rel="noopener noreferrer"&gt;@synalx&lt;/a&gt; from the Angular core team for showing me this approach in the Hallway track at NG Conf. It turns out this is also used to improve the typings for &lt;code&gt;ngFor&lt;/code&gt;. See &lt;a href="https://github.com/angular/angular/blob/main/packages/common/src/directives/ng_for_of.ts#L18" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Big thanks to Alex!&lt;/p&gt;




&lt;p&gt;Stephen Cooper - Senior Developer at &lt;a href="https://ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt;&lt;br&gt;
Twitter &lt;a href="https://twitter.com/SCooperDev" rel="noopener noreferrer"&gt;@ScooperDev&lt;/a&gt; or &lt;a href="https://twitter.com/intent/tweet?text=%22Does%20Angular%20Support%20Generic%20Component%20Types%3F%22%20by%20%40SCooperDev%20%23DEVCommunity%20https%3A%2F%2Fdev.to%2Fangular%2Fdoes-angular-support-generic-component-types-4fkm"&gt;Tweet&lt;/a&gt; about this post.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Reducing Angular Library Contributions to the Main Bundle</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Tue, 06 Dec 2022 09:39:48 +0000</pubDate>
      <link>https://dev.to/ag-grid/reducing-angular-library-contributions-to-the-main-bundle-3114</link>
      <guid>https://dev.to/ag-grid/reducing-angular-library-contributions-to-the-main-bundle-3114</guid>
      <description>&lt;p&gt;In this guest post by &lt;a href="https://ngehlert.com/" rel="noopener noreferrer"&gt;Nicolas Gehlert&lt;/a&gt;, origianlly published &lt;a href="https://developapa.com/angular-lazy-load/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, learn the best way to include AG Grid in your own shared Angular library that will not result in AG Grid being included in the main bundle, as long as it is only used in lazy loaded routes.&lt;/p&gt;

&lt;p&gt;Many thanks again to our user Nicolas for sharing his findings with us!&lt;/p&gt;




&lt;p&gt;Using an Angular workspace with your own library you might've noticed that your main bundle size keeps increasing the more features you are adding - even if you &lt;br&gt;
are not using all features up front. Let me show you how you can build a sustainable and lightweight library structure for your own library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Bundle analyzer&lt;/li&gt;
&lt;li&gt;Problem analysis&lt;/li&gt;
&lt;li&gt;Solution&lt;/li&gt;
&lt;li&gt;Test for yourself&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Angular libraries are awesome and Angular does a good job giving you a kick start with their &lt;a href="https://angular.io/guide/creating-libraries" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. &lt;br&gt;
However this only works for very simple and small libraries. If your library gets bigger and you want to optimize it, I found &lt;br&gt;
there to be very few good resources.  &lt;/p&gt;

&lt;p&gt;Let me tell you an example. I'm using a big library with dependencies like AG Grid to provide shared components and services &lt;br&gt;
in a multi application Angular workspace. I'm using lazy loading for all my routes but I discovered the main bundle get's increased &lt;br&gt;
for all applications if I add more things to the library. At some point I was just using a simple data service in the main &lt;br&gt;
application and an AG Grid component few lazy loaded modules down the line, but still AG Grid and all components were added to the &lt;br&gt;
main bundle.&lt;br&gt;&lt;br&gt;
In this post I will show you how to analyze your package and how to properly set up your library in order to prevent similar issues.&lt;/p&gt;

&lt;p&gt;All examples in this post use a sample project at &lt;a href="https://github.com/ngehlert/angular-lazy-load" rel="noopener noreferrer"&gt;https://github.com/ngehlert/angular-lazy-load&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bundle analyzer
&lt;/h2&gt;

&lt;p&gt;A bundle analyzer allows you to get insights in your bundled JavaScript. It shows you what dependencies are included and how much space &lt;br&gt;
each section needs.&lt;br&gt;&lt;br&gt;
The Angular team recommends to use a tool called &lt;code&gt;source-map-explorer&lt;/code&gt;. A lot of other post show tools like &lt;code&gt;webpack-bundle-analyzer&lt;/code&gt;&lt;br&gt;
or others. To some extend they work as well, but let's stick with the &lt;code&gt;source-map-explorer&lt;/code&gt; for now. &lt;/p&gt;

&lt;p&gt;First we need to install it with npm or yarn&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; source-map-explorer
yarn global add source-map-explorer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now build your project with the &lt;code&gt;--source-map=true&lt;/code&gt; flag. (If you are using a project with an Angular version prior 12 also add the &lt;code&gt;--prod&lt;/code&gt; flag)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng build app &lt;span class="nt"&gt;--source-map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can have a look at our stats with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;source-map-explorer dist/app/main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result looks something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frblcuwko6dsfy6v4mi8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frblcuwko6dsfy6v4mi8r.png" alt="Original Bloated Main Bundle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem analysis
&lt;/h2&gt;

&lt;p&gt;Let's have a look at our sample application. We are using a small service and a component to display a simple table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhwpck58gzpjzzclqx9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffhwpck58gzpjzzclqx9x.png" alt="Original Library Structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;app.component.ts&lt;/code&gt; we are using the &lt;code&gt;RandomService&lt;/code&gt; and in our lazy loaded route the &lt;code&gt;TableComponent&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
But if we have a look at our bundle analysis (see screenshot above) we will notice that AG Grid is actually added to the main module, instead of the lazy loaded one.&lt;/p&gt;

&lt;p&gt;It turns out by default an Angular library is actually not tree shakeable, and you will always add the entire library to your bundle, even if you are just &lt;br&gt;
importing one service or variable. &lt;/p&gt;
&lt;h2&gt;
  
  
  Solution - Multiple Entry points
&lt;/h2&gt;

&lt;p&gt;You can create multiple entry points to your library and then you only need to import a specific entry point you are interested in, instead of the entire library. &lt;br&gt;
Angular material is using the same concept, for example &lt;code&gt;import {MatDialog} from '@angular/material/dialog';&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Creating separate entry points is actually fairly simple and you only need to create a new directory inside your library and add an &lt;code&gt;ng-package.json&lt;/code&gt; file. &lt;br&gt;
Inside the &lt;code&gt;ng-package.json&lt;/code&gt; you need to put the following content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../../../../node_modules/ng-packagr/ng-package.schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"entryFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"public_api.ts"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our example I moved the &lt;code&gt;table&lt;/code&gt; directory into a separate &lt;code&gt;grid&lt;/code&gt; directory next to the already existing &lt;code&gt;src&lt;/code&gt; directory. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7agtup98b0xqqw6j5cx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7agtup98b0xqqw6j5cx9.png" alt="Multiple Entry Points"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you now run &lt;code&gt;ng build good-lib&lt;/code&gt; you will notice that actually two entry points are created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Building Angular Package

------------------------------------------------------------------------------
Building entry point 'good-lib'
------------------------------------------------------------------------------
✔ Compiling with Angular sources in Ivy partial compilation mode.
✔ Writing FESM bundles
✔ Copying assets
✔ Writing package manifest
✔ Built good-lib

------------------------------------------------------------------------------
Building entry point 'good-lib/grid'
------------------------------------------------------------------------------
✔ Compiling with Angular sources in Ivy partial compilation mode.
✔ Writing FESM bundles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now just one step is missing in order to use the secondary entry points. Go to your &lt;code&gt;tsconfig.json&lt;/code&gt; (in your root directory). And extend the path entry for your library with a &lt;br&gt;
placeholder match.&lt;br&gt;&lt;br&gt;
before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"good-lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dist/good-lib"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"good-lib/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dist/good-lib/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"good-lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dist/good-lib"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: You just need to do this once for your first secondary entry point. All future entry points will be resolved as well.&lt;/p&gt;

&lt;p&gt;You can now use the &lt;code&gt;TableModule&lt;/code&gt; from the secondary entry point like this &lt;code&gt;import { TableModule } from 'good-lib/grid'&lt;/code&gt;;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to reference between the different entry points
&lt;/h2&gt;

&lt;p&gt;You can NOT use relative imports between difference entry points. You need to import from the build package like you would from a regular application &lt;br&gt;
as well. In our example this would be &lt;code&gt;import {RandomService} from 'good-lib';&lt;/code&gt; if you want to use it in the grid entry point.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;ng-packagr&lt;/code&gt; will detect in which order it needs to compile the different entry points so everything can be resolved. You will get an &lt;br&gt;
error if you introduce a circular dependency and two (or more) entry points actually try to import from each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test for yourself
&lt;/h2&gt;

&lt;p&gt;You can clone the &lt;a href="https://github.com/ngehlert/angular-lazy-load" rel="noopener noreferrer"&gt;https://github.com/ngehlert/angular-lazy-load&lt;/a&gt; and just need to change three imports&lt;br&gt;
to switch between the good and the bad library. &lt;a href="https://github.com/ngehlert/angular-lazy-load/commit/90995878740d9df7139beebb43bee0cd54967136" rel="noopener noreferrer"&gt;See what imports to change&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In the end with the good library AG Grid gets added to the lazy loaded route instead of the initial bundle. Now our initial bundle is only 200kb instead of 1.3mb!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ywtorj3gkt1ij20un13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ywtorj3gkt1ij20un13.png" alt="Smaller Main Bundle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For further details about minimizing the AG Grid bundle size in your application make sure to also read our other post &lt;a href="https://blog.ag-grid.com/minimising-bundle-size/" rel="noopener noreferrer"&gt;Minimising Application Bundle Size&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
    </item>
    <item>
      <title>Code Refactoring with Regex Find and Replace</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Fri, 14 Oct 2022 13:39:17 +0000</pubDate>
      <link>https://dev.to/scooperdev/powerful-refactoring-with-regexes-3237</link>
      <guid>https://dev.to/scooperdev/powerful-refactoring-with-regexes-3237</guid>
      <description>&lt;p&gt;When faced with a major refactoring task across your codebase don't forget you can use Regexes to do the heavy lifting for you, potentially saving you hours of work!&lt;/p&gt;

&lt;p&gt;Let me show how I used a Regex to update over 1000 method calls across hundreds of files in about 5 minutes instead of a day's work!&lt;/p&gt;

&lt;h2&gt;
  
  
  My Refactoring Task
&lt;/h2&gt;

&lt;p&gt;In one section of code, I had many duplicated methods that could be replaced with a single generic getter. All the methods did was coerce a value from a configuration object and return this to the rest of the codebase without exposing the underlying object.&lt;/p&gt;

&lt;p&gt;But how do you go about updating hundreds of these method calls when there are many different properties names and call locations? &lt;/p&gt;

&lt;p&gt;Fortunately, all the methods follow a naming convention so we can take advantage of this and automate the update via Regex find and replace. &lt;/p&gt;

&lt;h3&gt;
  
  
  Old Code
&lt;/h3&gt;

&lt;p&gt;We had many methods of this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;isMaintainColumnOrder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maintainColumnOrder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;isSuppressRowTransform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suppressRowTransform&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;isSuppressColumnStateEvents&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suppressColumnEvents&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;Which were then called like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridOptionsWrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMaintainColumnOrder&lt;/span&gt;&lt;span class="p"&gt;()){&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New Code
&lt;/h3&gt;

&lt;p&gt;To be replaced with calls to the following method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;GridOptions&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridOptions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&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;Called like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridOptionsWrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;maintainColumnOrder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The change will result in the following diff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- if(this.gridOptionsWrapper.isMaintainColumnOrder()){
&lt;/span&gt;&lt;span class="gi"&gt;+ if(this.gridOptionsWrapper.is('maintainColumnOrder'){
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Breaking Down the Changes Required
&lt;/h2&gt;

&lt;p&gt;Thanks to the naming convention the changes follow this pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract the property name after the &lt;code&gt;is&lt;/code&gt; prefix in the original method call&lt;/li&gt;
&lt;li&gt;Lowercase the first letter of the matched property name&lt;/li&gt;
&lt;li&gt;Place the updated match into single quotes and parenthesis &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Required Regexes
&lt;/h2&gt;

&lt;p&gt;When doing a find and replace in VS Code we require two regexes. The first is used to find the code to update and extract the required information. The second regex is used to format the information, via a transformation, to be the new code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find Regex
&lt;/h3&gt;

&lt;p&gt;This is the regex we use to match all our method calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;gridOptionsWrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gridOptionsWrapper.is&lt;/code&gt;: first find this string&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(\w+)&lt;/code&gt;: now match one or more word characters for the method name and extract this into the regex group result&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\(\)&lt;/code&gt;: match the parenthesis &lt;code&gt;()&lt;/code&gt;. &lt;code&gt;\&lt;/code&gt; is an escape character so we can match a parenthesis. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the Regex sees the following code we will get a full match and also the extracted group match.&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;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridOptionsWrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMaintainColumnOrder&lt;/span&gt;&lt;span class="p"&gt;()){&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Full Match: &lt;code&gt;gridOptionsWrapper.isMaintainColumnOrder()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Group: &lt;code&gt;MaintainColumnOrder&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is enough information for us to make the transformation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replacer
&lt;/h3&gt;

&lt;p&gt;This is the regex we use to construct the new method call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gridOptionsWrapper.is('\l$1')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This replaces the code in the following way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gridOptionsWrapper.is&lt;/code&gt; remains unchanged&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;('&lt;/code&gt; open parenthesis and single quote&lt;/li&gt;
&lt;li&gt; &lt;code&gt;\l&lt;/code&gt; lowercase the first letter of the next section&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$1&lt;/code&gt; provide the matched group&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;')&lt;/code&gt; close quote and parenthesis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives us the required diff for every single one of our &lt;code&gt;is&lt;/code&gt; lookup methods no matter what the property name is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- if(this.gridOptionsWrapper.isMaintainColumnOrder()){
&lt;/span&gt;&lt;span class="gi"&gt;+ if(this.gridOptionsWrapper.is('maintainColumnOrder'){
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- if(this.gridOptionsWrapper.isSuppressRowTransform()){
&lt;/span&gt;&lt;span class="gi"&gt;+ if(this.gridOptionsWrapper.is('suppressRowTransform'){
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it is a matter of running through the changes or just hitting that "Replace All" button and committing! :P&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp47w6t96ygaiztfcs2h8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp47w6t96ygaiztfcs2h8.png" alt="VS Code Regex Find and Replace"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Don't forget about all the tools built into your IDE that can save you time and effort. Using Regexes for Find / Replace is just one example of the tools at your disposal.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Write Typescript in the browser with SystemJs</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Tue, 13 Sep 2022 10:49:34 +0000</pubDate>
      <link>https://dev.to/scooperdev/write-typescript-in-the-browser-with-systemjs-4llc</link>
      <guid>https://dev.to/scooperdev/write-typescript-in-the-browser-with-systemjs-4llc</guid>
      <description>&lt;p&gt;&lt;em&gt;(This post is just a rough public note about a side effect of some of my work. May or may not be helpful :) )&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Do you love Typescript and wish you could use it to power a simple toy HTML sites that you want to play with? Well you can with SystemJs and a bit of on the fly browser compilation. &lt;/p&gt;

&lt;p&gt;You don't always need a fancy setup :P&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Index.html + Typescript files
&lt;/h2&gt;

&lt;p&gt;Check out this demo on &lt;a href="https://plnkr.co/edit/qIXpl9gavsVd5aFF?open=main.ts"&gt;Plunker&lt;/a&gt; that shows you how you can write Typescript files and use their methods from your Index.html file.&lt;/p&gt;

&lt;p&gt;Yes, this does require you to get your SystemJs config setup right but that is why I am sharing this starter that I used for our &lt;a href="https://ag-grid.com/"&gt;AG Grid&lt;/a&gt; demos so that you can get started easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="c1"&gt;// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER&lt;/span&gt;
        &lt;span class="na"&gt;transpiler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;typescriptOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Copy of compiler options in standard tsconfig.json&lt;/span&gt;
            &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//gets rid of console warning&lt;/span&gt;
            &lt;span class="na"&gt;moduleResolution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;sourceMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;emitDecoratorMetadata&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;experimentalDecorators&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;lib&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;es2015&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="na"&gt;noImplicitAny&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;suppressImplicitAnyIndexErrors&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;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;typescript&lt;/span&gt;&lt;span class="p"&gt;:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;css&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;Aside from the systemjs.config.js file the main part of the code are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/systemjs@0.19.47/dist/system.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"systemjs.config.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;System&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./main.ts&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;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Attaching methods to the window scope so can be called from HTML. (This is done for you when you just use a plain Javascript file)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"onBtExcludeMedalColumns()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Exclude Medal Columns&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Attach external event handlers to window so they can be called from index.html&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onBtExcludeMedalColumns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;onBtExcludeMedalColumns&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's kind of it! &lt;/p&gt;

&lt;p&gt;(This post is just a rough public note about a side effect of some of my work. May or may not be helpful :) )&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>browser</category>
      <category>beginners</category>
    </item>
    <item>
      <title>AG Grid: Typescript Generics</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Thu, 14 Jul 2022 10:31:53 +0000</pubDate>
      <link>https://dev.to/ag-grid/ag-grid-typescript-generics-21bh</link>
      <guid>https://dev.to/ag-grid/ag-grid-typescript-generics-21bh</guid>
      <description>&lt;p&gt;In this article, we will show you how to make the most of Typescript Generics in AG Grid &lt;a href="https://blog.ag-grid.com/whats-new-in-ag-grid-28/" rel="noopener noreferrer"&gt;v28&lt;/a&gt;. We will demonstrate the great developer experience it unlocks with the help of code examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  AG Grid Generic Types
&lt;/h2&gt;

&lt;p&gt;There are two generic types that you can pass to AG Grid interfaces. They are: &lt;code&gt;TData&lt;/code&gt; and &lt;code&gt;TValue&lt;/code&gt;.  &lt;/p&gt;

&lt;h3&gt;
  
  
  TData - Row Data Type
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;TData&lt;/code&gt; is used to represent the shape of row data items. This generic parameter should match the interface used when defining your row data. If an AG Grid interface has a &lt;code&gt;TData&lt;/code&gt; generic parameter this will always refer to the row data type.&lt;/p&gt;

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

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;GridOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&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;rowData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;To maintain backwards compatibility a default type of &lt;code&gt;any&lt;/code&gt; is provided. This mimics the behaviour of previous versions of AG Grid that explicitly type the rowData as &lt;code&gt;any[]&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  TValue - Cell Value Type
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;TValue&lt;/code&gt; is used to represent the type of a specific cell value. This generic type is more limited in scope and can be used within cell renderers/value getters when you want to give a type to the &lt;code&gt;value&lt;/code&gt; property.&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;GetQuickFilterTextParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/** Value for the cell. */&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;As with &lt;code&gt;TData&lt;/code&gt;, we also default &lt;code&gt;TValue&lt;/code&gt; to &lt;code&gt;any&lt;/code&gt; to maintain backwards compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Generic Types
&lt;/h2&gt;

&lt;p&gt;In the examples below we use the &lt;code&gt;ICar&lt;/code&gt; interface to represent row data.&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;// Row Data interface&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;make&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toyota&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Celica&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Porsche&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Boxster&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;72000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The best place to assign &lt;code&gt;ICar&lt;/code&gt; to the &lt;code&gt;TData&lt;/code&gt; generic parameter is via the &lt;code&gt;GridOptions&lt;/code&gt; interface. This will cascade the generic type down the interface hierarchy. &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;// Pass ICar to GridOptions as a generic&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gridOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GridOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICar&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="c1"&gt;// rowData is typed as ICar[]&lt;/span&gt;
    &lt;span class="na"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ford&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Galaxy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;With the generic parameter set to &lt;code&gt;ICar&lt;/code&gt;, the &lt;code&gt;rowData&lt;/code&gt; property has the type &lt;code&gt;ICar[]&lt;/code&gt; instead of the default &lt;code&gt;any[]&lt;/code&gt;. This means that if you mistype one of the properties you will get a compile-time error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkudnm386ilzudfmlr3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkudnm386ilzudfmlr3d.png" alt="Row Data enforces ICar interface"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is worth noting that it is not just &lt;code&gt;rowData&lt;/code&gt; where the interface is used. For example, in the &lt;code&gt;getRowId&lt;/code&gt; callback, the &lt;code&gt;params.data&lt;/code&gt; property is typed as &lt;code&gt;ICar&lt;/code&gt; instead of &lt;code&gt;any&lt;/code&gt;. This is a result of AG Grid cascading the generic type down from &lt;code&gt;GridOptions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This also applies to all the grid events that contain a data property. i.e &lt;code&gt;onRowSelected&lt;/code&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;gridOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GridOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICar&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="c1"&gt;// Callback with params type: GetRowIdParams&amp;lt;ICar&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;getRowId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// params.data : ICar&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;make&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// Event with type: RowSelectedEvent&amp;lt;ICar&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;onRowSelected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// event.data: ICar | undefined&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;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;If you use the grid api via &lt;code&gt;gridOptions.api&lt;/code&gt; or callback &lt;code&gt;params.api&lt;/code&gt; properties, then the api will be aware of the row data type too. This means that the method &lt;code&gt;api.getSelectedRows()&lt;/code&gt; will return rows as &lt;code&gt;ICar[]&lt;/code&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;// Grid Api methods use ICar interface&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onSelection&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;cars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gridOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSelectedRows&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;
  
  
  Configure via Individual Interfaces
&lt;/h3&gt;

&lt;p&gt;While it is possible to configure everything inline within a &lt;code&gt;gridOptions&lt;/code&gt; object, you may want to split things out for readability / re-usability. To benefit from Typescript generics we can provide our &lt;code&gt;ICar&lt;/code&gt; interface to any AG Grid interface that accepts a &lt;code&gt;TData&lt;/code&gt; generic parameter. &lt;/p&gt;

&lt;p&gt;For example, we could configure our event handler with &lt;br&gt;
 &lt;code&gt;RowSelectedEvent&amp;lt;ICar&amp;gt;&lt;/code&gt;. This results in the &lt;code&gt;event.data&lt;/code&gt; property being typed as &lt;code&gt;ICar | undefined&lt;/code&gt;. (See &lt;em&gt;Generic Type or Undefined&lt;/em&gt; below for more details on the additional &lt;code&gt;undefined&lt;/code&gt; type).&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="nf"&gt;onRowSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RowSelectedEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// event.data: ICar | undefined&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Advantages over any
&lt;/h2&gt;

&lt;p&gt;There are two main reasons to provide generic types to AG Grid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compile-time errors&lt;/li&gt;
&lt;li&gt;Auto-completion in your IDE&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Compile Time Errors
&lt;/h3&gt;

&lt;p&gt;Say we mistyped &lt;code&gt;price&lt;/code&gt; as &lt;code&gt;prive&lt;/code&gt;. Without generics, your application code would compile but your grid would no longer show prices in the grid. You would then have to track down the source of the bug. It could even be possible that this type of bug escapes into production if it slips past your tests. &lt;/p&gt;

&lt;p&gt;However, if you supply generic types your application would not compile and would tell you the exact location of the bug. This enables you to instantly correct the code, instead of having to track down the bug. It will also be underlined with a red squiggle in your IDE! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfbit9jbag266me45s5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfbit9jbag266me45s5s.png" alt="Compile time error for incorrect property name"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Auto-Completion
&lt;/h3&gt;

&lt;p&gt;As Typescript knows that our &lt;code&gt;data&lt;/code&gt; object conforms to the &lt;code&gt;ICar&lt;/code&gt; interface our IDE can suggest properties to us. This speeds up your development process as you do not have to spend time thinking about property names as they are suggested to you automatically. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbu459i2hbchhx81kush2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbu459i2hbchhx81kush2.png" alt="Auto completion of data properties based on generic type"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Framework Specific Demo
&lt;/h2&gt;
&lt;h3&gt;
  
  
  React
&lt;/h3&gt;

&lt;p&gt;If you are using AG Grid via the React component then you can pass your row data type to the &lt;code&gt;AgGridReact&lt;/code&gt; component via this syntax: &lt;code&gt;&amp;lt;AgGridReact&amp;lt;ICar&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This will validate that all &lt;code&gt;props&lt;/code&gt; conform to the row data type. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AgGridReact&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="na"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   ref=&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gridRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   rowData=&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   columnDefs=&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columnDefs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   onGridReady=&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onGridReady&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&amp;gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AgGridReact&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If you provide conflicting types you will get a compile-time error. For example, here we have provided a different generic type, &lt;code&gt;INotCar&lt;/code&gt; to the &lt;code&gt;onGridReady&lt;/code&gt; event which does not match our &lt;code&gt;rowData&lt;/code&gt; of &lt;code&gt;ICar&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvg9rmigrytqqqt9xagx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvg9rmigrytqqqt9xagx.png" alt="React validating generic types are consistent"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;For Angular, providing a type to your &lt;code&gt;rowData&lt;/code&gt; property is enough to enable generics for the entire component. &lt;/p&gt;

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

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;rowData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;


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

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

 &lt;span class="nt"&gt;&amp;lt;ag-grid-angular&lt;/span&gt;
      &lt;span class="na"&gt;[columnDefs]=&lt;/span&gt;&lt;span class="s"&gt;"columnDefs"&lt;/span&gt;
      &lt;span class="na"&gt;[rowData]=&lt;/span&gt;&lt;span class="s"&gt;"rowData"&lt;/span&gt;
      &lt;span class="na"&gt;(gridReady)=&lt;/span&gt;&lt;span class="s"&gt;"onGridReady($event)"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/ag-grid-angular&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If you are not using the &lt;code&gt;rowData&lt;/code&gt; property, then type any other input/output that takes the generic parameter. For example, setting the following type on the &lt;code&gt;onGridReady&lt;/code&gt; event would set the generic type for the entire component.&lt;/p&gt;

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

&lt;span class="nf"&gt;onGridReady&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GridReadyEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ICar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If you provide conflicting types you will get a compile-time error. For example, here we have provided a different generic type, &lt;code&gt;INotCar&lt;/code&gt; to the &lt;code&gt;onGridReady&lt;/code&gt; event which does not match our &lt;code&gt;rowData&lt;/code&gt; type of &lt;code&gt;ICar&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylxp83ir65wbvjh9z0lk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylxp83ir65wbvjh9z0lk.png" alt="Angular validating generic types are consistent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic Type or Undefined
&lt;/h2&gt;

&lt;p&gt;One thing you may notice when adding generic support to an existing application is that you may get errors relating to the &lt;code&gt;data&lt;/code&gt; property potentially being &lt;code&gt;undefined&lt;/code&gt;. You will not have seen this previously as the &lt;code&gt;data&lt;/code&gt; property was typed as &lt;code&gt;any&lt;/code&gt; which silently includes &lt;code&gt;undefined&lt;/code&gt;. By specifying the type explicitly, AG Grid has the opportunity to warn you, via typings, that the data property could be &lt;code&gt;undefined&lt;/code&gt; under some circumstances.&lt;/p&gt;

&lt;p&gt;To demonstrate this we will look at a &lt;a href="https://ag-grid.com/angular-data-grid/component-cell-renderer/#data-in-cell-renderers" rel="noopener noreferrer"&gt;common pitfall&lt;/a&gt; that users have when adding custom cell renderers while supporting row grouping. &lt;/p&gt;

&lt;h3&gt;
  
  
  Common Pitfall with Cell Renderers
&lt;/h3&gt;

&lt;p&gt;When writing a custom cell renderer you are likely to  access the &lt;code&gt;data&lt;/code&gt; property from the &lt;code&gt;ICellRendererParams&lt;/code&gt; interface. Your first attempt may have code that accesses data properties with no undefined checks.&lt;/p&gt;

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

&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gold&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;silver&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bronze&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This may work in your application but it is not technically correct or future-proof.&lt;/p&gt;

&lt;p&gt;If you have row grouping enabled then as soon as the user groups by a column your cell renderer will fail. This is because when grouping the &lt;code&gt;data&lt;/code&gt; property is &lt;code&gt;undefined&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;However, if you provide a generic type to &lt;code&gt;ICellRendererParams&lt;/code&gt;, such as &lt;code&gt;IOlympicData&lt;/code&gt;, your code will warn that the &lt;code&gt;data&lt;/code&gt; property can be &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lmvk3ekt7nabfrfvalj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lmvk3ekt7nabfrfvalj.png" alt="params.data can be undefined"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the generic type supplied you are more likely to correctly write the cell renderer to handle this case.&lt;/p&gt;

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

&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICellRendererParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IOlympicData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gold&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;silver&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bronze&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;While it is possible to silence the "Object is possibly 'undefined'" errors with a non-null operator &lt;code&gt;!.&lt;/code&gt; such as &lt;code&gt;params.data!.gold&lt;/code&gt;, we would recommend writing your code defensively in line with the AG Grid interfaces. &lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;If you are using Typescript then we would encourage you to take advantage of our generic types to give you: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compile-time errors&lt;/li&gt;
&lt;li&gt;Auto-completion in your IDE&lt;/li&gt;
&lt;li&gt;Improved code typings &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a small change in your type definitions that results in a greatly improved developer experience!&lt;/p&gt;

&lt;p&gt;For more details visit our &lt;a href="https://ag-grid.com/javascript-data-grid/typescript-generics/" rel="noopener noreferrer"&gt;Typescript Generics&lt;/a&gt; documentation.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Upcoming changes to AG Grid Angular in v28</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Thu, 09 Jun 2022 09:48:17 +0000</pubDate>
      <link>https://dev.to/ag-grid/upcoming-changes-to-ag-grid-angular-in-v28-e3o</link>
      <guid>https://dev.to/ag-grid/upcoming-changes-to-ag-grid-angular-in-v28-e3o</guid>
      <description>&lt;p&gt;In this post, we want to give you notice of the changes that are coming to our Angular wrappers in v28 and actions that you can take now to make the upgrade as smooth as possible.&lt;/p&gt;

&lt;p&gt;In v28 of AG Grid, we are enabling the Ivy distribution format which brings numerous benefits. However, this means the minimum Angular dependency will increase to Angular v12. &lt;/p&gt;

&lt;p&gt;To maintain support for older versions of Angular, v8-11, we are going to release new legacy versions of our Angular packages. So please read on to understand what to expect when upgrading to AG Grid v28.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are we doing this?
&lt;/h2&gt;

&lt;p&gt;Since Angular v12 the &lt;a href="https://blog.angular.io/upcoming-improvements-to-angular-library-distribution-76c02f782aa4"&gt;recommend&lt;/a&gt; distribution format for Angular libraries was changed to Ivy. There are many advantages to this approach, namely no longer requiring &lt;code&gt;ngcc&lt;/code&gt; to run and mutate your node_modules. &lt;/p&gt;

&lt;p&gt;As Angular v12 is over a year old and there are other features that moving our library to Ivy unblocks we have decided to make this change now. This brings us in line with the rest of the Angular ecosystem in moving towards Ivy everywhere.&lt;/p&gt;

&lt;p&gt;We have decided to update our existing Angular libraries, &lt;code&gt;ag-grid-angular&lt;/code&gt; and &lt;code&gt;@ag-grid-community/angular&lt;/code&gt;, (see &lt;a href="https://ag-grid.com/javascript-data-grid/packages-modules/"&gt;Package vs Modules&lt;/a&gt; for why we have two Angular libraries) to use Ivy as opposed to creating new packages for the Ivy format. We have decided to minimise the change for users who are up to date with Angular and avoid future breaks when View Engine is no longer supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to expect when migrating to AG Grid v28
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Apps using Angular v12+
&lt;/h3&gt;

&lt;p&gt;First off, if your application is built with Angular v12 or above, then you will update as normal. Simply move &lt;code&gt;ag-grid-angular&lt;/code&gt; to v28 and notice that &lt;code&gt;ngcc&lt;/code&gt; will no longer run against our package. You may see a slight improvement in CI build time as a result of this. &lt;/p&gt;

&lt;p&gt;The following warning will disappear from your build log!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Generating browser application bundles &lt;span class="o"&gt;(&lt;/span&gt;phase: setup&lt;span class="o"&gt;)&lt;/span&gt;...
Processing legacy &lt;span class="s2"&gt;"View Engine"&lt;/span&gt; libraries:
- ag-grid-angular &lt;span class="o"&gt;[&lt;/span&gt;es2015/esm2015]
Encourage the library authors to publish an Ivy distribution.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deprecation of &lt;code&gt;AgGridModule.withComponents()&lt;/code&gt;.
&lt;/h3&gt;

&lt;p&gt;With our Angular lib now requiring its consuming applications to run with Ivy, we have deprecated and removed the &lt;code&gt;.withComponent()&lt;/code&gt; method from the &lt;code&gt;AgGridModule&lt;/code&gt;. This has not been required for any applications that were running with Ivy but as we supported Angular v8 we could not remove it. &lt;/p&gt;

&lt;p&gt;Simply delete the &lt;code&gt;.withComponents([MyComponent])&lt;/code&gt; if it is present in your code.&lt;/p&gt;

&lt;p&gt;That is all you need to do concerning this Angular upgrade as part of AG Grid v28. Do still check the changelog for grid-related breaking changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apps using Angular v8-11
&lt;/h3&gt;

&lt;p&gt;If your application is on an old version of Angular there is going to be more work for you to upgrade to AG Grid v28 to get our latest features. If possible take this opportunity to upgrade your Angular version to at least v12. However, we understand this is not always going to be viable. As a result, we will produce legacy versions of our Angular libraries that will still work in your app.&lt;/p&gt;

&lt;p&gt;These will be &lt;code&gt;ag-grid-angular-legacy&lt;/code&gt; and &lt;code&gt;@ag-grid-community/angular-legacy&lt;/code&gt; respectively for Package / Module setups.&lt;/p&gt;

&lt;p&gt;So in your &lt;code&gt;package.json&lt;/code&gt; file, you will need to change the AG Grid package to be the legacy version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;"dependencies":&lt;/span&gt; {
    ...
&lt;span class="gd"&gt;-    "ag-grid-angular": "^27.3.0",
&lt;/span&gt;&lt;span class="gi"&gt;+    "ag-grid-angular-legacy": "^28.0.0",
&lt;/span&gt;    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will then need to update all the import paths throughout your application to use the legacy package name too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import { AgGridModule } from 'ag-grid-angular';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { AgGridModule } from 'ag-grid-angular-legacy';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference between the standard and legacy packages is the Angular distribution format, so aside from standard major version breaking changes, the legacy package should act as a drop-in replacement.&lt;/p&gt;

&lt;h2&gt;
  
  
  AG Charts v6
&lt;/h2&gt;

&lt;p&gt;The same change will also be applied to our standalone Angular charting library. So from v6 of &lt;code&gt;ag-charts-angular&lt;/code&gt; you will need to have a minimum Angular dependency of v12. &lt;/p&gt;

&lt;p&gt;If you are on Angular v8-11 then you will need to switch to the &lt;code&gt;ag-charts-angular-legacy&lt;/code&gt; package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;"dependencies":&lt;/span&gt; {
    ...
&lt;span class="gd"&gt;-    "ag-charts-angular": "^5.3.0",
&lt;/span&gt;&lt;span class="gi"&gt;+    "ag-charts-angular-legacy": "^6.0.0",
&lt;/span&gt;    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then also update your chart import paths too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import { AgChartsAngularModule } from 'ag-grid-angular';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { AgChartsAngularModule } from 'ag-grid-angular-legacy';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;As Angular continues to move forward we want our users to be able to take advantage of the new features and so we will continue to move forward with Angular upgrades. This means that in future releases please expect that we will continue to drop support for older versions of Angular until we reach v12 and will drop the legacy packages entirely.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>upgrade</category>
    </item>
    <item>
      <title>VS Code: It's taking a while to configure your breakpoints"</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Thu, 12 May 2022 14:12:59 +0000</pubDate>
      <link>https://dev.to/scooperdev/vs-code-how-to-solve-its-taking-a-while-to-configure-your-breakpoints-5ej7</link>
      <guid>https://dev.to/scooperdev/vs-code-how-to-solve-its-taking-a-while-to-configure-your-breakpoints-5ej7</guid>
      <description>&lt;p&gt;I love using VS Code to run and debug local scripts. However, I had noticed that starting up the task in debug mode sometimes took a really long time. &lt;/p&gt;

&lt;p&gt;I also got the warning pictured here with the text, "&lt;em&gt;It's taking a while to configure your breakpoints. You can speed this up by updating the 'outFiles' in your launch.json.&lt;/em&gt;"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BPBDZ3Rx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mja7wq6rj9o878jxurg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BPBDZ3Rx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mja7wq6rj9o878jxurg1.png" alt="VS Code Warning" width="389" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is likely because, when working in a mono repo, there are a lot of files that the task runner tries to load into the debugger so that it can configure your breakpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Javascript Task Configuration
&lt;/h2&gt;

&lt;p&gt;This was the task configuration from my &lt;code&gt;launch.json&lt;/code&gt; file. This task just ran a Javascript file, meaning for debugging, I did not require any source maps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; {
    "type": "node",
    "request": "launch",
    "name": "Generate Cypress Config",
    "runtimeExecutable": "npm",
    "cwd": "${workspaceRoot}/grid-packages/ag-grid-docs/documentation",
    "runtimeArgs": [
        "run-script",
        "generate-cypress-config"
    ],
    "port": 9229,
    "skipFiles": [
        "&amp;lt;node_internals&amp;gt;/**"
    ],           
    "resolveSourceMapLocations": [
        "${workspaceFolder}/**",
        "!**/node_modules/**"
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simply setting &lt;code&gt;sourceMaps&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; caused the task to startup pretty much immediately saving me a lot of time!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"sourceMaps": false, 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Typescript Task
&lt;/h2&gt;

&lt;p&gt;However, if you are running a Typescript task you will need source maps loaded if you want to debug it. This is when we need to follow the instructions of the warning and set the &lt;code&gt;outfiles&lt;/code&gt; config property. &lt;/p&gt;

&lt;p&gt;I set this to the folder containing all the files that would be included in the given task run. Even if this still included some extra files, restricting to a small subset and ignoring node_modules gave a huge boost in startup speed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"outFiles": [
    "${workspaceFolder}/grid-packages/ag-grid-docs/**/*.js",
    "!**/node_modules/**"
],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should match the &lt;code&gt;.js&lt;/code&gt; files and not the sourcemap files directly. &lt;/p&gt;

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

&lt;p&gt;Don't ignore warnings when they are giving you a hint that could improve your productivity!&lt;/p&gt;

&lt;p&gt;You can read how VS Code does &lt;a href="https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_source-map-discovery"&gt;source map discovery&lt;/a&gt; from their documentation for a more complete picture.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Understanding async tests in Angular</title>
      <dc:creator>Stephen Cooper</dc:creator>
      <pubDate>Mon, 25 Apr 2022 15:58:19 +0000</pubDate>
      <link>https://dev.to/angular/understanding-async-tests-in-angular-f8n</link>
      <guid>https://dev.to/angular/understanding-async-tests-in-angular-f8n</guid>
      <description>&lt;p&gt;If you are testing an Angular application, then at some point, you will be required to test asynchronous behaviour. In this article, we will demonstrate how to write an asynchronous test with both &lt;code&gt;fakeAsync&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;. We will explain each step in detail to give you the understanding and confidence to write your own asynchronous tests.&lt;/p&gt;

&lt;p&gt;Full application code along with tests is available at &lt;a href="https://github.com/StephenCooper/async-angular-testing/tree/main/async-testing-app" rel="noopener noreferrer"&gt;StephenCooper/async-angular-testing&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Application for our Test
&lt;/h2&gt;

&lt;p&gt;We will be testing an application that uses &lt;a href="https://ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt;. Our application displays a table of Olympic medal winners and also provides users with a text box to filter the medal winners by any field. You can try the application out for yourself &lt;a href="https://plnkr.co/edit/ef2ozOyGVZlvT2wq?open=app%2Fapp.component.ts" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ven45ih1e21zpnzt21v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ven45ih1e21zpnzt21v.gif" alt="Filter data by text input" width="600" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are going to test that we can filter our data to a specific country of interest. Our test will validate that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Our grid shows the full set of 1000 rows and our application displays the row count of 1000.&lt;/li&gt;
&lt;li&gt;Upon entering the text "Germany" the grid should filter the rows to only show German athletes&lt;/li&gt;
&lt;li&gt;Our application row count should update to 68 (the number of German athletes).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reason for choosing this application is that it contains asynchronous code making it virtually impossible to test synchronously. &lt;/p&gt;

&lt;h2&gt;
  
  
  Application Code
&lt;/h2&gt;

&lt;p&gt;In our application we have a text input box that is bound to the &lt;code&gt;quickFilterText&lt;/code&gt; property of our component. We display the current number of rows in our template and we pass the &lt;code&gt;quickFilterText&lt;/code&gt; to our grid component so that it can filter its rows as required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"quickFilter"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;[(ngModel)]=&lt;/span&gt;&lt;span class="s"&gt;"quickFilterText"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"numberOfRows"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Number of rows: {{ displayedRows }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ag-grid-angular&lt;/span&gt; &lt;span class="na"&gt;#grid&lt;/span&gt;
  &lt;span class="na"&gt;[quickFilterText]=&lt;/span&gt;&lt;span class="s"&gt;"quickFilterText"&lt;/span&gt;
  &lt;span class="na"&gt;(modelUpdated)=&lt;/span&gt;&lt;span class="s"&gt;"onModelUpdated($event)"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ag-grid-angular&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number of rows will be kept up to date by using the grid callback &lt;code&gt;(modelUpdated)&lt;/code&gt;. This is fired every time the grid model is updated, including when filtering is performed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;quickFilterText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ViewChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AgGridAngular&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;onModelUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ModelUpdatedEvent&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;displayedRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDisplayedRowCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test helpers
&lt;/h3&gt;

&lt;p&gt;Before we get to the tests let me quickly explain the assertion helper function we will be using. This function will give us an insight into the inner workings of our test, especially when we start working with asynchronous callbacks. &lt;/p&gt;

&lt;p&gt;The function validates the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;internal grid state&lt;/li&gt;
&lt;li&gt;state of the component variable, i.e &lt;code&gt;displayedRows&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;rendered HTML output of the &lt;code&gt;{{ displayedRows }}&lt;/code&gt; binding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will see that these values do &lt;em&gt;not&lt;/em&gt; update in sync due to asynchronous callbacks and if change detection is required to have run to update the property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;templateRows&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate the internal grid model by calling its api method to get the row count&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDisplayedRowCount&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api.getDisplayedRowCount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate the component property displayedRows&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;component.displayedRows&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate the rendered html content that the user would see &lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rowNumberDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;div&amp;gt; {{displayedRows}} &amp;lt;/div&amp;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="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Number of rows: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.withContext()&lt;/code&gt; is a helpful Jasmine method to give us clearer error messages when values are not equal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Test Module
&lt;/h2&gt;

&lt;p&gt;The first part of the test is to configure the test module. It requires AG Grid's &lt;code&gt;AgGridModule&lt;/code&gt; and also Angular's &lt;code&gt;FormModule&lt;/code&gt; to provide support for &lt;code&gt;ngModel&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DebugElement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fakeAsync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FormsModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/forms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;By&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AgGridModule&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;ag-grid-angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;beforeEach&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="nf"&gt;configureTestingModule&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;AppComponent&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;AgGridModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormsModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Create the test component fixture&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentInstance&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;compDebugElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debugElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Get a reference to the quickFilter input and rendered template&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compDebugElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#quickFilter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;rowNumberDE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compDebugElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#numberOfRows&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;An important thing to note here is what is missing from &lt;code&gt;beforeEach&lt;/code&gt;. We have purposefully not included &lt;code&gt;fixture.detectChanges()&lt;/code&gt; as part of our setup logic. By doing this we ensure that all our tests are as isolated and it enables us to make assertions on our component before it is initialised. Finally, and most importantly, when working with &lt;code&gt;fakeAsync&lt;/code&gt; we do not want our component to be created outside of our test's &lt;code&gt;fakeAsync&lt;/code&gt; context. If we do this, we can end up with all sorts of test inconsistencies and bugs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that we do not run fixture.detectChanges() inside the beforeEach method! This can lead to numerous issues when testing asynchronous code!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Broken Synchronous Test
&lt;/h2&gt;

&lt;p&gt;To prove that we need to handle this test asynchronously, let's first try to write the test synchronously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should filter rows by quickfilter (sync version)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// When the test starts our test harness component has been created but not our child grid component&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeUndefined&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Our first call to detectChanges, causes the grid to be created&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Grid has now been created&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Run change detection to update template&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While it looks like this test should pass it does not. We would expect that by the point we call &lt;code&gt;validateState&lt;/code&gt; each assertion would correctly show 1000 rows. However, only the internal grid model has 1000 rows and both the component property and rendered output display 0. This results in the following test errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expected&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;equal&lt;/span&gt; &lt;span class="mf"&gt;1000.&lt;/span&gt;
&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expected&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Number of rows: 0 for&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="mf"&gt;1000.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens because the grid setup code runs synchronously and so has completed before our assertion. However, the component property is still 0 because the grid callback is asynchronously and is still in the Javascript event queue when we reach the assertion statement, i.e it has not run yet.&lt;/p&gt;

&lt;p&gt;If you are not familiar with the Javascript event queue and how asynchronous tasks are run then you may find it beneficial to read these articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif"&gt;JavaScript Visualized: Event Loop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/" rel="noopener noreferrer"&gt;Tasks, microtasks, queues and schedules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we cannot even validate the starting state of our test synchronously it is clear that we are going to need to update our tests to correctly handle asynchronous callbacks. &lt;/p&gt;

&lt;h1&gt;
  
  
  Writing an Async Test
&lt;/h1&gt;

&lt;p&gt;We are going to cover two approaches for writing our test that handles the asynchronous grid behaviour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;fakeAsync&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;async&lt;/code&gt; &lt;code&gt;await&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FakeAsync
&lt;/h2&gt;

&lt;p&gt;As asynchronous code is very common, Angular provides us with the &lt;a href="https://angular.io/api/core/testing/fakeAsync" rel="noopener noreferrer"&gt;fakeAsync&lt;/a&gt; test utility. It enables us to control the flow of time and when asynchronous tasks are executed with the methods &lt;code&gt;tick()&lt;/code&gt; and &lt;code&gt;flush()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The high-level concept with &lt;code&gt;fakeAsync&lt;/code&gt; is that when the test comes to execute an asynchronous task, it is added into a time-based queue instead of being executed. As a developer, we can then choose when the tasks are run. If we want to run all the currently queued async tasks we call &lt;code&gt;flush()&lt;/code&gt;. As the name suggests this flushes all the queued tasks executing them as they are removed from the queue. &lt;/p&gt;

&lt;p&gt;If we have code that uses a timeout, for example, &lt;code&gt;setTimeout(() =&amp;gt; {}, 500)&lt;/code&gt;, then this will be added to the fake async queue with a time delay of 500. We can use the &lt;code&gt;tick&lt;/code&gt; function to advance time by a set amount. This will walk through the queue and execute tasks that are scheduled before this time delay. Tick gives us more control over how many tasks are removed from the queue as compared to flush.&lt;/p&gt;

&lt;p&gt;It is worth noting that there is also a &lt;code&gt;flushMicrotasks()&lt;/code&gt; function. For an example of when you might use &lt;code&gt;flushMicrotasks&lt;/code&gt; instead of &lt;code&gt;flush&lt;/code&gt; take a look at this article &lt;a href="https://www.damirscorner.com/blog/posts/20210702-AngularTestingFlushVsFlushMicrotasks.html" rel="noopener noreferrer"&gt;Angular Testing Flush vs FlushMiscrotasks&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Controlling Change Detection in our test
&lt;/h3&gt;

&lt;p&gt;You will see the following line of code &lt;code&gt;fixture.detectChanges()&lt;/code&gt; in a lot of Angular tests. This enables you to control when change detection is run. As part of change detection, Input bindings receive their updated values and Html templates are re-rendered with updated component values. Each of these is important when you want to validate that code is working correctly. In the test code below, we will highlight why we are required to call &lt;code&gt;fixture.detectChanges()&lt;/code&gt; at multiple stages. &lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Filter Test with FakeAsync
&lt;/h2&gt;

&lt;p&gt;We will now walk through the full &lt;code&gt;fakeAsync&lt;/code&gt; test to validate that our application correctly filters data and updates the number of displayed rows. &lt;/p&gt;

&lt;h3&gt;
  
  
  Test setup
&lt;/h3&gt;

&lt;p&gt;The first thing to do is wrap our test body in &lt;code&gt;fakeAsync&lt;/code&gt;. This causes all async functions to be patched so that we can control their execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fakeAsync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flush&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should filter rows by quickFilterText&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;fakeAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the start of our test, our application component has been created but it has not been initialised. i.e &lt;code&gt;ngOnInit&lt;/code&gt; has not run. This means that our &lt;code&gt;&amp;lt;ag-grid-angular&amp;gt;&lt;/code&gt; component has not been created yet. To validate this, we can test that the grid is undefined.&lt;/p&gt;

&lt;p&gt;The first call to &lt;code&gt;fixture.detectChanges()&lt;/code&gt;, will create the grid and pass the component values to the grid via its @Inputs. When working with &lt;code&gt;fakeAsync&lt;/code&gt; ensure the first call to &lt;code&gt;fixture.detectChanges()&lt;/code&gt; is within the test body and &lt;strong&gt;NOT&lt;/strong&gt; in a &lt;code&gt;beforeEach&lt;/code&gt; section. This is vital as it means that during the construction of the grid all async function calls are correctly patched.&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;// At the start of the test the grid is undefined&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeUndefined&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Initialise our app component which creates our grid&lt;/span&gt;
&lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Validate that the grid has now been created&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we validate that the internal grid model is correct. It should have 1000 rows. At this point, the asynchronous grid callbacks have not run. i.e the (modelUpdated) @Output has not fired. This is why the internal grid state has 1000 rows, but the component and template still have 0 values.&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;// Validate the synchronous grid setup code has been completed but not any async updates&lt;/span&gt;
&lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the callbacks, that are currently in the fake task queue, we call &lt;code&gt;flush()&lt;/code&gt;. This executes all the async tasks that were added during the initialisation of the grid and also any others that are created during the flush itself until the task queue is empty. Async tasks may create new async tasks as they are executed. Be default &lt;code&gt;flush()&lt;/code&gt; will attempt to drain the queue of these newly added calls up to a default limit of 20 turns. If for some reason your async tasks trigger other async tasks more than 20 times you can increase this limit by passing it to flush. i.e &lt;code&gt;flush(100)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Flush all async tasks from the queue&lt;/span&gt;
&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the component has its &lt;code&gt;displayedRows&lt;/code&gt; property updated by the &lt;code&gt;(modelUpdated)&lt;/code&gt; event handler. However, this is not reflected in the template as change detection has not yet run. For the rendered template to reflect the updated component property we need to trigger change detection.&lt;/p&gt;

&lt;p&gt;Our test state is now consistent. The internal grid model, component data and renderer template all correctly show 1000 rows before any filtering is applied.&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;// Validate that our component property has now been updated by the onModelUpdated callback&lt;/span&gt;
&lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&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;// Force the template to be updated&lt;/span&gt;
&lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// Component state is stable and consistent&lt;/span&gt;
&lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update Filter Text
&lt;/h3&gt;

&lt;p&gt;Now it's time to enter text into the filter. We set the filter value to 'Germany' and fire the input event which is required for &lt;code&gt;ngModel&lt;/code&gt; to react to the filter change.&lt;/p&gt;

&lt;p&gt;At this point, the text input has been updated but the grid input binding, [quickFilterText]="quickFilterText", has not been updated as that requires change detection to run. This is why even the internal grid model still reports 1000 rows after the filter change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Mimic user entering Germany&lt;/span&gt;
&lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Germany&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Input [quickFilterText]="quickFilterText" has not been updated yet so grid is not filtered&lt;/span&gt;
&lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now run change detection which passes the text 'Germany' to the grid input [quickFilterText]="quickFilterText". We then validate that the internal number of rows has been reduced to 68 as the grid filters asynchronously. However, the &lt;code&gt;displayedRows&lt;/code&gt; property has not been updated as grid callbacks are asynchronous and sitting in the task queue.&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;// Run change detection to push new filter value into the grid component&lt;/span&gt;
&lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// Grid uses filter value to update its internal model&lt;/span&gt;
&lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now &lt;code&gt;flush&lt;/code&gt; our async task queue which causes the event handler &lt;code&gt;(modelUpdated)&lt;/code&gt; to fire and update our component's &lt;code&gt;displayedRows&lt;/code&gt; property. We then run change detection to update the template with the new value.&lt;/p&gt;

&lt;p&gt;Our component test state is once again stable and we can validate that our quick filter and model update logic is correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//flush all the asynchronous callbacks.&lt;/span&gt;
&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// Component property is updated as the callback has now run&lt;/span&gt;
&lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Run change detection to reflect the changes in our template&lt;/span&gt;
&lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Full Test Code
&lt;/h2&gt;

&lt;p&gt;Here is a more concise version of the test without all the intermediary validation steps. Hopefully it is now clear why we have this repeating pattern of &lt;code&gt;detectChanges&lt;/code&gt; -&amp;gt; &lt;code&gt;flush&lt;/code&gt; -&amp;gt; &lt;code&gt;detectChanges&lt;/code&gt;. In both cases, you can think of it as updating component inputs, running async tasks, and then updating the template with the resulting values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should filter rows by quickFilterText using fakeAsync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;fakeAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Setup grid, run async tasks, update HTML&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate full set of data is displayed&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// Update the filter text input&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Germany&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Push filter text to grid, run async tasks, update HTML&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate correct number of rows are shown for our filter text&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&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;
  
  
  Using Auto Detect Changes
&lt;/h3&gt;

&lt;p&gt;Now that we understand the data flow in the test above we can simplify the test by using &lt;a href="https://angular.io/guide/testing-utility-apis#componentfixture-methods" rel="noopener noreferrer"&gt;fixture.autoDetectChanges()&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When autodetect is true, the test fixture calls detectChanges immediately after creating the component. Then it listens for pertinent zone events and calls detectChanges accordingly.&lt;br&gt;
The default is false. Testers who prefer fine control over test behaviour tend to keep it false.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should filter rows by quickFilterText using fakeAsync auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;fakeAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Setup grid and start aut detecting changes, run async tasks and have HTML auto updated &lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoDetectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate full set of data is displayed&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// Update the filter text input, auto detect changes updates the grid input&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Germany&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Run async tasks, with auto detect then updating HTML&lt;/span&gt;
    &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate correct number of rows are shown for our filter text&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, writing the test with auto-detect hides a lot of complexity and so maybe a good starting point for your asynchronous tests. Just be aware you will lose precise control of when change detection is run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using async await
&lt;/h2&gt;

&lt;p&gt;Another way that we can test our application is to use the built-in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function" rel="noopener noreferrer"&gt;&lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; syntax&lt;/a&gt; along with the fixture method &lt;code&gt;fixture.whenStable()&lt;/code&gt;. This can at times be a simpler way to write async tests as you do not have to worry about manually running async tasks. &lt;/p&gt;

&lt;p&gt;It is worth noting that there are cases when it is impossible to write a test with &lt;code&gt;fakeAsync&lt;/code&gt;. If any of the executed code has a recursive setTimeout being used as a polling timeout, then the fakeAsync task queue can never empty during a flush. Each time a task is removed and executed it adds a new one to the queue indefinitely. This is why you may run into the following error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="n"&gt;reaching&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Does&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;polling&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run into this situation you may have more success with the &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; approach.&lt;/p&gt;

&lt;p&gt;Let's now re-write our test to work with &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should filter rows by quickFilterText (async version)&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;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="c1"&gt;// Grid is created&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeUndefined&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// At this point in the test we see that the async callback onModelUpdated has not run&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&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;// We wait for the fixture to be stable which allows all the asynchronous code to run.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;whenStable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Callbacks have now completed and our component property has been updated&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&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;// Run change detection to update the template&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// Now let's test that updating the filter text input does filter the grid data.&lt;/span&gt;
    &lt;span class="c1"&gt;// Set the filter to Germany&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Germany&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// We force change detection to run which applies the update to our &amp;lt;ag-grid-angular [quickFilterText] Input.&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Async tasks have not run yet&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// Again we wait for the asynchronous code to complete&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;whenStable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="c1"&gt;// Force template to update&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Final test state achieved.&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you may have noticed the structure of the test is very similar and we have just basically replaced &lt;code&gt;flush&lt;/code&gt; with &lt;code&gt;await fixture.whenStable&lt;/code&gt;. However, under the hood, these tests are running in very different ways so this will not be a straight swap in many other examples.&lt;/p&gt;

&lt;p&gt;Here is a concise version using &lt;code&gt;autoDetectChanges&lt;/code&gt; which is our shortest working test so far. It is also conceptually the most simple to understand and hides a lot of the complexity from the tester.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should filter rows by quickFilterText (async version)&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;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="c1"&gt;// Run initial change detection and start watching for changes&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoDetectChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// Wait for all the async task to complete before running validation&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;whenStable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// Set the filter to Germany&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Germany&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="nx"&gt;quickFilterDE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Wait for callbacks to run&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;whenStable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Changes automatically applied&lt;/span&gt;
    &lt;span class="nf"&gt;validateState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;gridRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;displayedRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;templateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Complete Test Application Code
&lt;/h2&gt;

&lt;p&gt;You can find the full application, complete with tests in the Github repo: &lt;a href="https://github.com/StephenCooper/async-angular-testing/tree/main/async-testing-app" rel="noopener noreferrer"&gt;StephenCooper/async-angular-testing&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We have taken a step by step walkthrough of an asynchronous Angular test. We explained how to write the test with both &lt;code&gt;fakeAsync&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt;, starting with first principles and then showing how to take advantage of &lt;code&gt;autoDetectChanges&lt;/code&gt;. I hope that you will have found this breakdown useful and it will enable you to confidently write tests for your applications' asynchronous behaviour. &lt;/p&gt;




&lt;p&gt;Stephen Cooper - Senior Developer at &lt;a href="https://ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>testing</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
