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