Multiple invocations of the callback function in an Angular5 template binding

In trying to create a grid component that uses structured data containing definitions for columns and an array of data.

Each column definition includes a callback function to customize the display of that specific column's value.

Within each callback, a console.log() is called to track how many times the callback function is executed.

I'm puzzled as to why the callback function is initially called four times, and then only two times after the changeSort() event is triggered. Can anyone shed some light on this?

The table component I have written looks like this:

import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'app-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.css']
})
export class TableComponent implements OnInit {

  public grid: any;

  constructor() {
    this.grid = {
      columns: [],
      data: [],
    };
  }

  ngOnInit() {
    this.grid = {
      data: [
        {
          desc: 'hello 1',
          header: 'my header 1'
        },
        {
          desc: 'hello 2',
          header: 'my header 2'
        },
        {
          desc: 'hello 3',
          header: 'my header 3'
        }
      ],
      columns: [
        {
          title: 'Description',
          field: 'desc',
          sortable: false,
          callback: (value) => this.myCallback1(value),
        },
        {
          title: 'Header',
          field: 'header',
          sortable: true,
          callback: (value) => this.myCallback2(value),
        },
      ],
    };
  }

  public changeSort(field) {
    console.log(field);
  }

  public myCallback1(value) {
    console.log('myCallback', value
    return value + ' mc1';
  }

  public myCallback2(value) {
    console.log('myCallback2', value);
    return value + ' mc2';
  }

}

Here is the template for this component :

<div class="table-responsive">
  <table class="table table-striped table-sm">
    <thead>
    <tr>
      <th>#</th>
      <th *ngFor="let col of grid.columns">
        <span (click)="changeSort(col)" *ngIf="col.sortable">{{col.title}}</span>
        <span *ngIf="!col.sortable">{{col.title}}</span>
      </th>
    </tr>
    </thead>
    <tbody>
    <tr *ngFor="let row of grid.data; let i = index">
      <td>{{i+1}}</td>
      <td *ngFor="let col of grid.columns">{{col.callback ? col.callback(row[col.field]) : row[col.field]}}</td>
    </tr>
    </tbody>
  </table>
</div>

This is the error log at the beginning:

myCallback hello 1
myCallback2 my header 1
myCallback hello 2
myCallback2 my header 2
myCallback hello 3
myCallback2 my header 3
myCallback hello 1
myCallback2 my header 1
myCallback hello 2
myCallback2 my header 2
myCallback hello 3
myCallback2 my header 3
myCallback hello 1
myCallback2 my header 1
myCallback hello 2
myCallback2 my header 2
myCallback hello 3
myCallback2 my header 3
myCallback hello 1
myCallback2 my header 1
myCallback hello 2
myCallback2 my header 2
myCallback hello 3
myCallback2 my header 3

Answer №1

Angular manages the connection between the model and the dom, which includes the component and template file. To accomplish this, the application goes through a change detection cycle to identify any alterations in values, updating the dom accordingly.

An issue arises when there are functions present in the template file; each cycle prompts Angular to execute these functions in order to check for changes in the 'value'.

For instance, if a simple get function is used that merely returns a value, Angular still needs to invoke it to verify any modifications.

{{ myValue() }} // found in the template file

myValue() { return 10 } // located in the component

In such cases, it is logical for Angular to call these functions during every cycle to ensure accurate tracking of value changes.

An alternate solution (for those who prefer to avoid frequent function calls by Angular) involves implementing a ChangeDetectionStrategy (https://angular.io/api/core/ChangeDetectionStrategy). This approach allows you to direct Angular on how updates should be managed and when the cycle should be initiated (specifically for that component).

To achieve this within the component metadata, include the following:

@Component({
  selector: 'app-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush // newly added line
})

Additionally, in the constructor:

constructor(private changeDetectorRef: ChangeDetectorRef) {}

Upon making any modifications, you can then utilize this.changeDetectorRef.markForCheck(); to manually initiate the cycle for that specific component and update the dom accordingly.

I highly recommend further exploration into this topic as it encompasses a wide array of details beyond the scope of this post.

Answer №2

When utilizing a template binding that invokes a method (in this case col.callback(row[col.field]) ), the method will execute each time something triggers your component's change-detection process (Angular renders your component's HTML and subsequently calls all bound methods). As a result, console logs will be displayed. Furthermore, since the method is invoked within an ngFor template, it gets executed for every item within it.

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

Ways to retrieve a json file within Angular4

Seeking guidance on accessing the data.json file within my myservice.service.ts file. Any suggestions on how to accomplish this task? Overview of directory structure https://i.stack.imgur.com/WiQmB.png Sample code from myservice.service.ts file ht ...

The struggle of accessing child components using ViewChild in Angular

I am facing an issue with a dialog box that is supposed to display a child component separately. Below is the code for the child component: @Component({ selector: 'userEdit', templateUrl: './edituser.component.html', styleUrls: [ ...

Ways to prevent encountering the "ERROR: Spec method lacks expectations" message despite achieving success

My Angular HTTP service is responsible for making various HTTP calls. Below is a snippet of the service implementation: import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable() expor ...

How can you exclude templates in an Angular 7 CLI build to incorporate MVC views?

Currently, I am exploring the latest updates in Angular (7+) and CLI. After completing parts of the 'Tour of Heroes' tutorial, I have been able to use 'ng build' to generate production files. However, a key requirement for me is using ...

The dynamic dropdowns in FormArray are experiencing issues with loading data correctly

Having trouble fetching data for selected country states using FormArray Index. The API keeps getting called every time the country code is passed to retrieve the data. Here's what I've tried, <form [formGroup]='formName'> ...

The transition() function in Angular 2.1.0 is malfunctioning

I am struggling to implement animations in my Angular 2 application. I attempted to use an example I found online and did some research, but unfortunately, I have not been successful. Can anyone please assist me? import {Component, trigger, state, anima ...

Add the onclick() functionality to a personalized Angular 4 directive

I'm facing an issue with accessing the style of a button in my directive. I want to add a margin-left property to the button using an onclick() function in the directive. However, it doesn't seem to be working. Strangely, setting the CSS from the ...

Setting up a .NetCore3.1 API on LinuxCentos with Nginx

Currently, I am facing an issue while attempting to deploy a .NET Core 3.1 API + Angular application on Linux Centos. The application runs fine on the server; however, the browser fails to load it properly. Surprisingly, when I publish the same project for ...

Understanding 'this' in ChartJS within an Angular application

Here is my event handler for chartJS in Angular that I created: legend: { onClick: this.toggleLegendClickHandler After changing the text of the y scale title, I need to update the chart. I am looking to accomplish this by calling this._chart.cha ...

TS7016: No declaration file was found for the module named 'rxjs'

I recently updated my Angular App dependencies and successfully installed them. However, I am now facing an issue with 'rxjs'. The IDE returned the following error: TS7016: Could not find a declaration file for module 'rxjs'.'C:/ ...

Packaging an Angular project without the need for compiling

In order to enhance reusability, I structured my Angular 6 Project into multiple modules. These modules have a reference to a ui module that contains all the style sheets (SASS) as values such as: $primary-text-color: #dde7ff !default; Although this app ...

Error: Uncaught TypeError - The function indexOf is not defined for e.target.className at the mouseup event in HTMLDocument (translator.js:433) within the angular

Upon clicking on an SVG to edit my data in a modal bootstrap, I encountered the following error: Uncaught TypeError: e.target.className.indexOf is not a function at HTMLDocument.mouseup (translator.js:433) This is my SVG code: <svg data-dismiss ...

Upgrading to Angular 2: Utilizing ElementRef in ES5

Currently, I am facing a challenge in creating an Attribute directive for Angular 2 that would allow me to set multiple default HTML attributes using a single custom attribute. My intention is to apply this directive specifically to the input element. Howe ...

Tips for uploading a file and submitting form data with Angular2, using [(ngModel)], and then storing the reference in MongoDB

Currently, I am working on a task page and I need to implement the functionality to upload a file along with the form submission to the NodeJs express server. @Component({ selector: 'tasks', template: `<div mdl class="mdl-grid demo-c ...

Executing the cucumberjs + playwright tests after starting the angular app using the ng serve command

Our testing process involves using cucumberjs and playwright. Is it possible to initiate Angular with ng serve (using test configuration) before running our tests, and then close the application once the tests are complete? Similar to configuring a web s ...

How to Incorporate and Utilize Untyped Leaflet JavaScript Plugin with TypeScript 2 in Angular 2 Application

I have successfully integrated the LeafletJS library into my Angular 2 application by including the type definition (leaflet.d.ts) and the leaflet node module. However, I am facing an issue while trying to import a plugin for the Leaflet library called "le ...

Strategies for ensuring that code does not execute in Angular until the API response has been received

Currently, I am facing an issue where I need to wait for data from an API in order to set the value of a variable and use it in an if condition. The problem lies in my uncertainty about how to properly handle this asynchronous task using async and await. ...

Having trouble reloading a seekbar (input range) in Angular 7 with a function?

I am currently in the process of developing a music player using Angular 7, and below is the HTML code for my component: <div class="track-controller"> <small>{{musicPlayerService.getCurrentTime()}}</small> <div class="progress- ...

A step-by-step guide for updating a minor version of Angular with Angular CLI

I've been searching online for the answer to this straightforward question, but can't seem to find it anywhere... In my angular 4 project (made with angular cli), I want to utilize the newly introduced http interceptors in version 4.3. Could so ...

Displayed even when data is present, the PrimeNg empty message persists

I have set up a PrimeNg table to display data with an empty message template like this: <ng-template pTemplate="emptymessage"> <tr> <td> No records found </td> </tr> </ng-template> ...