The Angular performance may be impacted by the constant recalculation of ngStyle when clicking on various input fields

I am facing a frustrating performance issue.

Within my component, I have implemented ngStyle and I would rather not rewrite it. However, every time I interact with random input fields on the same page (even from another component), the ngStyle recalculates slowly.

For example, I want to create a table displaying the product of two numbers with dynamic background colors:

<section>
  <div class="row" 
       *ngFor="let row of rows">
    <div class="col" 
         [ngStyle]="{'background-color': getBG(row*col)}" 
         *ngFor="let col of cols ">
      {{row * col}}
    </div>
  </div>
</section>

However, when I add several input fields to the page:

<section>
  <input type="text" [ngModel]="model1"/>
  <input type="text"[ngModel]="model2"/>
  <input type="text"[ngModel]="model3"/>
  <input type="text"[ngModel]="model4"/>
  <input type="text"[ngModel]="model5"/>
</section>

Each click on these inputs triggers a call to getBG(), resulting in sluggish performance. This is evident even if the function simply returns a string without any complex calculations.

To see this issue in action, check out the Example at StackBlitz. Open the console and try clicking swiftly among different input fields or entering values - the responsiveness is notably lacking.


UPD1: My scenario involves a more intricate setup, and I already employ ChangeDetectionStrategy.OnPush. Even binding ngStyle directly to a value instead of a function does not significantly improve performance, as it remains slow and introduces complexity. Ideally, I seek a way to instruct ngStyle not to recalculate unless explicitly requested. Perhaps leveraging ChangeDetectorRef.detach() could provide some assistance.

Answer №1

It all adds up perfectly. This is the method by which Angular conducts change detection. Additionally, Angular executes extra checks when a function is invoked within one of the data-binding syntaxes, like so:

[ngStyle]="{'background-color': getBG(row*col)}"

Angular carries out Change Detection in three scenarios:

  1. DOM Events.
  2. AJAX Calls.
  3. Timeouts / Intervals.

This specific scenario involves DOM Events (click).

During Change Detection, Angular examines whether a certain variable in the Component has been altered.

This process is straightforward for properties but not as simple with functions.

The only way to determine if a function's value has changed is by calling it.

Hence, Angular performs this action.

SOLUTION:

To resolve this issue, establish a matrix in the Component Class that specifies the number to display and the color to use:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  rows = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  cols = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  matrix = [];

  model1 = '';
  model2 = '';
  model3 = '';
  model4 = '';
  model5 = '';

  ngOnInit() {
    this.rows.forEach((row, rowIndex) => {
      this.matrix.push([]);
      this.cols.forEach((col, colIndex) => {
        const product = row * col;
        this.matrix[row].push({
          numberToShow: product,
          color: this.getBG(product),
        });
      })
    });
  }

  getBG(hue: number): string {
    console.log('getBG was called');
    return 'hsl(' + hue + ', 100%, 50%)';
  }

}

Then, incorporate it into your template:

<br/>
<div> 1. Open a console</div>
<br/>

<section>
    <div class="row" *ngFor="let row of matrix">
        <div 
      class="col" 
      [style.background-color]="col.color" 
      *ngFor="let col of row ">
            {{col.numberToShow}}
        </div>
    </div>
</section>

<br/>
<div>2. Click fast on the different inputs: </div>
<br/>

<section>
    <input type="text" [ngModel]="model1"/>
  <input type="text"[ngModel]="model2"/>
  <input type="text"[ngModel]="model3"/>
  <input type="text"[ngModel]="model4"/>
  <input type="text"[ngModel]="model5"/>
</section>

Difference in the performance:

In the previous setup, getBG was triggered 401 times upon initialization.

However, with the new implementation, getBG is only called 101 times during initialization.

This results in a significant performance enhancement of approximately 397%.

Besides, there are no additional calls to the getBG method when the user interacts with input fields.

Feel free to explore a Live Example on StackBlitz. It could be beneficial for reference.

You may also wish to peruse my Medium Article about Improving Performance of Reactive Forms in Angular. While centered on Reactive Forms, the article covers related aspects as well. I trust you will find it valuable.

Answer №2

Two key factors contribute to the slow detection process. Firstly, the sluggishness of development tools and the excessive printing of messages can further delay the process.

Secondly, unnecessary work is being done which hinders efficiency. By segregating tasks into distinct parts, it becomes feasible to switch the changeDetection strategy to OnPush.


To illustrate this concept, consider the following simplified example:

@Component({
    selector: 'my-cell',
    template: '<div [ngStyle]="styles"><ng-content></ng-content></div>',
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CellComponent {
    @Input() styles: {
    readonly "background-color": string;
  };
}

and

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    // Implementation details...
}

// Additional code snippets...

The OnPush detection strategy ensures that any changes in the @Inputs of a Component/Directive will trigger detection. By separating resource-intensive tasks into separate directives and ensuring that their @Inputs only change when necessary, optimal performance can be achieved.


Explore the provided StackBlitz for a live example: https://stackblitz.com/edit/style-performance-of-a-grid-fzbzkz

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilize a function as a parameter

I am struggling to figure out how to make this function pass by reference in my code. Is there a way to achieve this? var Class = function() { var callback1; var callback2; function buildStuff(data, callback) { element.onclick = funct ...

Best method for removing CrosshairMove event listener in lightweight charts

As per the documentation, using unsubscribeCrosshairMove allows us to remove a handler that was previously added with subscribeCrosshairMove. Our objective is to use unsubscribe... to eliminate previous handlers before re-subscribing with subscribe... af ...

Typescript is facing an issue locating the declaration file

I'm encountering an issue with TypeScript not recognizing my declaration file, even though it exists. Can anyone provide insight into why this might be happening? Here is the structure of my project: scr - main.ts - dec.d.ts str-utils - index. ...

Changing the background color of the canvas using Javascript

I want to create a rect on a canvas with a slightly transparent background, but I don't want the drawn rect to have any background. Here is an example of what I'm looking for: https://i.stack.imgur.com/axtcE.png The code I am using is as follo ...

Troubleshooting problem with Shopify mailto tag

I am facing an issue with external links in my Shopify store. My app injects a script to display a bubble with an anchor tag that redirects users to a specified link. However, Shopify is altering the anchor tag to a different link, resulting in a 404 erro ...

Improprove the performance of an array of objects using JavaScript

Hello there, I am currently in the process of creating an array. this.data = [{ label: 'Total', count: details.request.length, }, { label: 'In-Progress', count: details.request.filter((obj) => obj.statusId === 0 || ob ...

Runtime Error: Invalid source property detected - Firebase and Next.js collaboration issue

Currently, I am developing a Next.js application that retrieves data from a Firestore database. The database connection has been successfully established, and the data is populating the app. However, I am facing an issue with displaying the image {marketpl ...

PHP data is not displayed by Ajax

I seem to be encountering a bit of trouble. I am attempting to utilize ajax to retrieve data from a PHP server, which in turn fetches it from a MySQL database, and then display it within a specific HTML tag location. However, for some unknown reason, nothi ...

Looping through an object with AngularJS's ng-repeat

Upon receiving an object as the scope, which has the following structure: The controller function is defined as follows: module.controller('ActiveController', ['$scope','$http', function($scope, $http) { $h ...

Unable to utilize jQuery's .append(data) function due to the need to use .val(append(data)) instead

I have been attempting to utilize JQuery .append(data) on success in order to change the value of an input to the appended data like this: .val(append(data)), but it doesn't seem to be working. Surprisingly, I can successfully change the value to a st ...

Having trouble retrieving input field values with Angular.js

I am struggling to access the input field values in my Angular.js application. Below is the code snippet I am using: <div class="input-group bmargindiv1 col-md-12"> <span class="input-group-addon ndrftextwidth text-right" style="width:180px"& ...

"Unleash the Power of Go HTTP Server for React, Angular, and

Recently, I developed a small HTTP Server in GO specifically for static files: func wrapHandler(h http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { h.ServeHTTP(srw, r) log.Printf("GET %s", r.RequestU ...

When logging off from an Angular2 application, the OIDC-client does not properly clear the cookies for the MVC application

I currently have an authorization server that is being used by both my Angular2 app and MVC webapp. In my Angular2 app, I've implemented authorization using the oidc-client JavaScript package. Everything works well except for the logout functionality ...

Can the name of the ngx-datatable-column be altered?

I recently started developing an angular2 web application that includes a ngx-datatable component. The header columns all have numeric names, but I would like to customize them in the view. Despite my efforts to research this issue, I haven't come ac ...

Issue encountered: Cannot locate module: Error message - Unable to find 'stream' in 'C:devjszip-test ode_modulesjsziplib' folder

I am encountering an issue in my angular 7 application while using jszip v3.2.1. During the project build process (e.g., running npm start), I receive the following error message: ERROR in ./node_modules/jszip/lib/readable-stream-browser.js Module not fo ...

Encountering difficulties in generating a binary from a nodejs application with pkg

I am having trouble generating a binary executable from my nodejs app using the pkg command. My nodejs app is simple and consists of only three .js files: index.js, xlsx_to_pdf.js, and xlsx_extractor.js. This is how my package.json file looks like: { & ...

Programmatically switch between show and hide using Angular Material

Is there a way to programmatically toggle between displaying and hiding attributes by clicking a button? For example, I have a card that includes both a map and a list view. Normally, these are shown side by side. However, on mobile devices, the list view& ...

Exploring Angular: How to Access HTTP Headers and Form Data from POST Request

I am currently working with an authentication system that operates as follows: Users are directed to a third-party login page Users input their credentials The website then redirects the user back to my site, including an auth token in a POST request. Is ...

When toggling visibility with JS, the Bootstrap Grid Row may not center-align as expected

Sorry for the odd spacing issue that might occur. I currently have a basic Bootstrap Grid setup as follows: <div class="row justify-content-center" align="center" id="details"> <div class="col-sm-2"> ...

Troublesome glitches in jQuery?

I'm having an issue with the following code: var buggy_brand_name = ['Baby Jogger', 'Babyzen', 'Bugaboo', 'GB', 'Icandy', 'Joie', 'Maclaren', 'Mamas&Papas', 'Ma ...