<?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: AG Grid</title>
    <description>The latest articles on DEV Community by AG Grid (@ag-grid).</description>
    <link>https://dev.to/ag-grid</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%2Forganization%2Fprofile_image%2F4283%2F85141aae-f55f-422f-85f7-4885e8c25e1d.jpeg</url>
      <title>DEV Community: AG Grid</title>
      <link>https://dev.to/ag-grid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ag-grid"/>
    <language>en</language>
    <item>
      <title>Optimising Large Data Set Visualisations with the M4 Algorithm</title>
      <dc:creator>James Swinton-Bland</dc:creator>
      <pubDate>Tue, 14 Jan 2025 15:28:31 +0000</pubDate>
      <link>https://dev.to/ag-grid/optimising-large-data-set-visualisations-with-the-m4-algorithm-1dj2</link>
      <guid>https://dev.to/ag-grid/optimising-large-data-set-visualisations-with-the-m4-algorithm-1dj2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fve8vlte32oqf5lyp59x1.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%2Fve8vlte32oqf5lyp59x1.png" alt="Optimising Large Data Set Visualisations with the M4 Algorithm" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Version 11 of AG Charts, our &lt;a href="https://www.ag-grid.com/charts/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;JavaScript Charting Library&lt;/a&gt;, includes significant performance enhancements to our Line Series. These improvements allow you to render over a million data points that can be zoomed, panned, and interacted with at over 60 fps, making these transitions appear seamless to users.&lt;/p&gt;

&lt;p&gt;To achieve this level of performance, we implemented the M4 algorithm: an aggregation-based time series dimensionality reduction technique that provides error-free visualizations at high data reduction rates. This blog post details our implementation of this algorithm and the performance improvements we've gained from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges in Rendering Large Datasets
&lt;/h2&gt;

&lt;p&gt;AG Charts leverages the HTML5 Canvas API for its drawing operations. We've discussed some of the techniques we apply to &lt;a href="https://blog.ag-grid.com/optimising-html5-canvas-rendering-best-practices-and-techniques/" rel="noopener noreferrer"&gt;optimize HTML5 Canvas rendering&lt;/a&gt; in a previous blog post, however, no matter what optimisations are applied, rendering a million data points in a chart is computationally intensive.&lt;/p&gt;

&lt;p&gt;If you're not already familiar with the Canvas API, drawing lines is akin to instructing a virtual pen: you issue one command to place the pen nib at specific coordinates on the screen, and another command to move the nib to a different set of coordinates, which then draws a line between the two points. This means that as the size of our data grows, so does the number of drawing operations required, which means more work for us to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the M4 Algorithm
&lt;/h2&gt;

&lt;p&gt;Given the way the Canvas API works, the only way to reduce the work needed to draw a chart is to physically draw less, which is where the M4 algorithm comes in. According to the &lt;a href="https://www.vldb.org/pvldb/vol7/p797-jugel.pdf?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;original paper&lt;/a&gt;, the M4 algorithm &lt;em&gt;"exploits the semantics of line rasterization to drive the data reduction of high-volume time series data."&lt;/em&gt; In other words, this approach allows us to reduce the number of drawing operations required to render a line chart, whilst retaining its original characteristics.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the M4 Algorithm Works
&lt;/h3&gt;

&lt;p&gt;This algorithm might sound complicated, but it's actually quite simple. Consider the following line chart, which contains 100 data points:&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%2Fyaal0ajobuf3dh51fcqi.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%2Fyaal0ajobuf3dh51fcqi.png" alt="Optimising Large Data Set Visualisations with the M4 Algorithm" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To apply the M4 algorithm, start by determining the range of your x-values. For instance, if your x-data ranges from 100 to 200, you might divide this range into 10 equal buckets: 100–110, 110–120, and so on. In this case, we've divided our example into 5 buckets:&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%2Fu9pypv9fb8hdj7wik0y0.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%2Fu9pypv9fb8hdj7wik0y0.png" alt="Optimising Large Data Set Visualisations with the M4 Algorithm" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, for each bucket, identify the data points that meet the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimum x-value&lt;/li&gt;
&lt;li&gt;Maximum x-value&lt;/li&gt;
&lt;li&gt;Minimum y-value&lt;/li&gt;
&lt;li&gt;Maximum y-value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because a data point may fit more than one criterion, each bucket will have up to four points. These have been highlighted in each bucket below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh7-qw.googleusercontent.com%2Fdocsz%2FAD_4nXeq2zsyaCEIq5KvGSOadW23aleUnxZnsT9LR_ionWsG_qwoTtY0_vZN0jmBjPnf8H6CiS19g1XJx8FPoBYeTIs5hYeMLXwDplgFQvXuveC1bnt68Ff_R9TICi-duBLbPOJsUFeWtOGGx_5RYhnk24IVkZnn%3Fkey%3DeCupEZVhUad2HTY3t60hjXhi" 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%2Flh7-qw.googleusercontent.com%2Fdocsz%2FAD_4nXeq2zsyaCEIq5KvGSOadW23aleUnxZnsT9LR_ionWsG_qwoTtY0_vZN0jmBjPnf8H6CiS19g1XJx8FPoBYeTIs5hYeMLXwDplgFQvXuveC1bnt68Ff_R9TICi-duBLbPOJsUFeWtOGGx_5RYhnk24IVkZnn%3Fkey%3DeCupEZVhUad2HTY3t60hjXhi" alt="Optimising Large Data Set Visualisations with the M4 Algorithm" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, connect the selected data points with lines to draw the simplified Line Series:&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%2Flh7-qw.googleusercontent.com%2Fdocsz%2FAD_4nXdYs2qwcu6I1QZAf56hv_qryib9CE5LjhbY9aSypSA1KHIejBPHoQxmPgXBMaYnnxPDr8Nk2uBpArFb3RrqZ2eZLeJSZ6zWeyCJfksp8ErxSk7Bt6rvKp4hVbkG-StVVMuGBFYG8kqDXGs_j2RsBi93XpgK%3Fkey%3DeCupEZVhUad2HTY3t60hjXhi" 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%2Flh7-qw.googleusercontent.com%2Fdocsz%2FAD_4nXdYs2qwcu6I1QZAf56hv_qryib9CE5LjhbY9aSypSA1KHIejBPHoQxmPgXBMaYnnxPDr8Nk2uBpArFb3RrqZ2eZLeJSZ6zWeyCJfksp8ErxSk7Bt6rvKp4hVbkG-StVVMuGBFYG8kqDXGs_j2RsBi93XpgK%3Fkey%3DeCupEZVhUad2HTY3t60hjXhi" alt="Optimising Large Data Set Visualisations with the M4 Algorithm" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it... by applying the M4 algorithm we've reduced the number of drawing operations required from 100 to 18.&lt;/p&gt;

&lt;p&gt;We purposefully made our buckets large in this example to illustrate how the algorithm works. To make this optimised chart appear almost indistinguishable from if the algorithm had never been applied, you’ll want your number of buckets set so that each bucket is around a pixel wide.&lt;/p&gt;

&lt;p&gt;For example, if you're rendering a chart that's 500 pixels wide and contains one million data points, you would divide the data into 500 buckets - one bucket per pixel. Each bucket would contain 2,000 data points (since 1,000,000 divided by 500 equals 2,000). In this example, applying the M4 algorithm reduces each bucket to a maximum of four points, meaning you'd end up with up to 2,000 points in total (500 buckets × 4 points), which is just 0.2% of the original data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing for Interactivity
&lt;/h2&gt;

&lt;p&gt;When implementing this algorithm the first problem we encountered was that iterating over a million points is slow. To achieve 60fps performance we need to render each frame in around 16ms, however, our most optimised implementation took around 100ms to complete, which made features like &lt;a href="https://www.ag-grid.com/charts/javascript/zoom/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;&lt;u&gt;Zoom&lt;/u&gt;&lt;/a&gt; feel sluggish.&lt;/p&gt;

&lt;p&gt;To fix this, we can pre-compute the M4 algorithm at different zoom levels, much like Google Maps does as you zoom out from a small street to a city. This works by starting with of snapshot of the highest detail you have available, and then gradually reducing the level of detail to generate additional snapshots until there is no value in reducing it further.&lt;/p&gt;

&lt;p&gt;For the M4 algorithm, we employ a clever technique to accelerate detail reduction. Instead of processing the original dataset at each level of detail, we utilize the results from a previous run by combining each adjacent pair of buckets, effectively halving the number of buckets.&lt;/p&gt;

&lt;p&gt;Below is an illustration of our example after merging the buckets:&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%2Flh7-qw.googleusercontent.com%2Fdocsz%2FAD_4nXe_XeppydE8Cdc1A-Qo-Scn6Hf55iEItRRI5JaTLV-fhBenbQUQMnbr9jcxq6040KPtXS8IXuffP-ANYMW-UBrv9jaeUCFt2SpvVYPBScoO91t5YKvmZM4cFOQSKVey7atxSViyQTT5XiLfUogOMsza4ZIJ%3Fkey%3DeCupEZVhUad2HTY3t60hjXhi" 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%2Flh7-qw.googleusercontent.com%2Fdocsz%2FAD_4nXe_XeppydE8Cdc1A-Qo-Scn6Hf55iEItRRI5JaTLV-fhBenbQUQMnbr9jcxq6040KPtXS8IXuffP-ANYMW-UBrv9jaeUCFt2SpvVYPBScoO91t5YKvmZM4cFOQSKVey7atxSViyQTT5XiLfUogOMsza4ZIJ%3Fkey%3DeCupEZVhUad2HTY3t60hjXhi" alt="Optimising Large Data Set Visualisations with the M4 Algorithm" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach allows us to reuse the already filtered data, rather than starting from scratch with the entire dataset. When dealing with a dataset of one million points, reducing the filtered data to just a few thousand points makes a huge difference. As an added bonus, each time this step is repeated, it will take less time than the previous run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;As you can see from the chart below, these optimizations have the biggest impact on large datasets:&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%2Ftinaa3lxenvdl4rnfy4r.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%2Ftinaa3lxenvdl4rnfy4r.png" alt="JavaScript Chart Perforance" width="676" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;Test the performance by yourself by selecting a large data set and zooming / scrolling in our &lt;a href="https://www.ag-grid.com/blog/charts/javascript/optimsing-javascript-charts-with-m4-algorithm/high-performance-line-chart-example/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;By implementing the M4 algorithm and optimizing data processing, AG Charts 11 delivers high-performance rendering of large Line Series datasets. These enhancements enable you to create interactive charts that remain fast &amp;amp; responsive, even with millions of data points.&lt;/p&gt;

&lt;p&gt;If you've made it this far, you may be interested in some of our other performance-related blogs, that discuss a range of other optimisations we've made to AG Charts to make it The Best &lt;a href="https://www.ag-grid.com/charts/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;JavaScript Charting Library&lt;/a&gt; In The World:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/optimising-html5-canvas-rendering-best-practices-and-techniques/" rel="noopener noreferrer"&gt;Optimising HTML5 Canvas Rendering: Best Practices and Techniques&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/building-ag-charts-efficient-javascript-charting-with-tree-based-scene-graphs/" rel="noopener noreferrer"&gt;Building AG Charts: Efficient JavaScript Charting with Tree-Based Scene Graphs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to add high-performance &lt;a href="https://www.ag-grid.com/charts/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;JavaScript Charts&lt;/a&gt; to your application? Get started today, for free:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ag-grid.com/charts/javascript/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;Get Started&lt;/a&gt;&lt;/p&gt;




</description>
      <category>javascript</category>
      <category>charts</category>
      <category>charting</category>
      <category>performance</category>
    </item>
    <item>
      <title>Building AG Charts: Efficient JavaScript Charting with Tree-Based Scene Graphs</title>
      <dc:creator>James Swinton-Bland</dc:creator>
      <pubDate>Thu, 26 Sep 2024 09:33:32 +0000</pubDate>
      <link>https://dev.to/ag-grid/building-ag-charts-efficient-javascript-charting-with-tree-based-scene-graphs-47ka</link>
      <guid>https://dev.to/ag-grid/building-ag-charts-efficient-javascript-charting-with-tree-based-scene-graphs-47ka</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzfo6zf11ge4bb3jmqi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnzfo6zf11ge4bb3jmqi6.png" alt="Building AG Charts: Efficient JavaScript Charting with Tree-Based Scene Graphs" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AG Charts is a high-performance, canvas-based &lt;a href="https://www.ag-grid.com/charts/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;JavaScript charting library&lt;/a&gt;, designed for creating complex and interactive JavaScript charts. Initially built to power &lt;a href="https://www.ag-grid.com/react-data-grid/integrated-charts/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;Integrated Charts&lt;/a&gt; in AG Grid (our &lt;a href="https://www.ag-grid.com/react-table?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;React Data Table&lt;/a&gt; library), it now stands alone with over 1 million npm downloads per month.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore how AG Charts leverages a tree-based scene graph abstraction to efficiently render complex &lt;a href="https://www.ag-grid.com/charts?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;JavaScript charts&lt;/a&gt;, offering a powerful solution for developers building data visualizations in JavaScript, &lt;a href="https://www.ag-grid.com/charts/react/quick-start/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;React&lt;/a&gt;, &lt;a href="https://www.ag-grid.com/charts/angular/quick-start/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; and &lt;a href="https://www.ag-grid.com/charts/vue/quick-start/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges in JavaScript Charting with HTML Canvas
&lt;/h2&gt;

&lt;p&gt;Rendering graphics on the web can be a double-edged sword. While the HTML Canvas API provides a solid platform for drawing complex shapes and images, it comes with performance considerations, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rendering Overhead&lt;/strong&gt; : Canvas operations involve manipulating the pixels of a bitmap, which means that drawing, compositing, and transformations require direct interaction with the GPU or software rendering pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser API Overhead&lt;/strong&gt; : Each canvas API call incurs a certain amount of overhead due to interaction with the browser's rendering engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repainting and Redrawing&lt;/strong&gt; : When you draw on a canvas, the entire pixel area being modified has to be recalculated and redrawn, especially for complex shapes or animations. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-threaded Execution&lt;/strong&gt; : JavaScript and canvas rendering both run on the main thread. Intensive canvas operations can block the main thread, leading to slower response times and reduced performance for other JavaScript tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of Hardware Acceleration in Some Cases&lt;/strong&gt; : While some canvas operations can be hardware accelerated, not all operations benefit from it. This inconsistency can lead to performance bottlenecks, especially with complex or frequent drawing operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given this, we needed a solution that would allow us to work with the canvas as efficiently as possible.&lt;/p&gt;

&lt;p&gt;_Enter the &lt;strong&gt;scene graph.&lt;/strong&gt; _&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is a Scene Graph?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A tree-based scene graph is a hierarchical data structure used in computer graphics to represent the spatial and logical relationships between objects in a scene. Each node in the tree corresponds to an object or entity in the scene, and the parent-child relationships between nodes define transformations such as translation, rotation, and scaling relative to their parents.&lt;/p&gt;

&lt;p&gt;In AG Charts, the scene graph is an abstraction layer over the HTML Canvas, providing a higher-level API for constructing and manipulating chart elements, such as markers, lines, circles and shapes. The scene graph also provides a layer where we can perform caching and optimise canvas rendering, negating some of the aforementioned performance considerations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Scene Graph is Essential for JavaScript Chart Libraries
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Higher-Order Abstraction for Rendering:&lt;/strong&gt; The scene graph in AG Charts was designed to simplify the low-level drawing operations required by the Canvas API. Instead of working directly with pixels, chart renderings could work with higher-order constructs like markers, lines, circles, and shapes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching and Optimization:&lt;/strong&gt; One key advantage of the scene graph is its ability to cache and optimize canvas rendering. Since canvas operations are typically slower than JavaScript operations, caching results and minimizing redraws are essential for maintaining performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support for Matrix Transformations:&lt;/strong&gt; Initially, we added support for matrix transformations on the base node class of all scene graph nodes, which includes rotation, scaling and translation operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coordinate Conversion Utilities:&lt;/strong&gt; We also implemented methods for converting points and boxes to and from canvas coordinates. These utilities were used liberally throughout our codebase to manage positioning and layout tasks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How the Scene Graph Was Implemented in AG Charts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Our scene graph consists of nodes representing different chart elements, with a base node class that includes properties for positioning, styling, and rendering. Each node type—whether a line, shape, or marker—inherits from this base class and implements its own rendering logic.&lt;/p&gt;

&lt;p&gt;The base node class also includes support for matrix transformations. Matrix transformations are mathematical operations used to manipulate the position, scale, and rotation of objects within a 2D space. These transformations are represented as matrices, which, when applied to points or objects, alter their geometric properties. The main types of transformations are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Translation&lt;/strong&gt; : Shifts an object from one position to another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling&lt;/strong&gt; : Changes the size of an object either uniformly or non-uniformly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotation&lt;/strong&gt; : Rotates an object around a specific point or axis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a scene graph, these transformations are applied hierarchically. Each node in the graph represents an object or a group of objects and holds its transformation matrix. The final transformation of a child node is the result of its own transformation combined with the transformations of all its parent nodes.&lt;/p&gt;

&lt;p&gt;In addition to matrix transformations, we also added methods to convert to/from canvas coordinates for points and boxes, which were liberally used around the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Continuous Improvement of AG Charts JavaScript Charts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Whilst our initial implementation of the scene graph provided many benefits, as AG Charts continues to evolve we've noticed areas for further improvement, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overhead from Matrix Transformations:&lt;/strong&gt; Although matrix transformations were supported by default, they were unnecessary for most rendering tasks. In practice, 95% of our scene graph nodes do not require these transformations, leading to unnecessary computational overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity in Coordinate Conversions:&lt;/strong&gt; The frequent use of coordinate conversion methods added complexity to the codebase, making it harder to maintain and optimize.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Bottlenecks:&lt;/strong&gt; As our library grew, we noticed that matrix transformations were consuming a non-trivial amount of execution time, even for nodes where these transformations were not needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Our Approach to Optimizing the Scene Graph&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To address these challenges, we implemented a series of changes aimed at simplifying the scene graph and reducing performance overhead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selective Application of Matrix Transformations:&lt;/strong&gt; We removed all matrix support from the existing scene graph nodes and added mixin classes. This allowed us to selectively re-add matrix transforms to the specific parts of the codebase where they were still needed. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplifying Coordinate Transformations:&lt;/strong&gt; We refactored the base scene-graph nodes by replacing methods that returned canvas coordinates with helper functions using local coordinates, reducing costly matrix operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improving Hit-Testing Performance:&lt;/strong&gt; We streamlined hit testing (determining whether a mouse or touch event intersects with a specific object drawn on the canvas) by converting to local coordinates upfront and applying transforms as we traverse the scene graph, avoiding the need for continuous coordinate conversions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring and Cleanup:&lt;/strong&gt; As a result of these simplifications and improvements, we were able to generally refactor and clean up redundant operations throughout our codebase, reducing our overall tech debt, and thereby improving maintainability as well as performance.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Key Pull Requests and Changes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;These optimizations were implemented across several pull requests. Given AG Charts is entirely open-source, you can dig into these PRs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ag-grid/ag-charts/pull/2234?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;PR #2234: Clean up the Scene Graph, pt.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ag-grid/ag-charts/pull/2242?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;PR #2242: Remove translation support from Node.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ag-grid/ag-charts/pull/2245?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;PR #2245: Fix bbox caching &amp;amp; simplify coordinate translations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ag-grid/ag-charts/pull/2269?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;PR #2269: Clean up the Scene Graph, pt.2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Results and Performance Gains&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;These changes have led to several positive outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Overhead from Unnecessary Matrix Transforms:&lt;/strong&gt; Matrix transformations are now only applied where needed, mostly in rendering leaf nodes of the scene graph, reducing unnecessary computational costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Hit-Testing and Rendering:&lt;/strong&gt; Hit-testing operations are now cheaper and faster, thanks to optimized coordinate handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Improvements in Benchmarks:&lt;/strong&gt; Internal benchmarking shows notable performance gains in large-scale use cases, demonstrating the effectiveness of these optimizations:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvtrkj7ivof9hxkqepfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvtrkj7ivof9hxkqepfa.png" alt="Overview of scene graph performance improvements" width="800" height="920"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;bbox caching introduced in b10.1 &amp;amp; Matrix transformations removed in latest (b10.2)&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;By refining our use of matrix transformations and optimizing the scene graph, we've significantly improved the performance of AG Charts. These changes are now part of the latest (10.2) release, and we're excited to see how they enhance your charting experience. Stay tuned for more updates as we continue to innovate and improve AG Charts!&lt;/p&gt;

&lt;p&gt;Want to build &lt;a href="https://www.ag-grid.com/charts?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;JavaScript charts&lt;/a&gt; with AG Charts? Get Started today by visiting our documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ag-grid.com/charts/javascript/quick-start/?ref=blog.ag-grid.com" rel="noopener noreferrer"&gt;Get Started with AG Charts JavaScript Charting Library&lt;/a&gt;&lt;/p&gt;

</description>
      <category>charts</category>
    </item>
    <item>
      <title>Introducing AG Charts Enterprise</title>
      <dc:creator>James Swinton-Bland</dc:creator>
      <pubDate>Tue, 05 Dec 2023 11:37:40 +0000</pubDate>
      <link>https://dev.to/ag-grid/introducing-ag-charts-enterprise-f87</link>
      <guid>https://dev.to/ag-grid/introducing-ag-charts-enterprise-f87</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wXrUy7Od--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/Introducing-AG-Charts-Enterprise-Banner.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wXrUy7Od--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/Introducing-AG-Charts-Enterprise-Banner.png" alt="Introducing AG Charts Enterprise" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://charts.ag-grid.com/?utm_source=AG+Grid+Blog&amp;amp;utm_medium=blog&amp;amp;utm_campaign=introducing_ag_charts"&gt;AG Charts&lt;/a&gt; is an open-source JavaScript charting and graphing library from the makers of AG Grid built for React, Angular and Vue. Last week saw the release of &lt;a href="https://blog.ag-grid.com/whats-new-in-ag-charts-9/"&gt;AG Charts 9&lt;/a&gt;, and, most notably, the introduction of AG Charts Enterprise.&lt;/p&gt;

&lt;p&gt;Read on to learn a bit more about the background of AG Charts, how we arrived where we are today, and what you can expect from our brand-new enterprise product 👇&lt;/p&gt;

&lt;h2&gt;
  
  
  History of AG Charts
&lt;/h2&gt;

&lt;p&gt;AG Charts was built by the AG Grid team in 2019 as a way to power &lt;a href="https://www.ag-grid.com/react-data-grid/integrated-charts/?ref=blog.ag-grid.com"&gt;integrated charts&lt;/a&gt; in AG Grid - a feature that lets users build charts directly from AG Grid. Sticking to our roots as an open-source company, we released AG Charts as a completely free and stand-alone open-source library. We knew that we had built a great library in its own right, so why not share it? Since then, AG Charts has steadily grown in popularity, now amassing over &lt;a href="https://www.npmjs.com/package/ag-charts-community?activeTab=readme&amp;amp;ref=blog.ag-grid.com"&gt;300,000 NPM downloads/month&lt;/a&gt; (as of 04/12/2023). With this rise in popularity, we decided it was time to invest further in AG Charts which has now culminated in the release of AG Charts 9 and the introduction of AG Charts Enterprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AG Charts?
&lt;/h2&gt;

&lt;p&gt;At this point, you may be thinking: "Why another charting library?" or "Why should I use AG Charts?". Our response would be that AG Charts comes with the same performance and reliability our users have come to expect of AG Grid. This reputation, and the subsequent trust we've earned with grid developers, has consistently separated us from our competition. We're confident we can earn that same reputation and trust from Charts developers, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise vs. Community
&lt;/h2&gt;

&lt;p&gt;The obvious next question is, what's the difference between the Enterprise and Community versions of AG Charts? Well, in a nutshell:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AG Charts Community is the original version of AG Charts that is designed to meet most use cases and is free forever.&lt;/li&gt;
&lt;li&gt;AG Charts Enterprise is an advanced version of AG Charts, with more series types, features, and dedicated Zendesk support, designed for enterprise use cases and customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a closer look at the series types &amp;amp; features available in AG Charts Enterprise:&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Series Types
&lt;/h2&gt;

&lt;p&gt;AG Charts Enterprise comes with &lt;strong&gt;13 additional series types -&lt;/strong&gt; &lt;a href="https://charts.ag-grid.com/react/box-plot-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Box Plot&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/bullet-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Bullet&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/heatmap-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Heatmap&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/range-area-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Range Area&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/range-bar-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Range Bar&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/radar-area-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Radar Area&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/radar-line-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Radar Line&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/radial-bar-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Radial Bar&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/radial-column-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Radial Column&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/nightingale-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Nightingale&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/sunburst-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Sunburst&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/treemap-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Treemap&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://charts.ag-grid.com/react/waterfall-series/?ref=blog.ag-grid.com"&gt;&lt;em&gt;Waterfall&lt;/em&gt;&lt;/a&gt;&lt;em&gt;:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wF9DNqjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/heatmap-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wF9DNqjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/heatmap-screenshot.png" alt="Introducing AG Charts Enterprise" width="730" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P6dzIw6w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/nightingale-screenshot-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P6dzIw6w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/nightingale-screenshot-2.png" alt="Introducing AG Charts Enterprise" width="375" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C6V8mkRH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/radar-area-screenshot-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C6V8mkRH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/radar-area-screenshot-2.png" alt="Introducing AG Charts Enterprise" width="427" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zMipxM9g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/radial-bar-screenshot-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zMipxM9g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/radial-bar-screenshot-2.png" alt="Introducing AG Charts Enterprise" width="338" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H30ZVrl_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/range-area-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H30ZVrl_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/range-area-screenshot.png" alt="Introducing AG Charts Enterprise" width="731" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hal7Iqx3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/range-bar-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hal7Iqx3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/range-bar-screenshot.png" alt="Introducing AG Charts Enterprise" width="731" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uB0IJHAw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/treemap-screenshot-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uB0IJHAw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/treemap-screenshot-1.png" alt="Introducing AG Charts Enterprise" width="668" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OQFE7H4w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/waterfall-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OQFE7H4w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/waterfall-screenshot.png" alt="Introducing AG Charts Enterprise" width="729" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CagmVcFt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/box-plot-screenshot-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CagmVcFt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/box-plot-screenshot-4.png" alt="Introducing AG Charts Enterprise" width="728" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://charts.ag-grid.com/gallery/?ref=blog.ag-grid.com"&gt;gallery&lt;/a&gt; for a full list of series types &amp;amp; examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Features
&lt;/h2&gt;

&lt;p&gt;In addition to enhanced series types, AG Charts Enterprise also comes with a range of new features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://charts.ag-grid.com/react/animation/?ref=blog.ag-grid.com"&gt;Animation&lt;/a&gt; of the graph or chart on load or update&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://charts.ag-grid.com/react/context-menu/?ref=blog.ag-grid.com"&gt;Context Menu&lt;/a&gt; for additional actions, such as zooming, downloading or whatever else your users may need&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://charts.ag-grid.com/react/axes-crosshairs/?ref=blog.ag-grid.com"&gt;Crosshairs&lt;/a&gt; for precise analysis of complex graphs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://charts.ag-grid.com/react/zoom/?ref=blog.ag-grid.com"&gt;Zoom&lt;/a&gt; into and out of data&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://charts.ag-grid.com/react/error-bars/?ref=blog.ag-grid.com"&gt;Error Bars&lt;/a&gt; for showing confidence intervals within data&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://charts.ag-grid.com/react/background-image/?ref=blog.ag-grid.com"&gt;Background Images&lt;/a&gt; to brand and style the chart&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the animation below that demonstrates some of these features:&lt;/p&gt;





&lt;p&gt;&lt;br&gt;
                            &lt;br&gt;
                                &lt;br&gt;
                            &lt;br&gt;
                        &lt;br&gt;
                            &lt;br&gt;
                                &lt;br&gt;
                                &lt;br&gt;
                            &lt;br&gt;
                        0:00&lt;br&gt;
 /0:12&lt;br&gt;
1×&lt;br&gt;
                            &lt;br&gt;
                                &lt;br&gt;
                            &lt;br&gt;
                        &lt;br&gt;
                            &lt;br&gt;
                                &lt;br&gt;
                            &lt;br&gt;
                        &lt;/p&gt;

&lt;h2&gt;
  
  
  Framework Support
&lt;/h2&gt;

&lt;p&gt;AG Charts Community and Enterprise have both been built specifically for React, Angular, and Vue meaning you can build React Charts as easily as you can build JavaScript charts. These aren't just wrappers, either; we've built the library specifically for these frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and Upgrading
&lt;/h2&gt;

&lt;p&gt;It's free to try out AG Charts Enterprise and you do not need to contact us at all; we just ask that you don't use AG Charts Enterprise in a project intended for production without a licence.&lt;/p&gt;

&lt;p&gt;When testing without a valid license key installed, your console log will will show a watermark for 5 seconds and display a series of warnings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FEX_Yt4E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/enterprise-console-warning.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FEX_Yt4E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2023/12/enterprise-console-warning.png" alt="Introducing AG Charts Enterprise" width="799" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you would like to remove this watermark or access support via Zendesk, you will need a trial license. Please email &lt;a href="//mailto:info@ag-grid.com"&gt;info@ag-grid.com&lt;/a&gt; to request a trial license key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;AG Charts Enterprise pricing at $399 / developer, but there is a limited-time launch price of $199 / developer. Check out the &lt;a href="https://charts.ag-grid.com/license-pricing/?ref=blog.ag-grid.com"&gt;pricing&lt;/a&gt; page for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AG Charts was built in 2019 by the AG Grid team to power Integrated Charting within AG Grid.&lt;/li&gt;
&lt;li&gt;Sticking to our roots, we released AG Charts as a free, standalone, open-source library.&lt;/li&gt;
&lt;li&gt;AG Charts proved to be popular with the community, so we decided to release an Enterprise version in December 2023 with new features, series types and dedicated support.&lt;/li&gt;
&lt;li&gt;AG Charts Enterprise requires a license and AG Charts Community will remain free forever&lt;/li&gt;
&lt;li&gt;You can trial AG Charts Enterprise for free - it just displays a watermark and a console warning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to try it out? Get started with AG Charts Enterprise now 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://charts.ag-grid.com/documentation?ref=blog.ag-grid.com"&gt;Get Started&lt;/a&gt;&lt;/p&gt;




</description>
      <category>aggrid</category>
      <category>agcharts</category>
      <category>releases</category>
    </item>
    <item>
      <title>Introducing the AG Grid Figma Design System</title>
      <dc:creator>James Swinton-Bland</dc:creator>
      <pubDate>Tue, 22 Aug 2023 09:01:29 +0000</pubDate>
      <link>https://dev.to/ag-grid/introducing-the-ag-grid-figma-design-system-3g7g</link>
      <guid>https://dev.to/ag-grid/introducing-the-ag-grid-figma-design-system-3g7g</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;If you're a designer working with AG grid it's time to get very excited! With AG Grid version 30.1.0 we've released our new Figma Design System.&lt;/p&gt;

&lt;p&gt;If you want to skip the preamble and dig into the design system yourself you can download it directly from our &lt;a href="https://ag-grid.com/javascript-data-grid/ag-grid-design-system/?ref=blog.ag-grid.com"&gt;design system documentation page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Everything you need to design with AG Grid
&lt;/h3&gt;

&lt;p&gt;The new design system implements our Alpine and Alpine Dark themes in Figma. All of the core AG Grid components are fully built out and ready to use, with even more components arriving in future releases. Each component has been carefully crafted to precisely mirror what's available in the AG Grid library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make it your own...
&lt;/h3&gt;

&lt;p&gt;And when you're ready to make AG Grid your own we've got you covered. The Alpine and Alpine Dark themes can be extended to create custom AG Grid themes that work with your brand. We even provide a sample style dictionary project so you can export your bespoke themes to json and quickly create custom AG Grid CSS themes.&lt;/p&gt;

&lt;p&gt;To learn more about the design system and thow it can help your design workflow watch this short video on Youtube.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Get designing now
&lt;/h3&gt;

&lt;p&gt;Once you're ready to get started with the Figma design system checkout our &lt;a href="https://ag-grid.com/javascript-data-grid/ag-grid-design-system/?ref=blog.ag-grid.com"&gt;design system documentation page&lt;/a&gt; to download the design system and start creating with AG Grid.&lt;/p&gt;

</description>
      <category>figma</category>
      <category>designsystem</category>
      <category>design</category>
      <category>grid</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>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>AG Grid Community Roundup July 2022</title>
      <dc:creator>Alan Richardson</dc:creator>
      <pubDate>Tue, 02 Aug 2022 13:11:48 +0000</pubDate>
      <link>https://dev.to/ag-grid/ag-grid-community-roundup-july-2022-4m9p</link>
      <guid>https://dev.to/ag-grid/ag-grid-community-roundup-july-2022-4m9p</guid>
      <description>&lt;p&gt;We melted our way through the heat of July to try and find the best of the content on the web related to AG Grid.&lt;/p&gt;

&lt;p&gt;We also released a &lt;a href="https://blog.ag-grid.com/whats-new-in-ag-grid-28/"&gt;new version of AG Grid&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Videos
&lt;/h2&gt;

&lt;p&gt;We found &lt;a href="https://www.youtube.com/watch?v=tO-A3LY6Auc&amp;amp;t=2030s"&gt;an interesting video from Valispace&lt;/a&gt;, I think it is one of their recruitment videos but it describes their evaluation and adoption process for AG Grid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How AG Grid chosen?

&lt;ul&gt;
&lt;li&gt;Aware of the libraries available&lt;/li&gt;
&lt;li&gt;Create a proof of concept using the library quickly&lt;/li&gt;
&lt;li&gt;Explore the main use-cases at a high level&lt;/li&gt;
&lt;li&gt;Evaluate the results&lt;/li&gt;
&lt;li&gt;Test with 100s of thousands of rows&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.adaptabletools.com/"&gt;AdapTable Tools&lt;/a&gt; have been creating quite a few videos recently. And this one shows a demo of their &lt;a href="https://www.youtube.com/watch?v=LM1MVLnefGU"&gt;save charts state feature&lt;/a&gt;. Adaptable is a great example of how extensible AG Grid is in the right hands and their AG Grid extension can really add a lot of user focussed features instantly when added to your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linkedin.com/in/philip-eschenbacher-57a817b5"&gt;Philip Eschenbacher&lt;/a&gt; has created another video in his Mongo DB and AG Grid series showing how to use &lt;a href="https://www.youtube.com/watch?v=YCHRW8d46m0"&gt;enhanced Graph QL Searches&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blogs
&lt;/h2&gt;

&lt;p&gt;Max Rosenberg wrote a &lt;a href="https://www.maxrosenb.com/blog/aggrid-reactquery-tutorial"&gt;blog post showing how to combine React Query with AG Grid&lt;/a&gt;. This has been on our internal todo list for quite some time so we are grateful to Max for creating such a good post we can point people towards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://brianflove.com/"&gt;Brian Love&lt;/a&gt; on his consultancy &lt;a href="https://liveloveapp.com/services/ag-grid"&gt;Live Live App&lt;/a&gt; posted a &lt;a href="https://liveloveapp.com/blog/2022-07-14-ag-grid-cell-rendering-pipeline"&gt;blog post about AG Grid Rendering pipeline&lt;/a&gt; this explains in detail the ordering and scope of rendering features in AG Grid: value getters then formatters, then renderers.&lt;/p&gt;

&lt;p&gt;We also released a few blog posts on the AG Grid blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/using-playwright-to-test-ag-grid-react-apps/"&gt;Using Playwright to Test AG Grid React Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/using-ag-grid-in-electron-applications/"&gt;Using AG Grid in Electron Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/using-react-bootstrap-and-ag-grid/"&gt;Using React-Bootstrap and AG Grid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/how-to-optimize-a-react-application-using-hooks-and-ag-grid/"&gt;How to Optimize a React Application Using Hooks and AG Grid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/typescript-generics/"&gt;Typescript Generics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ag-grid.com/why-use-a-third-party-data-grid-component/"&gt;Why Use a Third-Party Data Grid Component?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Apps
&lt;/h2&gt;

&lt;p&gt;We found quite a few apps using AG Grid that we were not aware of and added a &lt;a href="https://blog.ag-grid.com/showcase/#opensourceprojectsusingaggrid"&gt;Github section on our sample apps page&lt;/a&gt; which lists interesting projects that use AG Grid and release their code on Github.&lt;/p&gt;

&lt;p&gt;A few projects to take a look at that we became aware of recently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://lowdefy.com/"&gt;Lowdefy&lt;/a&gt; is a low code tool that uses AG Grid as a block component, allowing you to create apps which render data in AG Grid without a lot of coding knowledge. There is &lt;a href="https://example-reporting.lowdefy.com/report"&gt;a Lowdefy example using AG Grid here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://github.com/systelab/systelab-components"&gt;Systelab Component Library&lt;/a&gt; has common components for Angular. AG Grid is used as the grid component in their &lt;a href="https://systelab.github.io/components/"&gt;'tables' component showcase&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consta Design have created a &lt;a href="https://github.com/consta-design-system/ag-grid-adapter"&gt;React component that wraps AG Grid&lt;/a&gt; to provide some formatting and editing components. You can see it in action in their &lt;a href="https://ag-grid-adapter.consta.design/?path=/story/common-start--page"&gt;Storybook implementation&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many more on the &lt;a href="https://blog.ag-grid.com/showcase/#opensourceprojectsusingaggrid"&gt;Sample Apps Page&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Podcasts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/scooper-dev"&gt;Stephen Cooper&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/mana-peirov-184229138/"&gt;Mana Peirov&lt;/a&gt; from the AG Grid team appeared on the &lt;a href="https://twitter.com/angularpodcast/status/1552872210913566720"&gt;Adventures in Angular podcast&lt;/a&gt; discussing Testing and Charting.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://topenddevs.com/podcasts/adventures-in-angular/episodes/async-angular-testing-and-introducing-ag-charts-aia-352"&gt;listen to the podcast here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aggrid</category>
    </item>
    <item>
      <title>Using Playwright to Test AG Grid React Apps</title>
      <dc:creator>Alan Richardson</dc:creator>
      <pubDate>Fri, 22 Jul 2022 12:37:00 +0000</pubDate>
      <link>https://dev.to/ag-grid/using-playwright-to-test-ag-grid-react-apps-30h8</link>
      <guid>https://dev.to/ag-grid/using-playwright-to-test-ag-grid-react-apps-30h8</guid>
      <description>&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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fblog-playwright-react-components-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fblog-playwright-react-components-tiny.png" alt="Using Playwright to Test AG Grid React Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article contributed to the AG Grid blog by &lt;a href="https://blog.pavey.dev/" rel="noopener noreferrer"&gt;Cameron Pavey&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; is a testing tool created and maintained by Microsoft geared primarily toward end-to-end (E2E) tests. It boasts several features that make it a compelling choice for teams considering different E2E tools, including a cross-language API that lets you write your tests in various languages (JavaScript, TypeScript, Python, .NET, and Java) and cross-platform support for all major browsers.&lt;/p&gt;

&lt;p&gt;Similar to &lt;a href="https://www.cypress.io" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; and &lt;a href="https://www.selenium.dev" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt;, Playwright’s primary use case is E2E testing; however, recently, it has also gained experimental support for component testing. Component testing allows you to validate individual UI components’ functionality in isolation without invoking the whole application as you typically would in an E2E test.&lt;/p&gt;

&lt;p&gt;In this tutorial, you’ll learn more about the distinction between E2E testing and component testing before seeing how Playwright facilitates both kinds of tests. You’ll learn how to create a simple React application that utilizes &lt;a href="https://www.ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt; and how to validate the functionality of that application at an E2E and component testing level using Playwright.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are E2E Tests
&lt;/h2&gt;

&lt;p&gt;E2E tests are one of the three tiers of testing described by the &lt;a href="https://martinfowler.com/articles/practical-test-pyramid.html" rel="noopener noreferrer"&gt;testing pyramid&lt;/a&gt;. They are typically regarded as one of the slower and more expensive types of tests because they usually invoke the entire system and simulate user interactions with a fully composed system. This inevitably leads to longer-running tests (compared to unit tests and integration tests, which both reside closer to the base of the pyramid), with often fragile imperative instructions describing how the test runner should interact with the application. While this tutorial will primarily focus on Playwright, Cypress is also a very compelling option, and it recently released the &lt;a href="https://docs.cypress.io/guides/component-testing/writing-your-first-component-test" rel="noopener noreferrer"&gt;beta of their component testing solution&lt;/a&gt;, similar to Playwright.&lt;/p&gt;

&lt;p&gt;One of the big differences between Playwright and Cypress is the philosophies that they follow. Playwright endorses the use of the &lt;a href="https://playwright.dev/docs/test-pom" rel="noopener noreferrer"&gt;Page Object Model (POM)&lt;/a&gt; pattern. However, Cypress suggests that &lt;a href="https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/" rel="noopener noreferrer"&gt;code being reused through custom commands&lt;/a&gt; is an effective alternative, although the POM pattern is still achievable in Cypress if you prefer it. AG Grid has a blog post about using a &lt;a href="https://blog.ag-grid.com/cypress-plugin-for-ag-grid/" rel="noopener noreferrer"&gt;Cypress plug-in for testing AG Grid applications&lt;/a&gt; that eloquently demonstrates the virtues of Cypress’s custom command functionality. Either way, both tools are effective for E2E testing, but what about component testing?&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Testing Challenges
&lt;/h3&gt;

&lt;p&gt;Component testing can be tricky because, depending on your interpretation, there are various places it could fit in the testing pyramid. You can treat it like an E2E test, as it typically deals with the same UI components, but this approach has some drawbacks. Using the full E2E setup for component testing will be slower than potential alternatives because it still needs to invoke the entire application and its dependencies. This also poses a challenge if you are dealing with a component library with no distinct application to invoke. In this case, you need to make a simple application that mounts your components to test this. This increases the amount of setup and the amount of overhead involved in running these tests, and makes them generally less appealing when viewed from a cost/benefit perspective.&lt;/p&gt;

&lt;p&gt;Another approach is to treat them more like unit tests. You can achieve this by using tools like &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; as your test runner and &lt;a href="https://testing-library.com/docs/react-testing-library/intro/" rel="noopener noreferrer"&gt;Testing Library&lt;/a&gt;, which is a testing utility library that exposes some functions to help you test your components in isolation. This is typically a good approach in terms of effort, maintainability, and general developer experience. However, there are still some drawbacks, the most notable of which is that the tests don’t run in a real browser. Instead, the tests use &lt;a href="https://github.com/jsdom/jsdom" rel="noopener noreferrer"&gt;JSDom&lt;/a&gt; in most cases. This is where the component testing functionality of Playwright comes in.&lt;/p&gt;

&lt;p&gt;With Playwright, you can run your component tests in real browsers, using the same sort of tests that you would write for your E2E tests but without the drawbacks of using the full E2E setup, with additional overhead and unrelated application code being included.&lt;/p&gt;

&lt;h2&gt;
  
  
  E2E and Component Testing with Playwright
&lt;/h2&gt;

&lt;p&gt;Before getting started, there are a few things you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A recent version of &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; (which comes with npm). This tutorial uses v14.19.3.&lt;/li&gt;
&lt;li&gt;A code editor of your choice. &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; is a good choice if you don’t already have a code editor you prefer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you wish to see the completed code for the tutorial, you can find it in &lt;a href="https://github.com/cpave3/ag-grid-playwright" rel="noopener noreferrer"&gt;this public GitHub repo&lt;/a&gt;. Otherwise, if you’d prefer to build it yourself, or see how it’s done, keep following along.&lt;/p&gt;

&lt;p&gt;Once you have your prerequisites, the first thing you need to do is create a new project. You can use &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; for this tutorial, as it is fast, minimal, and easy to get started with, but other app starters, like &lt;a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer"&gt;create-react-app&lt;/a&gt; and &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, should also work. To create your new project, open a terminal and run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest ag-grid-playwright --template react
cd ag-grid-playwright
npm install

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

&lt;/div&gt;



&lt;p&gt;This will create a directory named &lt;code&gt;ag-grid-playwright/&lt;/code&gt; with a minimalist project inside it before navigating into the directory and installing all the current node dependencies. Next, you can install Playwright with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init playwright@latest

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

&lt;/div&gt;



&lt;p&gt;The installer will ask you questions like whether you want to use JavaScript or TypeScript, where you want to store the test files, and whether you want to create a GitHub Action. If you are following along, select &lt;strong&gt;JavaScript&lt;/strong&gt; when prompted and then accept the default answers for the other questions, as these will work for this tutorial.&lt;/p&gt;

&lt;p&gt;Once Playwright is installed, you can test that it works as expected. By default, it comes with an example test spec that runs twenty-five sample assertions in each of the three major browsers: Chrome, Firefox, and WebKit. To run this spec file, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx playwright test

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

&lt;/div&gt;



&lt;p&gt;If all is well so far, you should see an output 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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fplaywright-reports-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fplaywright-reports-tiny.png" alt="Using Playwright to Test AG Grid React Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To simplify the output later when you add your tests, you can delete the example located at &lt;code&gt;tests/example.spec.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that you have Playwright set up, you can install a couple more dependencies that you will need to build the actual application, AG Grid. To do this, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install ag-grid-react ag-grid-community

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating the Application
&lt;/h3&gt;

&lt;p&gt;With the dependencies installed, you need to create the components you will use in this application. So that there is a level of contrived complexity in the application, you will create two main components: the &lt;code&gt;DataGrid&lt;/code&gt; and the &lt;code&gt;CustomRenderer&lt;/code&gt; for one of the columns in your grid. You can do this by running the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir src/components
touch src/components/DataGrid.jsx
touch src/components/CustomRenderer.jsx

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

&lt;/div&gt;



&lt;p&gt;This will create the directory and files for the components you need. Next, open the &lt;code&gt;CustomRenderer.jsx&lt;/code&gt; file in your editor and paste in the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const CustomRenderer = (props) =&amp;gt; {
  return &amp;lt;span&amp;gt;{`$${props.value.toLocaleString()}`}&amp;lt;/span&amp;gt;;
};

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

&lt;/div&gt;



&lt;p&gt;This simple component will be responsible for rendering the integer values in your data, formatted as monetary values. After this, open the &lt;code&gt;DataGrid.jsx&lt;/code&gt; file and paste in the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from "react";
import { AgGridReact } from "ag-grid-react";

import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
import { CustomRenderer } from "./CustomRenderer";

export const DataGrid = () =&amp;gt; {
  const [rowData] = useState([
    { make: "Toyota", model: "Celica", price: 35000 },
    { make: "Ford", model: "Mondeo", price: 32000 },
    { make: "Porsche", model: "Boxster", price: 72000 },
  ]);

  const [columnDefs] = useState([
    { field: "make" },
    { field: "model" },
    { field: "price", cellRenderer: CustomRenderer },
  ]);

  return (
    &amp;lt;div className="ag-theme-alpine" style={{ height: 400, width: 600 }}&amp;gt;
    &amp;lt;AgGridReact rowData={rowData} columnDefs={columnDefs}&amp;gt;&amp;lt;/AgGridReact&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;This code is a slightly modified version of the &lt;a href="https://www.ag-grid.com/react-data-grid/getting-started/" rel="noopener noreferrer"&gt;example&lt;/a&gt; from the AG Grid documentation. The modification to this code is simply to use the &lt;code&gt;CustomRenderer&lt;/code&gt; component for the &lt;code&gt;price&lt;/code&gt; column, which will display the value as a formatted monetary value rather than just a number. You will need to change one more file to ensure the application renders correctly. Open &lt;code&gt;src/App.jsx&lt;/code&gt; and replace its content with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { DataGrid } from "./components/DataGrid";

function App() {
  return &amp;lt;DataGrid /&amp;gt;;
}

export default App;

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

&lt;/div&gt;



&lt;p&gt;Before proceeding to the E2E test, you should verify that the app is working as expected. To do this, from your terminal, run &lt;code&gt;npm run dev&lt;/code&gt;, which will tell Vite to start a dev server, allowing you to access your application, typically located at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; if the port is free. If it needs to run on a different port for whatever reason, the terminal output will tell you where it is running when you execute the command. When you visit that URL, you should see 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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fsample-application-for-playwright-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fsample-application-for-playwright-tiny.png" alt="Using Playwright to Test AG Grid React Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the application is very simple, it gives you a few things that you can test with both E2E and component tests and, as such, serves as an excellent example for this kind of tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  The E2E Test
&lt;/h3&gt;

&lt;p&gt;For your E2E test, you want to ensure that the application works as expected. In the case of this simple application, that essentially amounts to showing the correct data. To do this, make a new file located at &lt;code&gt;tests/app.spec.jsx&lt;/code&gt; and give it the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { test, expect } from "@playwright/test";

test("Check that all expected data is present", async ({ page }) =&amp;gt; {
  await page.goto("http://localhost:3000");

  // Verify that the title is correct
  await expect(page).toHaveTitle("Vite App");

  // Specify the data that we expect to be present
  const expectedData = [
    ["Toyota", "Celica", "$35,000"],
    ["Ford", "Mondeo", "$32,000"],
    ["Porsche", "Boxster", "$72,000"],
  ];

  // Verify that the data is correct
  for (let index = 0; index &amp;lt; expectedData.length; index++) {
    const row = await page.locator("role=row").nth(index + 1);
    const [make, model, price] = expectedData[index];
    await expect(row).toContainText([make, model, price]);
  }
});

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

&lt;/div&gt;



&lt;p&gt;This test will instruct Playwright to navigate to your application, hosted by Vite’s dev server. Check that the page title is correct and that the three expected rows of data are present and correct. You can add a script to your &lt;code&gt;package.json&lt;/code&gt; file to help run your E2E tests. Open your &lt;code&gt;package.json&lt;/code&gt; file and add the following line to your &lt;code&gt;scripts&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;…
"test": "playwright test"
…

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

&lt;/div&gt;



&lt;p&gt;Now, ensure that your Vite dev server is still running so that your application is available on port 3000, and then in a new terminal window (navigate back to the project directory if you need to), run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test

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

&lt;/div&gt;



&lt;p&gt;You should see an output 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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Frun-playwright-tests-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Frun-playwright-tests-tiny.png" alt="Using Playwright to Test AG Grid React Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though you only have one test at the moment, Playwright is configured to run three different projects, one with each of the three major browsers. You can see this configuration in &lt;code&gt;playwright.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This E2E test requires your application to be running and will load your entire application for each test. That isn’t a problem for a small application like this one. With larger, more complex real-world applications, however, it isn’t ideal to have that overhead if you aren’t testing the whole application.&lt;/p&gt;

&lt;p&gt;Next, you’ll see how you can use Playwright to create some simple component tests for your &lt;code&gt;CustomRenderer&lt;/code&gt; and &lt;code&gt;DataGrid&lt;/code&gt; components.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Component Tests
&lt;/h3&gt;

&lt;p&gt;Getting started with Playwright component testing is similar to the initial setup for Playwright E2E testing. However, you should note that at the time of writing this, component testing support in Playwright is still regarded as experimental, so it’s possible things will change in the future. If you find that these instructions do not work as expected, please refer to the &lt;a href="https://playwright.dev/docs/test-components" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; for further guidance.&lt;/p&gt;

&lt;p&gt;To get started, run the following command from the root of your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init playwright@latest --ct

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

&lt;/div&gt;



&lt;p&gt;This will prompt you with similar questions to the initial setup, but if you are following along, answer with “JavaScript” and “React” when prompted for your language and framework, respectively. When this command finishes, you should have a new file called &lt;code&gt;playwright-ct.config.js&lt;/code&gt;. Open this file and edit the &lt;code&gt;testDir&lt;/code&gt; property as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;…
testDir: ‘./src’,
…

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

&lt;/div&gt;



&lt;p&gt;This change is necessary because the default value of &lt;code&gt;'./'&lt;/code&gt; will include your E2E tests, which you don’t necessarily want to run alongside your component tests in the same command. After making this change, you can create your two component tests. For the first one, create a file at &lt;code&gt;src/components/CustomRenderer.spec.jsx&lt;/code&gt; and give it the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { test, expect } from "@playwright/experimental-ct-react";
import { CustomRenderer } from "./CustomRenderer";

test.use({ viewport: { width: 500, height: 500 } });

test("formats value correctly", async ({ mount }) =&amp;gt; {
  const component = await mount(&amp;lt;CustomRenderer value={10000} /&amp;gt;);
  await expect(component).toContainText("$10,000");
});

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

&lt;/div&gt;



&lt;p&gt;This test will ensure that the &lt;code&gt;CustomRenderer&lt;/code&gt; parses numeric values into monetary values correctly. Next, create a file at &lt;code&gt;src/components/DataGrid.spec.jsx&lt;/code&gt; and give it the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { test, expect } from "@playwright/experimental-ct-react";
import { DataGrid } from "./DataGrid";

test.use({ viewport: { width: 500, height: 500 } });

test("contains the expected data", async ({ mount }) =&amp;gt; {
  const component = await mount(&amp;lt;DataGrid /&amp;gt;);

  const expectedData = [
    ["Toyota", "Celica", "$35,000"],
    ["Ford", "Mondeo", "$32,000"],
    ["Porsche", "Boxster", "$72,000"],
  ];

  // Verify that the data is correct
  for (let index = 0; index &amp;lt; expectedData.length; index++) {
    const row = await component.locator("role=row").nth(index + 1);
    const [make, model, price] = expectedData[index];
    await expect(row).toContainText([make, model, price]);
  }
});

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

&lt;/div&gt;



&lt;p&gt;You will notice that this test borrows heavily from the E2E test in terms of logic. It is, after all, performing very similar assertions due to the limited scope of the application. The key difference here, however, is that it will not instantiate your whole application, nor does it require your Vite dev server to be running. With both of these tests created, you can run the following command to execute them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test-ct

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

&lt;/div&gt;



&lt;p&gt;From this command, you should see similar output to your E2E test, except it will be &lt;code&gt;6&lt;/code&gt; now instead of &lt;code&gt;3&lt;/code&gt; (because you have two tests and three browsers):&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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fcomponent-test-output-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Fcomponent-test-output-tiny.png" alt="Using Playwright to Test AG Grid React Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being able to test components in isolation, away from the rest of your application, is important because not only will the tests run faster than heavier E2E tests but, more importantly, the tests will not be affected by external factors and unrelated code. Performing your component tests with the same tools as your E2E tests (as opposed to using Jest and Testing Library) is a huge advantage. It opens the door for productivity boosts, such as sharing test helpers between E2E and component tests, allowing you to share abstractions and utilities between these two testing domains without the drawbacks that traditionally come with fully merging them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of Github Steps
&lt;/h2&gt;

&lt;p&gt;You can find the code for this tutorial in this public &lt;a href="https://github.com/cpave3/ag-grid-playwright" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, clone or download and unzip the repo code, then install the dependencies:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Second, install Playwright and browsers:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx playwright install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To run the component tests:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run test-ct&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To run the end to end tests we need to run the dev server from one console with &lt;code&gt;npm run dev&lt;/code&gt; to start the app running on &lt;code&gt;localhost&lt;/code&gt;. Then run the end to end tests with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run test&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After each test run you can use &lt;code&gt;npx playwright show-report&lt;/code&gt; to see the execution report.&lt;/p&gt;

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

&lt;p&gt;In this tutorial, you learned how to create a simple application with React, Vite, and &lt;a href="https://www.ag-grid.com/react-data-grid" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt;, and then test that application at an E2E and component level using Playwright. You’ve also learned about the differences between E2E and component testing, as well as some tools that can help you create those kinds of tests, like Playwright, Cypress, Jest, and Testing Library.&lt;/p&gt;

&lt;p&gt;If you want to learn more about Playwright, the &lt;a href="https://playwright.dev/docs/intro" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; is a great place to get started.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>react</category>
      <category>aggrid</category>
    </item>
    <item>
      <title>Using AG Grid in Electron Applications</title>
      <dc:creator>Alan Richardson</dc:creator>
      <pubDate>Thu, 21 Jul 2022 14:06:03 +0000</pubDate>
      <link>https://dev.to/ag-grid/using-ag-grid-in-electron-applications-d7e</link>
      <guid>https://dev.to/ag-grid/using-ag-grid-in-electron-applications-d7e</guid>
      <description>&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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Faggrid-electron-app-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Faggrid-electron-app-tiny.png" alt="Using AG Grid in Electron Applications"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post contributed to the AG Grid blog by &lt;a href="https://areknawo.com/" rel="noopener noreferrer"&gt;Arek Nawo&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.electronjs.org" rel="noopener noreferrer"&gt;Electron&lt;/a&gt; is a cross-platform framework for building native desktop applications with web technologies. It’s built on top of Node.js and the &lt;a href="https://www.chromium.org/chromium-projects/" rel="noopener noreferrer"&gt;Chromium&lt;/a&gt; browser, allowing you to use all of the latest features and improvements from the browser and JavaScript language.&lt;/p&gt;

&lt;p&gt;Electron enables you to create the best user experience while taking care of all the complex parts of making a native app. With Electron, you can reuse both your knowledge and codebase across the web and all desktop platforms. If you already know how to develop frontend web apps and Node.js backends, you’ll feel right at home with Electron as it basically combines those two into a single app.&lt;/p&gt;

&lt;p&gt;In this article, you’ll learn how to integrate &lt;a href="https://www.ag-grid.com/javascript-data-grid/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt;—an advanced and performant JavaScript grid library—into your Electron app. You’ll create a simple to-do app with native functionality to save and restore its state from a JSON file.&lt;/p&gt;

&lt;p&gt;You can follow along with &lt;a href="https://github.com/areknawo/Using-AG-Grid-in-Electron-Applications" rel="noopener noreferrer"&gt;this GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Electron Work?
&lt;/h2&gt;

&lt;p&gt;A basic Electron app consists of two processes. &lt;a href="https://www.electronjs.org/docs/latest/tutorial/process-model#the-main-process" rel="noopener noreferrer"&gt;The main process&lt;/a&gt; acts as an entry point for the app, with access to Node.js APIs and modules (including native ones). It also controls the app’s lifecycle and manages its windows using Electron-provided APIs.&lt;/p&gt;

&lt;p&gt;So while the main process is like your web app’s backend, &lt;a href="https://www.electronjs.org/docs/latest/tutorial/process-model#the-renderer-process" rel="noopener noreferrer"&gt;the renderer process&lt;/a&gt; is more like its frontend. It’s responsible for rendering the app’s UI and runs in every opened window. Thus, it should follow web standards (such as &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/HTML5" rel="noopener noreferrer"&gt;HTML5&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS" rel="noopener noreferrer"&gt;CSS3&lt;/a&gt;, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Language_Resources" rel="noopener noreferrer"&gt;ECMAScript&lt;/a&gt;) and use browser Web APIs. No Node-specific code is allowed.&lt;/p&gt;

&lt;p&gt;To communicate between processes (e.g., to handle data transfer or invoke native functionality from the UI), you can use &lt;a href="https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scriptshttps://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts" rel="noopener noreferrer"&gt;preload scripts&lt;/a&gt; and &lt;a href="https://www.electronjs.org/docs/latest/tutorial/ipc" rel="noopener noreferrer"&gt;inter-process communication&lt;/a&gt; (IPC). Preload scripts run in the renderer process before the main script and have access to the Node.js API. You can use them with the Electron’s &lt;code&gt;contextBridge&lt;/code&gt; module to safely expose privileged APIs to the renderer process. Most notably, you can expose helpers leveraging the &lt;code&gt;ipcRenderer&lt;/code&gt; module to communicate with the main process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using AG Grid with Electron
&lt;/h2&gt;

&lt;p&gt;A good way to get started with Electron is with &lt;a href="https://www.electronforge.io/" rel="noopener noreferrer"&gt;Electron Forge&lt;/a&gt;—“a complete tool for creating, publishing, and installing modern Electron applications.” Electron Forge has everything you need to work on your Electron app, including a &lt;a href="https://www.electronforge.io/templates/webpack-template" rel="noopener noreferrer"&gt;Webpack-powered template&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up the Project
&lt;/h3&gt;

&lt;p&gt;To initiate a new project with Electron Forge, ensure you have git and Node.js v12.13.0 or newer installed. Then, run the following commands to create the project, install additional dependencies, and start the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-electron-app@latest project --template=webpack
cd project
npm install ag-grid-community
npm run start

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Managing the App’s Window
&lt;/h4&gt;

&lt;p&gt;By default, the template includes files for main and renderer processes. Inside the &lt;strong&gt;src/main.js&lt;/strong&gt; file, you’ll see the starting point of your app. Take a closer look at the &lt;code&gt;createWindow()&lt;/code&gt; function and event listeners below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { app, BrowserWindow } = require("electron");
// ...

const createWindow = () =&amp;gt; {
  const mainWindow = new BrowserWindow();

  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
  mainWindow.webContents.openDevTools();
};

app.on("ready", createWindow);
app.on("window-all-closed", () =&amp;gt; {
  if (process.platform !== "darwin") {
    app.quit();
  }
});
app.on("activate", () =&amp;gt; {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;app&lt;/code&gt; module controls your app’s event lifecycle. When the app is ready, it creates a new window by calling &lt;code&gt;createWindow()&lt;/code&gt;, which in turn creates a new instance of &lt;code&gt;BrowserWindow&lt;/code&gt;. Using the &lt;code&gt;loadURL()&lt;/code&gt; method, the window loads the content served by Webpack’s development server. It also opens dev tools for easier debugging using the &lt;code&gt;webContents.openDevTools()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The other event listeners handle macOS-specific edge cases, like keeping the app open without any windows (&lt;code&gt;window-all-closed&lt;/code&gt;) or opening a new window when activated from the dock (&lt;code&gt;activate&lt;/code&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding Preload Script
&lt;/h4&gt;

&lt;p&gt;To allow native API access from the renderer process, you’ll have to expose some functions in the preload script. Although the template doesn’t include it by default, adding it yourself is easy.&lt;/p&gt;

&lt;p&gt;Create a new &lt;strong&gt;src/preload.js&lt;/strong&gt; file and edit the &lt;code&gt;config.forge.plugins&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; to inform Electron Forge about the preload script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  /* ... */
  "config": {
    "forge": {
      "packagerConfig": {},
      /* ... */
      "plugins": [
        [
          "@electron-forge/plugin-webpack",
          {
            "mainConfig": "./webpack.main.config.js",
            "renderer": {
              "config": "./webpack.renderer.config.js",
              "entryPoints": [
                {
                  "html": "./src/index.html",
                  "js": "./src/renderer.js",
                  "preload": {
                    "js": "./src/preload.js"
                  },
                  "name": "main_window"
                }
              ]
            }
          }
        ]
      ]
    }
  }
  /* ... */
}

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

&lt;/div&gt;



&lt;p&gt;To initialize the script, specify it when creating a &lt;code&gt;BrowserWindow&lt;/code&gt; in the &lt;strong&gt;src/main.js&lt;/strong&gt; file using the Webpack-provided global variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
const createWindow = () =&amp;gt; {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
  });

  // ...
};
// ...

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building the Renderer Process UI
&lt;/h3&gt;

&lt;p&gt;With essential window management ready, you can start working on the renderer process. You’ll use AG Grid to create a simple to-do list with the ability to add, remove, and mark items as complete.&lt;/p&gt;

&lt;p&gt;Developing a renderer process is very similar to creating a frontend web application. You can use all the frontend frameworks and APIs available in the browser environment. In this tutorial, you’ll use plain HTML, JS, and CSS to keep things simple.&lt;/p&gt;

&lt;p&gt;Start by creating your UI’s structure in the &lt;strong&gt;src/index.html&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8" /&amp;gt;
    &amp;lt;title&amp;gt;AG Grid + Electron&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class="container"&amp;gt;
      &amp;lt;h1&amp;gt;Electron TO-DO&amp;lt;/h1&amp;gt;
      &amp;lt;div class="btn-container"&amp;gt;
        &amp;lt;button id="save-btn" class="btn"&amp;gt;Save&amp;lt;/button&amp;gt;
        &amp;lt;button id="restore-btn" class="btn"&amp;gt;Restore&amp;lt;/button&amp;gt;
        &amp;lt;button id="add-btn" class="btn add-btn"&amp;gt;Add&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="divider"&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div id="grid" class="ag-theme-alpine"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Then, add the necessary styling in &lt;strong&gt;src/index.css&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html,
body {
  height: 100%;
  margin: 0;
}
#grid {
  width: 100%;
  flex: 1;
}
.container {
  margin: auto;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: calc(100% - 2rem);
  padding: 1rem;
  width: 30rem;
}
.btn-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: 0.5rem;
  width: 100%;
}
.divider {
  margin: 0.5rem 0;
  background: #e6e6e6;
  height: 0.125rem;
}
.checkbox {
  height: 100%;
  margin: 0;
  width: 1.25rem;
}
.btn {
  flex: 1;
  padding: 0.5rem;
  background: #6b7280;
  border: none;
  color: #fff;
  font-size: 1rem;
}
.btn:hover {
  background: #9ca3af;
  cursor: pointer;
}
.add-btn {
  padding: 0.5rem;
  background: #f97316;
  border: none;
}
.add-btn:hover {
  background: #fb923c;
  cursor: pointer;
}
.remove-btn {
  display: inline-flex;
  max-height: 1.25rem;
  max-width: 1.25rem;
  font-size: 1.25rem;
  justify-content: center;
  align-items: center;
  background-color: #ef4444;
}
.remove-btn:hover {
  background-color: #f87171;
}

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

&lt;/div&gt;



&lt;p&gt;With this setup, you can now move to the &lt;strong&gt;src/renderer.js&lt;/strong&gt; file to initialize the grid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
import "./index.css";
import { Grid } from "ag-grid-community";

let rowData = [];

const columnDefs = [
  // ...
];
const gridOptions = {
  columnDefs,
  rowData,
};
const saveBtn = document.getElementById("save-btn");
const restoreBtn = document.getElementById("restore-btn");
const addBtn = document.getElementById("add-btn");
const addTodo = () =&amp;gt; {
  // ...
};
const removeTodo = (rowIndex) =&amp;gt; {
  // ...
};
const saveToFile = () =&amp;gt; {
  // ...
};
const restoreFromFile = async () =&amp;gt; {
  // ...
};
const setupGrid = () =&amp;gt; {
  const gridDiv = document.getElementById("grid");

  new Grid(gridDiv, gridOptions);
  addBtn.addEventListener("click", addTodo);
  saveBtn.addEventListener("click", saveToFile);
  restoreBtn.addEventListener("click", restoreFromFile);
};

document.addEventListener("DOMContentLoaded", setupGrid);

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

&lt;/div&gt;



&lt;p&gt;All setup, including creating a &lt;code&gt;Grid&lt;/code&gt; instance and adding event handlers, happens after the DOM is loaded. The grid is created using the provided configuration, defining its columns and input data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
let rowData = [];

const columnDefs = [
  { field: "task", editable: true, flex: 1 },
  {
    field: "completed",
    width: 120,
    cellRenderer(params) {
      const input = document.createElement("input");

      input.type = "checkbox";
      input.checked = params.value;
      input.classList.add("checkbox");
      input.addEventListener("change", (event) =&amp;gt; {
        params.setValue(input.checked);
      });

      return input;
    },
  },
  {
    field: "remove",
    width: 100,
    cellRenderer(params) {
      const button = document.createElement("button");

      button.textContent = "✕";
      button.classList.add("btn", "remove-btn");
      button.addEventListener("click", () =&amp;gt; removeTodo(params.rowIndex));

      return button;
    },
  },
];
// ...

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

&lt;/div&gt;



&lt;p&gt;The columns are defined by certain parameters such as &lt;code&gt;field&lt;/code&gt; name, &lt;code&gt;width&lt;/code&gt;, or custom &lt;code&gt;cellRenderer&lt;/code&gt; in case you want to display the data differently. &lt;code&gt;editable&lt;/code&gt; enables built-in editing support, allowing the user to change the task’s name. At the same time, &lt;code&gt;flex&lt;/code&gt; is an alternative to &lt;code&gt;width&lt;/code&gt;, indicating that the column should fill the remaining space.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;“completed”&lt;/em&gt; and &lt;em&gt;“remove”&lt;/em&gt; columns, custom cell renderers render a checkbox and button to, respectively, change the status of the task or remove it from the list entirely. The actual change to the grid’s data is done with &lt;code&gt;params.setValue()&lt;/code&gt; and a separate &lt;code&gt;removeTodo()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;addTodo()&lt;/code&gt; and &lt;code&gt;removeTodo()&lt;/code&gt; operate using the &lt;code&gt;gridOptions.api&lt;/code&gt; object. After being provided to the grid, &lt;code&gt;gridOptions&lt;/code&gt; gets the &lt;code&gt;api&lt;/code&gt; property to allow control of the grid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
const addTodo = () =&amp;gt; {
  rowData = [...rowData, { task: "New Task", completed: false }];
  gridOptions.api.setRowData(rowData);
};
const removeTodo = (rowIndex) =&amp;gt; {
  rowData = rowData.filter((value, index) =&amp;gt; {
    return index !== rowIndex;
  });
  gridOptions.api.setRowData(rowData);
};
// ...

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

&lt;/div&gt;



&lt;p&gt;Values from &lt;code&gt;rowData&lt;/code&gt; items are also bound to the grid, meaning that if the user changes the completion status or the task’s name, the new value is reflected in one of the &lt;code&gt;rowData&lt;/code&gt; items.&lt;/p&gt;

&lt;p&gt;With all these changes, the app’s UI is now fully functional and looks 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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Felectron-todo-app-with-ag-grid-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Felectron-todo-app-with-ag-grid-tiny.png" alt="Using AG Grid in Electron Applications"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All that’s left is to implement save and restore functionality. For that, you’ll have to return to the main process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Native Functionality
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;strong&gt;src/main.js&lt;/strong&gt; file, create a new function, &lt;code&gt;handleCommunication()&lt;/code&gt;, for handling IPC implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { app, BrowserWindow, ipcMain, dialog } = require("electron");
// ...

const handleCommunication = () =&amp;gt; {
  ipcMain.removeHandler("save-to-file");
  ipcMain.removeHandler("restore-from-file");
  ipcMain.handle("save-to-file", async (event, data) =&amp;gt; {
    try {
      const { canceled, filePath } = await dialog.showSaveDialog({
        defaultPath: "todo.json",
      });

      if (!canceled) {
        await fs.writeFile(filePath, data, "utf8");

        return { success: true };
      }
      return {
        canceled,
      };
    } catch (error) {
      return { error };
    }
  });
  ipcMain.handle("restore-from-file", async () =&amp;gt; {
    try {
      const { canceled, filePaths } = await dialog.showOpenDialog({
        properties: ["openFile"],
        filters: [
          {
            name: "json",
            extensions: ["json"],
          },
        ],
      });

      if (!canceled) {
        const [filePath] = filePaths;
        const data = await fs.readFile(filePath, "utf8");

        return { success: true, data };
      } else {
        return { canceled };
      }
    } catch (error) {
      return { error };
    }
  });
};
// ...

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

&lt;/div&gt;



&lt;p&gt;First, using &lt;code&gt;ipcMain.removeHandler()&lt;/code&gt;, ensure that no existing handlers are attached to the used channels in case the window was reactivated (macOS-specific). The &lt;code&gt;ipcMain.handle()&lt;/code&gt; method allows you to handle specific events and respond with data by simply returning a value from the handler.&lt;/p&gt;

&lt;p&gt;For this app, the channels used are &lt;code&gt;”save-to-file”&lt;/code&gt; and &lt;code&gt;”restore-from-file”&lt;/code&gt;. Their handlers use the &lt;code&gt;dialog&lt;/code&gt; module to bring up the system’s native open or save dialogues. The resulting paths are then provided to Node.js’s built-in &lt;code&gt;fs&lt;/code&gt; module to read from or write to the provided file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;handleCommunication()&lt;/code&gt; should be called from the &lt;code&gt;createWindow()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
const createWindow = () =&amp;gt; {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
  });

  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
  mainWindow.webContents.openDevTools();
  handleCommunication();
};
// ...

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

&lt;/div&gt;



&lt;p&gt;To be able to send an IPC message from the renderer process, you’ll have to use the preload script and the &lt;code&gt;contextBridge&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electronAPI", {
  saveToFile(data) {
    return ipcRenderer.invoke("save-to-file", data);
  },
  restoreFromFile() {
    return ipcRenderer.invoke("restore-from-file");
  },
});

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;contextBridge.exposeInMainWorld()&lt;/code&gt; safely exposes the provided API to the renderer process. Keep in mind that, in preload script, you’ve got access to privileged APIs, which, for security reasons, should be freely available from the frontend of your Electron app.&lt;/p&gt;

&lt;p&gt;The exposed methods use the &lt;code&gt;ipcRenderer&lt;/code&gt; module to send messages to the &lt;code&gt;ipcMain&lt;/code&gt; listener on the other process. In the case of the &lt;code&gt;”save-to-file”&lt;/code&gt; channel, additional data in the form of a JSON string to save is provided.&lt;/p&gt;

&lt;p&gt;With the bridge ready, you can return to the renderer process and finish the integration by adding proper handlers for the last two buttons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
const saveToFile = () =&amp;gt; {
  window.electronAPI.saveToFile(JSON.stringify(rowData));
};
const restoreFromFile = async () =&amp;gt; {
  const result = await window.electronAPI.restoreFromFile();

  if (result.success) {
    rowData = JSON.parse(result.data);
    gridOptions.api.setRowData(rowData);
  }
};
// ...

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

&lt;/div&gt;



&lt;p&gt;Each handler uses methods from the &lt;code&gt;electronAPI&lt;/code&gt; object available on the &lt;code&gt;window&lt;/code&gt;. In &lt;code&gt;saveToFile()&lt;/code&gt;, the &lt;code&gt;rowData&lt;/code&gt; is stringified and sent to the main process for writing it to the file. In the case of the restore operation, it first &lt;code&gt;await&lt;/code&gt;s the file’s stringified content to then parse and assign it to the grid.&lt;/p&gt;

&lt;p&gt;Now your app can use the native file dialog to restore and save its state:&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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Ftodo-save-dialog-tiny.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%2Fblog.ag-grid.com%2Fcontent%2Fimages%2F2022%2F07%2Ftodo-save-dialog-tiny.png" alt="Using AG Grid in Electron Applications"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final app outputs a JSON file like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[{"task":"Learn Electron","completed":true},{"task":"Learn AG Grid","completed":false}]

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

&lt;/div&gt;



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

&lt;p&gt;Now you know how to use AG Grid in an Electron app. You’ve learned about Electron processes—what they are and how they work. Finally, you established IPC to communicate between them to implement native functionality into your app.&lt;/p&gt;

&lt;p&gt;As you’ve seen, thanks to Electron, you can quickly transform your web app into an installable native desktop app with all the functionality that comes alongside it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ag-grid.com/" rel="noopener noreferrer"&gt;AG Grid&lt;/a&gt; is a high-performance JavaScript table library that’s easy to set up. It integrates well with your favorite frameworks, like React, and works great with other parts of the JavaScript ecosystem, such as Electron. Check out &lt;a href="https://www.ag-grid.com/react-data-grid/" rel="noopener noreferrer"&gt;the official documentation&lt;/a&gt; to learn more.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>aggrid</category>
    </item>
    <item>
      <title>Using React-Bootstrap and AG Grid</title>
      <dc:creator>Alan Richardson</dc:creator>
      <pubDate>Wed, 20 Jul 2022 13:24:24 +0000</pubDate>
      <link>https://dev.to/ag-grid/using-react-bootstrap-and-ag-grid-13pk</link>
      <guid>https://dev.to/ag-grid/using-react-bootstrap-and-ag-grid-13pk</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U6Hv9bFc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/react-bootstrap-aggrid-tiny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U6Hv9bFc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/react-bootstrap-aggrid-tiny.png" alt="Using React-Bootstrap and AG Grid" width="688" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post contributed to the AG Grid blog by &lt;a href="https://areknawo.com/"&gt;Arek Nawo&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt; is one of the most popular CSS frameworks, so it’s no surprise that many libraries integrate it with the top JavaScript UI frameworks. One such library is React-Bootstrap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://react-bootstrap.github.io/"&gt;React-Bootstrap&lt;/a&gt; provides Bootstrap-powered React components with built-in reactivity. Every component is compatible with Bootstrap themes, optimized for accessibility, and can be controlled with React props. As one of the oldest React component libraries, React-Bootstrap is an excellent choice for building modern, responsive UI.&lt;/p&gt;

&lt;p&gt;In this article, you’ll learn how to integrate React-Bootstrap with &lt;a href="https://www.ag-grid.com/"&gt;AG Grid&lt;/a&gt;, a batteries-included JavaScript grid with &lt;a href="https://www.ag-grid.com/react-data-grid/"&gt;first-party React integration&lt;/a&gt;. You’ll see how easy it is to use both tools to build a compelling, user-friendly UI.&lt;/p&gt;

&lt;p&gt;You can find the complete code for this tutorial in &lt;a href="https://github.com/areknawo/Using-React-Bootstrap-and-AG-Grid"&gt;this GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AG Grid vs. React-Bootstrap Table
&lt;/h2&gt;

&lt;p&gt;Before getting into the code, you should know the differences between AG Grid and React-Bootstrap’s built-in &lt;code&gt;Table&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;At its core, the &lt;code&gt;Table&lt;/code&gt; component is a traditional HTML table with a header, body, rows, and cells. React-Bootstrap makes the often-tedious process of creating an HTML table easier, not only with React’s reactivity and JSX but also with a faster styling process. You can set basic styles, add a row hover effect or dark theme, and make the table responsive with just a few props. However, it’s still a simple table that’s meant for basic use cases.&lt;/p&gt;

&lt;p&gt;On the other hand, AG Grid is a full-blown, high-performance grid library. It provides you with everything you need to create an advanced, highly interactive grid-based UI, including filtering, data streaming, charting, and more. It’s your go-to solution if you need anything beyond a static table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using AG Grid with React-Bootstrap
&lt;/h2&gt;

&lt;p&gt;To get started, make sure you’ve got NPM v7 and Node.js v12.2.0 or newer installed. Then, run the following commands to scaffold your project using &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt;—a fast ES module-based bundler—and install necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite project --template react
cd project
npm install
npm install bootstrap react-bootstrap ag-grid-community ag-grid-react

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

&lt;/div&gt;



&lt;p&gt;The installed dependencies include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React-Bootstrap and “vanilla” Bootstrap for CSS styles loading&lt;/li&gt;
&lt;li&gt;AG Grid’s core “community” package and React Data Grid for rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To start the development server, use &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Card Container
&lt;/h3&gt;

&lt;p&gt;To learn how to integrate React-Bootstrap with AG Grid, you’ll build a simple app listing countries to visit. You use React-Bootstrap to add functionality and visuals to the grid with images and buttons.&lt;/p&gt;

&lt;p&gt;Inside the &lt;strong&gt;src/App.jsx&lt;/strong&gt; file, you initialize AG Grid together with React-Bootstrap. Start by creating a Bootstrap card for holding the grid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Card from "react-bootstrap/Card";
import Stack from "react-bootstrap/Stack";
import "bootstrap/dist/css/bootstrap.min.css";

const App = () =&amp;gt; {
  return (
    &amp;lt;Card body className="h-100"&amp;gt;
      &amp;lt;Stack className="h-100"&amp;gt;
        &amp;lt;Card.Title&amp;gt;Countries to visit&amp;lt;/Card.Title&amp;gt;
        [Grid]
      &amp;lt;/Stack&amp;gt;
    &amp;lt;/Card&amp;gt;
  );
};

export default App;

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

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;Card&lt;/code&gt; and &lt;code&gt;Stack&lt;/code&gt; components, you just built a full-height card with vertically stacked content. The grid will be placed just below the title.&lt;/p&gt;

&lt;p&gt;To center the card in the body, add some CSS in the &lt;strong&gt;src/index.css&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html {
  height: 100%;
  width: 100%;
}
body {
  margin: 0;
  padding: 1rem;
  height: 100%;
}
#root {
  margin: auto;
  height: 100%;
  width: 100%;
  max-width: 48rem;
}

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

&lt;/div&gt;



&lt;p&gt;With these changes, the card should now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sTjcSR3U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/bootstrap-card-container-tiny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sTjcSR3U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/bootstrap-card-container-tiny.png" alt="Using React-Bootstrap and AG Grid" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Basic AG Grid
&lt;/h3&gt;

&lt;p&gt;To add the grid to the setup, you have to create a data source, which can be done using the &lt;code&gt;useState()&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
import { useState } from "react";
// ...

const App = () =&amp;gt; {
  const [columnDefs] = useState([
    {
      headerName: "Country",
      field: "country",
    },
    {
      headerName: "Image",
      field: "image",
      flex: 1,
    },
    {
      headerName: "Visited",
      field: "visited",
    },
  ]);
  const [rowData] = useState([
    {
      country: "United Kingdom",
      image:
        "https://images.unsplash.com/photo-1486299267070-83823f5448dd?ixlib=rb-1.2.1&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=2371&amp;amp;q=80",
      visited: false,
    },
    {
      country: "United States",
      image:
        "https://images.unsplash.com/photo-1501594907352-04cda38ebc29?ixlib=rb-1.2.1&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=2832&amp;amp;q=80",
      visited: false,
    },
    {
      country: "India",
      visited: false,
      image:
        "https://images.unsplash.com/photo-1524492412937-b28074a5d7da?ixlib=rb-1.2.1&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=2371&amp;amp;q=80",
    },
    {
      country: "Canada",
      visited: false,
      image:
        "https://images.unsplash.com/photo-1519832979-6fa011b87667?ixlib=rb-1.2.1&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=2353&amp;amp;q=80",
    },
    {
      country: "Brazil",
      visited: false,
      image:
        "https://images.unsplash.com/photo-1483729558449-99ef09a8c325?ixlib=rb-1.2.1&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=2370&amp;amp;q=80",
    },
    {
      country: "Germany",
      visited: false,
      image:
        "https://images.unsplash.com/photo-1554072675-66db59dba46f?ixlib=rb-1.2.1&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=2373&amp;amp;q=80",
    },
    {
      country: "France",
      visited: false,
      image:
        "https://images.unsplash.com/photo-1431274172761-fca41d930114?ixlib=rb-1.2.1&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=2370&amp;amp;q=80",
    },
  ]);

  // ...
};
// ...

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

&lt;/div&gt;



&lt;p&gt;Each data row contains the name of the country, a related &lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt; image URL, and a Boolean indicating whether it was already visited. In addition, &lt;code&gt;columnDefs&lt;/code&gt; define how the grid’s columns should be structured—e.g., their headers and width. In this case, all columns are displayed as text, with the image column taking the remaining horizontal space with &lt;code&gt;flex: 1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To display the grid, use the &lt;code&gt;AgGridReact&lt;/code&gt; component passing the row data, column definition, and any additional properties—&lt;code&gt;rowHeight&lt;/code&gt; in pixels in this case—as props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-material.css";
// ...

const App = () =&amp;gt; {
  // ...
  return (
    &amp;lt;Card body className="h-100"&amp;gt;
      &amp;lt;Stack className="h-100 ag-theme-material"&amp;gt;
        &amp;lt;Card.Title&amp;gt;Countries to visit&amp;lt;/Card.Title&amp;gt;
        &amp;lt;AgGridReact
          columnDefs={columnDefs}
          rowData={rowData}
          rowHeight={200}
        &amp;gt;&amp;lt;/AgGridReact&amp;gt;
      &amp;lt;/Stack&amp;gt;
    &amp;lt;/Card&amp;gt;
  );
};

// ...

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

&lt;/div&gt;



&lt;p&gt;The grid also comes with CSS of its own, which you also have to import. The &lt;code&gt;ag-theme-material&lt;/code&gt; class on the grid’s wrapper indicates what theme to use.&lt;/p&gt;

&lt;p&gt;The grid is now ready, but it displays all columns as text:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yCkIR1Lb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/basic-ag-grid-setup-tiny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yCkIR1Lb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/basic-ag-grid-setup-tiny.png" alt="Using React-Bootstrap and AG Grid" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s use React-Bootstrap and replace URLs with images.&lt;/p&gt;

&lt;h3&gt;
  
  
  Displaying Images
&lt;/h3&gt;

&lt;p&gt;To display images in the grid, you’ll have to create a new component to be used as a cell renderer.&lt;/p&gt;

&lt;p&gt;Create a new file at &lt;strong&gt;src/ImageRenderer.jsx&lt;/strong&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Image from "react-bootstrap/Image";
import Stack from "react-bootstrap/Stack";

const ImageRenderer = (props) =&amp;gt; {
  return (
    &amp;lt;Stack direction="horizontal" className="h-100"&amp;gt;
      &amp;lt;Image rounded src={props.getValue()} className="h-auto w-100" /&amp;gt;
    &amp;lt;/Stack&amp;gt;
  );
};

export default ImageRenderer;

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

&lt;/div&gt;



&lt;p&gt;Cell-renderer components receive data about the grid and current cell as props. You can use &lt;code&gt;props.getValue()&lt;/code&gt; to get the cell’s current value, an image URL in this example. This value is then provided to React-Bootstrap’s &lt;code&gt;Image&lt;/code&gt; component, along with other props. Additional Bootstrap class names and horizontally aligned &lt;code&gt;Stack&lt;/code&gt; assure centered positioning and grid-adjusted sizing of the image.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;ImageRenderer&lt;/code&gt; in the grid, return to &lt;strong&gt;src/App.jsx&lt;/strong&gt; and set it as a cell renderer for the image column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
import ImageRenderer from "./ImageRenderer";
// ...

const App = () =&amp;gt; {
  const [columnDefs] = useState([
    {
      headerName: "Country",
      field: "country",
    },
    {
      headerName: "Image",
      field: "image",
      flex: 1,
      // Set ImageRenderer component as cell renderer
      cellRenderer: ImageRenderer,
    },
    {
      headerName: "Visited",
      field: "visited",
    },
  ]);
  // ...
};

// ...

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

&lt;/div&gt;



&lt;p&gt;With these improvements, the grid already looks a lot better:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B2AY2Ymj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/ag-grid-with-images-tiny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B2AY2Ymj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/ag-grid-with-images-tiny.png" alt="Using React-Bootstrap and AG Grid" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Toggle Buttons
&lt;/h3&gt;

&lt;p&gt;Besides seeing images, the user should be able to mark countries as visited. Currently, the visited column is empty as it contains only Boolean &lt;code&gt;false&lt;/code&gt; values. To show a toggle button instead, create a new cell renderer at &lt;strong&gt;src/VisitedRenderer.jsx&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ToggleButton from "react-bootstrap/ToggleButton";

const VisitedRenderer = (props) =&amp;gt; {
  return (
    &amp;lt;ToggleButton
      size="sm"
      id={`visited-${props.rowIndex}`}
      type="checkbox"
      variant={props.getValue() ? "outline-primary" : "outline-secondary"}
      checked={props.getValue()}
      value="1"
      onChange={(e) =&amp;gt; {
        props.setValue(e.currentTarget.checked);
      }}
    &amp;gt;
      {props.getValue() ? "YES" : "NO"}
    &amp;lt;/ToggleButton&amp;gt;
  );
};

export default VisitedRenderer;

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

&lt;/div&gt;



&lt;p&gt;Using React-Bootstrap’s &lt;code&gt;ToggleButton&lt;/code&gt;, you can easily create a button-style checkbox. The button is styled and labeled according to the prop’s value, which is set on every toggle with &lt;code&gt;props.setValue()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pay special attention to the &lt;code&gt;id&lt;/code&gt; prop. It’s important for it to be unique, as React-Bootstrap uses it as an HTML &lt;code&gt;id&lt;/code&gt; attribute, matching the underlying &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; checkbox and button-styled &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now all you have to do is load the cell renderer in &lt;strong&gt;src/App.jsx&lt;/strong&gt; , and you should see toggle buttons appear in the column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
import VisitedRenderer from "./VisitedRenderer";
// ...

const App = () =&amp;gt; {
  const [columnDefs] = useState([
    {
      headerName: "Country",
      field: "country",
    },
    {
      headerName: "Image",
      field: "image",
      flex: 1,
      cellRenderer: ImageRenderer,
    },
    {
      headerName: "Visited",
      field: "visited",
      // Set VisitedRenderer component as cell renderer
      cellRenderer: VisitedRenderer,
    },
  ]);
  // ...
};

// ...

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C6w7W8fL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/ag-grid-toggle-buttons-tiny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C6w7W8fL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/ag-grid-toggle-buttons-tiny.png" alt="Using React-Bootstrap and AG Grid" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Customizing Column Header
&lt;/h3&gt;

&lt;p&gt;One of many great things about AG Grid is that it’s very customizable, far beyond custom cell renderers. You can utilize this customizability together with React-Bootstrap to build—e.g., custom column headers with different functionalities.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up External Filter
&lt;/h4&gt;

&lt;p&gt;For this example, you’ll build a custom header for the visited column with the ability to filter the list for only the visited countries. Start by adding an external filter to the grid in &lt;strong&gt;src/App.jsx&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useEffect, useState, useRef } from "react";
// ...

const App = () =&amp;gt; {
  const gridRef = useRef();
  const [filterEnabled, setFilterEnabled] = useState(false);
  // ...
  const doesExternalFilterPass = (node) =&amp;gt; {
    return node.data.visited;
  };

  useEffect(() =&amp;gt; {
    if (gridRef.current &amp;amp;&amp;amp; gridRef.current.api) {
      gridRef.current.api.onFilterChanged();
    }
  }, [filterEnabled]);

  return (
    &amp;lt;Card body className="h-100"&amp;gt;
      &amp;lt;Stack className="h-100"&amp;gt;
        &amp;lt;Card.Title&amp;gt;Countries to visit&amp;lt;/Card.Title&amp;gt;
        &amp;lt;div className="ag-theme-material h-100 w-100"&amp;gt;
          &amp;lt;AgGridReact
            ref={gridRef}
            columnDefs={columnDefs}
            rowData={rowData}
            rowHeight={200}
            doesExternalFilterPass={doesExternalFilterPass}
            isExternalFilterPresent={() =&amp;gt; filterEnabled}
          &amp;gt;&amp;lt;/AgGridReact&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/Stack&amp;gt;
    &amp;lt;/Card&amp;gt;
  );
};

// ...

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

&lt;/div&gt;



&lt;p&gt;In AG Grid, &lt;a href="https://www.ag-grid.com/react-data-grid/filter-external/"&gt;external filters&lt;/a&gt; allow you to mix your custom filtering logic with the mechanisms already integrated into the grid. To enable them, you have to provide two additional props:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;doesExternalFilterPass&lt;/code&gt;: This is the actual filter function; given a node, it should return a Boolean indicating whether or not to include the row in the result.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isExternalFilterPresent&lt;/code&gt;: This is a function returning a Boolean that informs the grid about the presence of the external filter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the code above, the external filter is controlled by a &lt;code&gt;filterEnabled&lt;/code&gt; state property and a &lt;code&gt;doesExternalFilterPass()&lt;/code&gt; function, which extracts the &lt;code&gt;visited&lt;/code&gt; boolean from row data to filter the grid.&lt;/p&gt;

&lt;p&gt;To make the grid process the filter accordingly on &lt;code&gt;filterEnabled&lt;/code&gt; change, you must first inform it about a change in the filter configuration using &lt;code&gt;api.onFilterChanged()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;api&lt;/code&gt; object is accessible on the grid element; thus, you should use the &lt;code&gt;useRef()&lt;/code&gt; hook to create a reference to it first. Then, with the &lt;code&gt;useEffect()&lt;/code&gt; hook, call &lt;code&gt;api.onFilterChanged()&lt;/code&gt; every time &lt;code&gt;filterEnabled&lt;/code&gt; changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding Toggle Button Header
&lt;/h4&gt;

&lt;p&gt;With the external filter ready, now it’s just a matter of creating a custom header component that will set &lt;code&gt;filterEnabled&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;src/VisitedHeader.jsx&lt;/strong&gt; , create the following component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState, useEffect } from "react";
import ToggleButton from "react-bootstrap/ToggleButton";

const VisitedHeader = (props) =&amp;gt; {
  const [checked, setChecked] = useState(false);

  useEffect(() =&amp;gt; {
    props.setFilterEnabled(checked);
  }, [checked]);

  return (
    &amp;lt;ToggleButton
      id="filter-visited"
      type="checkbox"
      variant={checked ? "outline-primary" : "outline-secondary"}
      checked={checked}
      value="1"
      onChange={(e) =&amp;gt; {
        setChecked(e.currentTarget.checked);
      }}
    &amp;gt;
      Visited
    &amp;lt;/ToggleButton&amp;gt;
  );
};

export default VisitedHeader;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;VisitedHeader&lt;/code&gt; once again uses React-Bootstrap’s &lt;code&gt;ToggleButton&lt;/code&gt; with custom &lt;code&gt;id&lt;/code&gt; and value-based styling. On top of that, it has its own state property, &lt;code&gt;checked&lt;/code&gt;, to keep track of the toggle button. The parent component is notified about every change with the &lt;code&gt;useEffect()&lt;/code&gt; and &lt;code&gt;setFilterEnabled()&lt;/code&gt; method passed through the props.&lt;/p&gt;

&lt;p&gt;Back in the &lt;strong&gt;src/App.jsx&lt;/strong&gt; file, you should set the header component and pass the required &lt;code&gt;setFilterEnabled&lt;/code&gt; prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
import VisitedHeader from "./VisitedHeader";
// ...

const App = () =&amp;gt; {
  // ...
  const [columnDefs] = useState([
    {
      headerName: "Country",
      field: "country",
    },
    {
      headerName: "Image",
      field: "image",
      flex: 1,
      cellRenderer: ImageRenderer,
    },
    {
      headerName: "Visited",
      // Set VisitedHeader component as header component
      headerComponent: VisitedHeader,
      // Pass additional props to the header component
      headerComponentParams: {
        setFilterEnabled,
      },
      field: "visited",
      cellRenderer: VisitedRenderer,
    },
  ]);

  // ...
};

// ...

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

&lt;/div&gt;



&lt;p&gt;It’s worth noting that AG Grid also provides a distinctive &lt;code&gt;headerComponentParams&lt;/code&gt; property to pass custom props to the header component (aside from the grid-provided ones). The component itself can be set using the &lt;code&gt;headerComponent&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;The final result looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VBwh-k7M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/final-result-ag-grid-bootstrap-tiny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VBwh-k7M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/07/final-result-ag-grid-bootstrap-tiny.png" alt="Using React-Bootstrap and AG Grid" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, you’ve learned how you can use AG Grid and React-Bootstrap together, as well as how customizable AG Grid really is. By using AG Grid’s React Data Grid, you not only get a great, highly advanced grid view but also easy access to the ecosystem of the leading JS UI framework. With such powerful tools, the only limit to your app is your imagination.&lt;/p&gt;

&lt;p&gt;AG Grid is the leading JavaScript Grid. It should be your go-to open source tool whenever you need a batteries-included grid solution, with advanced features like live data streaming, charting, editing, and more. Check out AG Grid’s &lt;a href="https://www.ag-grid.com/react-data-grid/"&gt;official documentation&lt;/a&gt; to learn more.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>react</category>
      <category>aggrid</category>
    </item>
    <item>
      <title>How to Optimize a React Application Using Hooks and AG Grid</title>
      <dc:creator>Alan Richardson</dc:creator>
      <pubDate>Mon, 18 Jul 2022 11:28:52 +0000</pubDate>
      <link>https://dev.to/ag-grid/how-to-optimize-a-react-application-using-hooks-and-ag-grid-26bn</link>
      <guid>https://dev.to/ag-grid/how-to-optimize-a-react-application-using-hooks-and-ag-grid-26bn</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--em4Qs-B5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/blog-optimizing-hooks-react-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--em4Qs-B5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/blog-optimizing-hooks-react-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="688" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post contributed to the AG Grid blog by &lt;a href="https://twitter.com/cpave3"&gt;Cameron Pavey&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://reactjs.org"&gt;React&lt;/a&gt; describes itself as a “JavaScript library for building user interfaces.” Its internal workings are quite complex, but there are essentially two main pieces: React itself and the &lt;a href="https://reactjs.org/docs/test-renderer.html"&gt;React renderer&lt;/a&gt;, which is &lt;code&gt;react-dom&lt;/code&gt; in the case of web browsers.&lt;/p&gt;

&lt;p&gt;The main React library is responsible for taking your code and converting it to a structure that a React renderer, such as &lt;code&gt;react-dom&lt;/code&gt;, can then use to reconcile the &lt;em&gt;desired state&lt;/em&gt; with the &lt;em&gt;current state&lt;/em&gt;, and make the necessary changes for those two states to converge. How you write your code can have a great impact on the magnitude of these changes. It’s not uncommon for React to make more changes than strictly necessary when reconciling the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction"&gt;Document Object Model (DOM)&lt;/a&gt;. These changes, or “renders,” can typically be reduced by optimizing your code in various ways. Such optimizations are generally desirable, but even more so when dealing with large volumes of data or many DOM nodes. While unoptimized code may not be problematic in small quantities, at scale, it can quickly affect user experience.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn about some of these optimizations. You’ll also learn about some common mistakes that can lead to unnecessary renders, and you’ll see how you can avoid them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ag-grid.com"&gt;AG Grid&lt;/a&gt;, a high-performance Data Grid, plays a large role in this guide. It works well with most leading frontend libraries, including React, and provides a good use case for the optimizations discussed here for two reasons. Firstly, AG Grid itself is optimized internally, and thus the impact of poorly optimized code surrounding it is more apparent. Secondly, AG Grid is capable of dealing with tremendous amounts of data, which can often have an adverse impact on performance if mishandled, but is also an excellent way to test the efficacy of supposed code optimizations at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profiling the Unoptimized Grid
&lt;/h2&gt;

&lt;p&gt;In order to focus on the optimizations themselves, this guide won’t cover the building of the unoptimized application step by step. Instead, if you’d like to follow along, you can clone the unoptimized source code for this guide from the &lt;a href="https://github.com/cpave3/ag-grid-optimizations"&gt;public GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You also need the following prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a recent version of &lt;a href="https://nodejs.org/en/download/"&gt;Node.js and npm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;a code editor (&lt;a href="https://code.visualstudio.com/"&gt;VS Code&lt;/a&gt; is a good choice if you don’t have an existing preference.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As mentioned before, AG Grid is already &lt;a href="https://build.ag-grid.com/react-data-grid/reactui/#no-wasted-renders"&gt;quite heavily optimized&lt;/a&gt;, so much so that any adverse performance issues you may encounter while using it are likely to be in your application code, and the code surrounding your usage of the Grid component. To this end, the optimizations covered in this article will primarily focus on this surrounding code to help ensure that you’re using AG Grid as responsibly as possible.&lt;/p&gt;

&lt;p&gt;To establish a baseline for these optimizations, you need to see how the current unoptimized application performs. There are a few different ways to do this. The most common way is to use the &lt;a href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en"&gt;React Developer Tools&lt;/a&gt; browser extension, which includes a purpose-built profiler. This profiler lets you see details about your application, like which components were re-rendered in a given &lt;a href="https://reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html#browsing-commits"&gt;commit&lt;/a&gt;, why they were re-rendered, and how long it took to do so. If you want to follow along with this tutorial, go ahead and add this extension to your browser.&lt;/p&gt;

&lt;p&gt;Once you’ve cloned the demo application from the &lt;a href="https://github.com/cpave3/ag-grid-optimizations"&gt;public GitHub repo&lt;/a&gt;, navigate into the newly created directory, and run the following commands:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;This command installs the code dependencies and starts the development server. Once started, the development server will indicate which port it’s running on (typically port 3000) and open the demo application in your default browser.&lt;/p&gt;

&lt;p&gt;When the page has loaded, you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wPmAakvd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/initial-page-load-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wPmAakvd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/initial-page-load-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the developer tools by right-clicking somewhere on the page and selecting &lt;strong&gt;Inspect&lt;/strong&gt;. By default, this will open the &lt;strong&gt;Elements&lt;/strong&gt; tab. You can find the React DevTools Profiler by selecting the &lt;strong&gt;Profiler&lt;/strong&gt; tab. You may need to click the &lt;strong&gt;arrow icon&lt;/strong&gt; at the end of the tabs to see it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3POfCrZU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/devs-profiler-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3POfCrZU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/devs-profiler-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The DevTools are quite extensive, and their full functionality is beyond the scope of this guide. If you want to learn more about the DevTools as a whole, you can start with this &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-debug-react-components-using-react-developer-tools"&gt;guide&lt;/a&gt; that focuses primarily on the profiling functionality.&lt;/p&gt;

&lt;p&gt;The unoptimized Grid has a few issues that cause unnecessary re-renders. To help identify these, some visual aids have been added, but it’s also possible to see them in the profiler. To get a consistent baseline for later measurements, it helps to do some controlled checks that you can replicate later. In this initial measurement, perform the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start the profiler recording&lt;/li&gt;
&lt;li&gt;For each of the first four cells in the &lt;strong&gt;First_name&lt;/strong&gt; column, click on the cell once&lt;/li&gt;
&lt;li&gt;Next, click the &lt;strong&gt;Change Columns&lt;/strong&gt; button four times&lt;/li&gt;
&lt;li&gt;Stop the profiler recording&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you interact with the table, you’ll notice that some visual aspects changed like the color of the &lt;strong&gt;Id&lt;/strong&gt; column and the numbers prefixing the &lt;strong&gt;First_name&lt;/strong&gt; values. These are visual helpers added to show when certain components have re-rendered. Before you learn about this in more detail, let’s take a look at the results in the profiler:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o2BkOboe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/unoptimized-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o2BkOboe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/unoptimized-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your exact output may vary if you didn’t follow the actions outlined above precisely. Near the top of the profiler, there is a small chart that you can cycle through, along with some numbers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eO6FKtZ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/chart-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eO6FKtZ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/chart-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="393" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This chart represents the &lt;strong&gt;commits&lt;/strong&gt; made by React and their relative durations. The larger the bar, the longer the commit took. In this scenario, the commits don’t take very long (the largest one only takes about 12 ms). However, the principles here apply equally to larger React applications that may be affected by more adverse performance issues, with renders that take anywhere from 100 ms to full seconds.&lt;/p&gt;

&lt;p&gt;As you cycle through the commits, different parts of the flame graph will be highlighted, representing the re-rendered components in a given commit. Looking at the previous screenshot, you can see that in the highlighted commit (and the other similar spikes in the graph), all of the rows re-rendered. This results in the slowest commits out of those that the profiler captured. If you go back and look at the first few commits, you can similarly see that each time you clicked on one of the cells, it caused two cells to be re-rendered—the one that you clicked on and the one that you were already focused on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2DktnkJ1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/cell-renderers-profile-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2DktnkJ1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/cell-renderers-profile-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This can cause inefficiencies if those cells are using expensive, poorly optimized &lt;a href="https://www.ag-grid.com/react-data-grid/component-cell-renderer/"&gt;custom cell renderers&lt;/a&gt;. The &lt;strong&gt;First_name&lt;/strong&gt; column uses a custom cell renderer to display the counter in parenthesis. This counter will increment by one, each time the component is re-rendered. This is a fairly inexpensive operation, but you can see how often it’s triggered by clicking around on these cells. If this were a more expensive operation, it could have a significant impact. Similarly, each time you click the &lt;strong&gt;Change Columns&lt;/strong&gt; button, the &lt;code&gt;columnDefs&lt;/code&gt; prop on the AG Grid component is updated with a similar (though not identical) value. As a side effect of this, the object that defines the column’s coloring gets recreated each time this happens with a random color:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5P4gfuws--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/anim-unoptimized-app.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5P4gfuws--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/anim-unoptimized-app.gif" alt="How to Optimize a React Application Using Hooks and AG Grid" width="698" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing the Grid
&lt;/h2&gt;

&lt;p&gt;In the next section, you’ll learn a few techniques that you can use to optimize your application and reduce some of the unnecessary re-renders. After the optimizations, you can run the profiler again while performing the same actions listed above. This will give you clear data showing what impact the optimizations had. Before proceeding, you may want to download the data in this profile for future comparison. You can do this by clicking the &lt;strong&gt;down arrow icon&lt;/strong&gt; in the top left corner:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Adf_1717--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/save-profile-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Adf_1717--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/save-profile-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="324" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Memoized Components
&lt;/h3&gt;

&lt;p&gt;If you haven’t already done so, open the cloned codebase in your editor of choice. The first optimization to look at relates to the custom cell renderers. The counter included in this component increments each time it is re-rendered, but essentially all of these re-renders are wasted because the cell’s content doesn’t change. You can alleviate this issue with the &lt;a href="https://reactjs.org/docs/react-api.html#reactmemo"&gt;&lt;code&gt;React.memo&lt;/code&gt;&lt;/a&gt; Higher-Order Component (HOC) that wraps your components and essentially returns the previously computed value if none of the inputs changed.&lt;/p&gt;

&lt;p&gt;Begin by opening the file located at &lt;code&gt;src/components/name-formatter.jsx&lt;/code&gt;, which is currently just a normal Function Component. To stop it from needlessly re-computing its output, all you need to do is wrap it in the HOC like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as React from 'react';

const NameFormatter = React.memo(({ value }) =&amp;gt; {
  const renderCountRef = React.useRef(1);
  return (
    &amp;lt;strong&amp;gt;
    {`(${renderCountRef.current++}) ${value}`}
    &amp;lt;/strong&amp;gt;
  );
});

export default NameFormatter;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Please note: It’s inadvisable to simply wrap all of your components with this HOC. There is a time and a place for it, but generally, if you have a component that renders often and renders deterministically, that is, for a given set of input props, it will always give the same output. Wrapping it with &lt;code&gt;React.memo&lt;/code&gt; can help you eliminate some excess renders.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the application has reloaded, save your changes, and then click on the &lt;strong&gt;First_name&lt;/strong&gt; cells. You should find that doing so no longer causes the counters to increment. React simply uses the previously returned value rather than rendering a newly computed value because the input props don’t change:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Vlp0K86--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-renderer.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Vlp0K86--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-renderer.gif" alt="How to Optimize a React Application Using Hooks and AG Grid" width="698" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching Expensive Values
&lt;/h3&gt;

&lt;p&gt;You may have noticed that when you interact with the unoptimized Grid and click the &lt;strong&gt;Change Columns&lt;/strong&gt; button, the rows in the table change. This data is randomly generated with a library called &lt;code&gt;@faker-js/faker&lt;/code&gt;. In real life, this data would likely be coming from an API endpoint. For simplicity, this data generator is being used in favor of a real API. However, the principle behind this optimization remains the same.&lt;/p&gt;

&lt;p&gt;In this instance, the value produced by the fake data generator is not being preserved when the Grid component re-renders. Each time input props change, all of the data is regenerated. If this was an API call, it would likely be making network requests each time the props change instead. This behavior is not optimal because of its impact on performance and, in most cases, it wastes resources. Typically, it would be better to cache this value and reuse it between renders. There are some cases where you might want to regenerate or re-fetch the data, but this should be done deliberately and not as a side effect of poorly optimized code.&lt;/p&gt;

&lt;p&gt;There are a few different React hooks that you can use to cache your data, depending on the scenario. For reference, the current unoptimized implementation in &lt;code&gt;src/components/grid.jsx&lt;/code&gt; has the data generator function being called without any hooks, so it will be called on each render:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Unoptimized
function Grid({ columnDefs, defaultColDef }) {
  // This will be called on each render  
  const data = getData(10);

  return (
    &amp;lt;div className="ag-theme-alpine" style={{ height: '98vh' }}&amp;gt;
    &amp;lt;AgGridReact
        maintainColumnOrder
        defaultColDef={defaultColDef}
        rowData={data}
        columnDefs={columnDefs}
    /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;p&gt;If your data is coming from a synchronous function, as is the case here, you can wrap the value with the &lt;code&gt;React.useMemo&lt;/code&gt; hook. This behaves similarly to the aforementioned &lt;code&gt;React.memo&lt;/code&gt;, but rather than being a HOC, it’s a hook that you can apply to values other than just components and its approach looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Grid({ columnDefs, defaultColDef }) {
  // This value will now persist between renders
  const data = React.useMemo(() =&amp;gt; getData(10), []);

  return (
    &amp;lt;div className="ag-theme-alpine" style={{ height: '98vh' }}&amp;gt;
    &amp;lt;AgGridReact
        maintainColumnOrder
        defaultColDef={defaultColDef}
        rowData={data}
        columnDefs={columnDefs}
    /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;p&gt;This approach works well for functions like &lt;code&gt;getData&lt;/code&gt; here, but does not work so well for asynchronous operations like API calls. In such cases, you can instead use a combination of &lt;code&gt;React.useState&lt;/code&gt; and &lt;code&gt;React.useEffect&lt;/code&gt; to asynchronously call the API, and set the value into a state hook when it resolves. That approach looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Grid({ columnDefs, defaultColDef }) {
  const [data, setData] = React.useState([]);

  // This effect will be invoked the first time the component renders
  React.useEffect(() =&amp;gt; {
    (async () =&amp;gt; {
            // This value will be persisted between renders
    setData(getData(10));
    })();
  }, []);

  return (
    &amp;lt;div className="ag-theme-alpine" style={{ height: '98vh' }}&amp;gt;
    &amp;lt;AgGridReact
        maintainColumnOrder
        defaultColDef={defaultColDef}
        rowData={data}
        columnDefs={columnDefs}
    /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that while &lt;code&gt;getData&lt;/code&gt; is not an asynchronous function, this pattern works here and can be used with asynchronous API calls. Deciding which pattern is best for you will depend on the specifics of your application; however, between &lt;code&gt;React.useState&lt;/code&gt;, &lt;code&gt;React.useEffect&lt;/code&gt;, and &lt;code&gt;React.useMemo&lt;/code&gt;, you should be able to derive a solution for most scenarios. It’s a good idea to &lt;a href="https://reactjs.org/docs/hooks-intro.html"&gt;familiarize yourself with React Hooks&lt;/a&gt; if you have not already done so, as they have some rather nuanced behavior but are generally quite powerful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After applying either of these optimizations (the &lt;code&gt;useMemo&lt;/code&gt; approach or the &lt;code&gt;useEffect&lt;/code&gt; approach), you will discover that the rows themselves no longer change when you click &lt;strong&gt;Change Columns&lt;/strong&gt;. The data is now persisted between renders:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Z6ZSbhT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-row.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Z6ZSbhT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-row.gif" alt="How to Optimize a React Application Using Hooks and AG Grid" width="698" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next optimization addresses the random colors being assigned to the &lt;strong&gt;Id&lt;/strong&gt; column.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extracting Static Values
&lt;/h3&gt;

&lt;p&gt;If you look at &lt;code&gt;src/app.jsx&lt;/code&gt;, you’ll see the following block of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const updateColumns = () =&amp;gt; {
    setColumnDefs([
    { field: 'id', cellStyle: { background: randomColor() } },
    { field: 'first_name', cellRenderer: NameFormatter },
    { field: 'last_name' },
    { field: 'email' },
    { field: 'gender' },
    { field: 'ip_address' },
    ]);
  };

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

&lt;/div&gt;



&lt;p&gt;The first object in this array has a property called &lt;code&gt;cellStyle&lt;/code&gt;. This property contains a CSS style object that will be applied to all of the cells in this column. In this instance, the value of this property is dynamically computed each time the &lt;code&gt;updateColumns&lt;/code&gt; function is called, which is why the column color changes each time you click the &lt;strong&gt;Change Columns&lt;/strong&gt; button. This is a contrived example to demonstrate that passing objects by value like this results in a new instance of the object being created each time and is not desirable behavior. Even if the column definitions were to change, it’s unnecessary to recompute all of the values for all of their properties. You can eliminate the color-changing behavior of the column by making the following optimizations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 1. Extract the value of the cellStyle property to outside of the App component
const cellStyle = { background: randomColor() };

function App() {
…
// 2. Update the updateColumns function to use this extracted value
  const updateColumns = () =&amp;gt; {
    setColumnDefs([
    { field: 'id', cellStyle },
    { field: 'first_name', cellRenderer: NameFormatter },
    { field: 'last_name' },
    { field: 'email' },
    { field: 'gender' },
    { field: 'ip_address' },
    ]);
  };

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

&lt;/div&gt;



&lt;p&gt;Now, whenever &lt;code&gt;updateColumns&lt;/code&gt; is called, although the &lt;code&gt;columnDefs&lt;/code&gt; prop will still change, the style object applied to the &lt;strong&gt;Id&lt;/strong&gt; column will remain consistent, thus eliminating the random color changes. It should be noted that it will still change color after the first time you click the &lt;strong&gt;Change Columns&lt;/strong&gt; button, as the initial value supplied to the &lt;code&gt;useState&lt;/code&gt; hook does not have a &lt;code&gt;cellStyle&lt;/code&gt; supplied for that column.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4hMJBEKg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-app.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4hMJBEKg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-app.gif" alt="How to Optimize a React Application Using Hooks and AG Grid" width="698" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Profiling the Optimized Grid
&lt;/h2&gt;

&lt;p&gt;With these optimizations applied, you can see that AG Grid’s behavior is perceptibly less erratic. Now it’s time to profile the application again to see if these optimizations have a measurable impact. To do this, run through the same actions taken during the first measurements, listed here again for your convenience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start the profiler recording&lt;/li&gt;
&lt;li&gt;For each of the first four cells in the &lt;strong&gt;First_name&lt;/strong&gt; column, click on the cell once&lt;/li&gt;
&lt;li&gt;Next, click the &lt;strong&gt;Change Columns&lt;/strong&gt; button four times&lt;/li&gt;
&lt;li&gt;Stop the profiler recording&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After stopping the profiler, you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k9pLvjLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-profile-opt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k9pLvjLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/optimized-profile-opt.png" alt="How to Optimize a React Application Using Hooks and AG Grid" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will note that the number of commits is down to twelve (from the original twenty-six), and there is only one large spike this time (instead of four), which corresponds with the one time the &lt;code&gt;cellStyle&lt;/code&gt; changes. The optimizations have had a respectable impact on the render performance of this application.&lt;/p&gt;

&lt;p&gt;Understanding how and why these optimizations work puts you in a position to apply them at your discretion. Not every application will be as simple as this contrived example, but the patterns used here are generally applicable in many real-world scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Increasing the Amount of Data
&lt;/h2&gt;

&lt;p&gt;In a real application, you will likely be dealing with much larger volumes of data than the ten rows in this demo application. To ensure these optimizations hold up under load, you can easily tweak the random data generator call found in &lt;code&gt;src/components/grid.jsx&lt;/code&gt; to generate 100,000 rows of data or more. To do this, tweak the &lt;code&gt;useEffect&lt;/code&gt; block this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  React.useEffect(() =&amp;gt; {
    (async () =&amp;gt; {
    setData(getData(100000));
    })();
  }, []);

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

&lt;/div&gt;



&lt;p&gt;Now, if you save and reload the page, you should see a lot more data. You can run the profiler and step through the actions again, but you will likely find that there isn’t any perceptible performance difference. This is largely thanks to optimizations built into AG Grid, including virtualization.&lt;/p&gt;

&lt;p&gt;A source of performance issues for many browsers is the DOM. When the DOM has too many nodes (such as 100,000 table rows), it’s easy for performance to suffer if these nodes exhibit any kind of complexity beyond being simple text containers. One of the most common ways to solve this is through &lt;a href="https://www.ag-grid.com/react-data-grid/dom-virtualisation/"&gt;DOM Virtualization&lt;/a&gt;, where only the visible elements are rendered. As the user scrolls, React will render new elements as they come into view, and old elements will be removed once they are no longer visible. You can see this in practice using React DevTools.&lt;/p&gt;

&lt;p&gt;Aside from the profiler, there is also a &lt;strong&gt;Components&lt;/strong&gt; tab that you can access. This tab will show you all of the React components rendered on the page and details about them. If you navigate to this view and scroll down the Grid, you will notice that the number of row components doesn’t increase or decrease much (there is slight fluctuation when rows are half visible), but the rows themselves change. This is virtualization in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LLJI6l8_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/dom-virtualization-opt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LLJI6l8_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://blog.ag-grid.com/content/images/2022/05/dom-virtualization-opt.gif" alt="How to Optimize a React Application Using Hooks and AG Grid" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, you’ve seen how poorly optimized code can have a detrimental impact on the render performance of your application. You have also learned how to leverage React Hooks to apply optimizations to reduce this impact. It’s important to be aware of optimizations like these to avoid making such mistakes in your applications. Those mistakes can lead to unnecessary re-renders even when using highly optimized libraries like &lt;a href="https://www.ag-grid.com"&gt;AG Grid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Aside from DOM virtualization, AG Grid applies numerous optimizations internally to ensure that there are no wasted renders from the library itself. Keenly optimized performance is just one of the benefits. AG Grid also has numerous powerful features, from handling massive amounts of data to livestreaming data updates and integrated charting. If you’re looking for a robust all-in-one Data Grid solution, and performance is a must, give &lt;a href="https://www.ag-grid.com"&gt;AG Grid&lt;/a&gt; a try.&lt;/p&gt;

&lt;p&gt;All the code samples in this article can be found in this &lt;a href="https://github.com/cpave3/ag-grid-optimizations"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>howto</category>
      <category>aggrid</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>
  </channel>
</rss>
