{"id":815,"date":"2025-05-02T11:33:44","date_gmt":"2025-05-02T11:33:44","guid":{"rendered":"https:\/\/www.cmarix.com\/qanda\/?p=815"},"modified":"2026-02-05T12:06:39","modified_gmt":"2026-02-05T12:06:39","slug":"angular-component-update-external-dom","status":"publish","type":"post","link":"https:\/\/www.cmarix.com\/qanda\/angular-component-update-external-dom\/","title":{"rendered":"After a third-party library updates the DOM (like a chart or modal), How to fix component changes detection?"},"content":{"rendered":"\n<p>In Angular, the framework uses Zone.js to track and react to asynchronous operations. Skilled Angular developers know this enables Angular-based apps to detect changes based on updates the view gets on model changes.<\/p>\n\n\n\n<p>However, most third-party libraries that are not optimized for Angular might manipulate the DOM directly, which results in Angular being unaware of any changes. This results in the view not getting updated properly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Problem Overview<\/h2>\n\n\n\n<p><strong>Angular runs change detection when:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Events like clicks or input occur<\/li>\n\n\n\n<li>Timers (setTimeout, setInterval) fire<\/li>\n\n\n\n<li>HTTP calls complete<\/li>\n\n\n\n<li>Promises resolve<\/li>\n<\/ul>\n\n\n\n<p>These are wrapped by Zone.js. But if a third-party library manipulates the DOM outside of Angular&#8217;s Zone, Angular won&#8217;t detect any need to refresh the UI.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Solution: NgZone or ChangeDetectorRef<\/h2>\n\n\n\n<p>Angular provides tools to manually trigger change detection:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>NgZone \u2013 Re-enters Angular&#8217;s zone after an external operation.<\/li>\n\n\n\n<li>ChangeDetectorRef.detectChanges() \u2013 Triggers change detection explicitly.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Example: Chart Library Updating DOM<\/h4>\n\n\n\n<p>Let\u2019s say you\u2019re using a third-party charting library (like Chart.js or ApexCharts), and after rendering a chart, you want to show a message or toggle a flag in the component.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Problem Code<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>@Component({\n  selector: 'app-chart',\n  template: `\n    &lt;div id=\"chart-container\">&lt;\/div>\n    &lt;p *ngIf=\"chartRendered\">Chart rendered!&lt;\/p>\n  `\n})\nexport class ChartComponent implements OnInit {\n  chartRendered = false;\n\n  ngOnInit() {\n    thirdPartyRenderChart('#chart-container', () => {\n      this.chartRendered = true; \/\/ DOM changes outside Angular\n      \/\/ Angular won\u2019t detect this change!\n    });\n  }\n}<\/code><\/pre>\n\n\n\n<p>Angular won&#8217;t detect the chartRendered = true change because the callback is executed outside its zone.<\/p>\n\n\n\n<p><strong>Fix 1: Using NgZone.run()<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { Component, NgZone, OnInit } from '@angular\/core';\n\n@Component({\n  selector: 'app-chart',\n  template: `\n    &lt;div id=\"chart-container\">&lt;\/div>\n    &lt;p *ngIf=\"chartRendered\">Chart rendered!&lt;\/p>\n  `\n})\nexport class ChartComponent implements OnInit {\n  chartRendered = false;\n\n  constructor(private ngZone: NgZone) {}\n\n  ngOnInit() {\n    thirdPartyRenderChart('#chart-container', () => {\n      this.ngZone.run(() => {\n        this.chartRendered = true; \/\/ Now Angular will detect it!\n      });\n    });\n  }\n}<\/code><\/pre>\n\n\n\n<p>NgZone.run() ensures that the code runs <em>inside Angular\u2019s zone<\/em>, so change detection is triggered.<\/p>\n\n\n\n<p><strong>Fix 2: Using ChangeDetectorRef.detectChanges()<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { Component, ChangeDetectorRef, OnInit } from '@angular\/core';\n\n@Component({\n  selector: 'app-chart',\n  template: `\n    &lt;div id=\"chart-container\">&lt;\/div>\n    &lt;p *ngIf=\"chartRendered\">Chart rendered!&lt;\/p>\n  `\n})\nexport class ChartComponent implements OnInit {\n  chartRendered = false;\n\n  constructor(private cdr: ChangeDetectorRef) {}\n\n  ngOnInit() {\n    thirdPartyRenderChart('#chart-container', () => {\n      this.chartRendered = true;\n      this.cdr.detectChanges(); \/\/ Manually trigger change detection\n    });\n  }\n}<\/code><\/pre>\n\n\n\n<p>Use this when you know Angular won&#8217;t auto-detect changes and you want fine-grained control.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">When to Use Which?<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Situation<\/strong><\/td><td><strong>Use<\/strong><\/td><\/tr><tr><td>DOM changes in callback from third-party lib<\/td><td>NgZone.run()<\/td><\/tr><tr><td>For delaying or controlling change detection manually<\/td><td>ChangeDetectorRef.detectChanges()<\/td><\/tr><tr><td>You&#8217;re doing batch DOM updates outside Angular<\/td><td>NgZone.runOutsideAngular() + NgZone.run()<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>Bonus: NgZone.runOutsideAngular()<\/strong><\/p>\n\n\n\n<p>Sometimes you intentionally avoid Angular change detection (e.g., rendering a large chart to improve performance), then re-enter Angular once done:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this.ngZone.runOutsideAngular(() => {\n  thirdPartyRenderChart('#chart', () => {\n    this.ngZone.run(() => {\n      this.chartRendered = true;\n    });\n  });\n});<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">To sum it all up<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Third-party libraries often modify the DOM outside Angular\u2019s zone.<\/li>\n\n\n\n<li>Angular can\u2019t identify or detect changes automatically in such cases.<\/li>\n\n\n\n<li>Use NgZone.run() to re-enter Angular&#8217;s zone and ensure change detection.<\/li>\n\n\n\n<li>Or use ChangeDetectorRef.detectChanges() for explicit detection.<\/li>\n\n\n\n<li>For performance, combine with runOutsideAngular().<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In Angular, the framework uses Zone.js to track and react to asynchronous operations. Skilled Angular developers know this enables Angular-based apps to detect changes based on updates the view gets on model changes. However, most third-party libraries that are not optimized for Angular might manipulate the DOM directly, which results in Angular being unaware of [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1001,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[7,3],"tags":[],"class_list":["post-815","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-angular","category-web"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/815","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/comments?post=815"}],"version-history":[{"count":10,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/815\/revisions"}],"predecessor-version":[{"id":948,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/815\/revisions\/948"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media\/1001"}],"wp:attachment":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media?parent=815"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/categories?post=815"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/tags?post=815"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}