Angular5 template binding, callback function called more than once

懵懂的女人 提交于 2019-12-09 13:37:06

问题


I tried to implement a grid component with a structured data containing columns definition and its data array.

There is a callback function in the definition of each column, to customize displaying the value of that column.

Inside of each callback, it called a console.log() to show me how many times the callback function will be called.

I don't know why callback function called four times in the beginning, and two times after changeSort() event fired !! Please let me know.

I wrote the following table component:

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

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

  // @Input() public grid: {
  //   columns: any[],
  //   data: any[]
  // };

  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';
  }

}

And its template is this :

<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>

Here is log error 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

回答1:


Angular handles the binding between the model and the dom (component and template file). To do this the application ticks (change detection cycle) and checks for if any values that have changed, and if so -> Update the dom.

The problem is when you have functions in your template file, and each cycle angular will call the function to check if the 'value' has changed.

For example, if I had a simple get function which only returned a value, Angular needs to run it to check that there is actually a change.

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

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

Here it makes sense that angular must call the function each to check if the value has changed.

A solution (if you don't want Angular to be constantly calling your functions), is to implement a ChangeDectionStrategy (https://angular.io/api/core/ChangeDetectionStrategy). With this, you can tell Angular that you will handle the updates, and when it should run the cycle (for only that component).

To do so within the component metadata you will add the following:

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

Then in the constructor:

constructor(private changeDetectorRef: ChangeDetectorRef) {}

Whenever you make a change, you can then call this.changeDetectorRef.markForCheck(); which will manually run the cycle for this component and update the dom when needed.

I really suggest reading into this more as the topic is too broad to describe in this one post




回答2:


when you have a template binding that calls a method (which here is col.callback(row[col.field]) ), the method will be called everytime something triggers your component's change-detection (angular renders your component's html and hence, calls all those bound methods). and because of that you'll see the console logs. and because the method is called inside an ngFor's template, it's called for each item of it.



来源:https://stackoverflow.com/questions/48960408/angular5-template-binding-callback-function-called-more-than-once

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!