ExpressionChangedAfterItHasBeenCheckedError - 递增一个数字并在视图中显示该数字
ExpressionChangedAfterItHasBeenCheckedError - incrementing a number and showing the number in view
查看 Stackblitz here。
我想显示 callCount
的实时显示,因为 rowCallback()
在检查每个 Kendo 网格单元格时被调用。
我希望最终的 callCount
数字为 12(即 3 行 x 4 个单元格,每行 = 12)。
我遇到了典型的 ExpressionChangedAfterItHasBeenCheckedError
错误。 Telerik 网站上没有提到任何有用的内容:https://www.telerik.com/kendo-angular-ui/components/grid/api/GridComponent/#toc-rowclass
方便代码:
import { Component, ViewEncapsulation } from '@angular/core';
import { RowClassArgs } from '@progress/kendo-angular-grid';
@Component({
selector: 'my-app',
encapsulation: ViewEncapsulation.None,
styles: [`
.k-grid tr.even { background-color: #f45c42; }
.k-grid tr.odd { background-color: #41f4df; }
`],
template: `
<kendo-grid [data]="gridData" [rowClass]="rowCallback">
</kendo-grid>
<div>{{callCount}}</div> <!-- Double bind to show the current number -->
`
})
export class AppComponent {
public gridData: any[] = products;
public callCount: number = 0;
public rowCallback = (context: any) => {
this.callCount++; // Incrementing the number
}
}
const products = [{
"ProductID": 1,
"ProductName": "Chai",
"UnitPrice": 18.0000,
"Discontinued": true
}, {
"ProductID": 2,
"ProductName": "Chang",
"UnitPrice": 19.0000,
"Discontinued": false
}, {
"ProductID": 3,
"ProductName": "Chang",
"UnitPrice": 28.0000,
"Discontinued": false
}
];
我认为您不应该使用 rowClass 方法来计算 kendo 网格中的单元格数。
如行Class documentation 中指定,您应该使用动态设置Css Class 名称。我认为您可以根据以下场景使用以下方法轻松实现您的目的:
案例 1: 当您知道要在 Kendo 网格上显示的列数时,您可以创建一个 属性 将保存它的值并在模板表达式中使用它,如下所示:
import { Component, ViewEncapsulation } from '@angular/core';
import { RowClassArgs } from '@progress/kendo-angular-grid';
@Component({
selector: 'my-app',
encapsulation: ViewEncapsulation.None,
styles: [`
.k-grid tr.even { background-color: #f45c42; }
.k-grid tr.odd { background-color: #41f4df; }
`],
template: `
<kendo-grid [data]="gridData">
</kendo-grid>
<div>{{ columnCount * gridData.length }}</div> <!-- Double bind to show the current number-->
`
})
export class AppComponent {
public gridData: any[] = products;
public callCount: number = 0;
public readonly columnCount = 4; // let say grid has 4 column (known prior)
}
const products = [{
"ProductID": 1,
"ProductName": "Chai",
"UnitPrice": 18.0000,
"Discontinued": true
}, {
"ProductID": 2,
"ProductName": "Chang",
"UnitPrice": 19.0000,
"Discontinued": false
}, {
"ProductID": 3,
"ProductName": "Chang",
"UnitPrice": 28.0000,
"Discontinued": false
}
];
Case-2: 当您不知道要在 Kendo 网格上显示的列数时,您可以在您的组件负载上动态计算它:
import { Component, ViewEncapsulation } from '@angular/core';
import { RowClassArgs } from '@progress/kendo-angular-grid';
@Component({
selector: 'my-app',
encapsulation: ViewEncapsulation.None,
styles: [`
.k-grid tr.even { background-color: #f45c42; }
.k-grid tr.odd { background-color: #41f4df; }
`],
template: `
<kendo-grid [data]="gridData">
</kendo-grid>
<div>{{ columnCount * gridData.length }}</div> <!-- Double bind to show the current number-->
`
})
export class AppComponent {
public gridData: any[] = products;
public callCount: number = 0;
public readonly columnCount = Object.keys(gridData);
}
const products = [{
"ProductID": 1,
"ProductName": "Chai",
"UnitPrice": 18.0000,
"Discontinued": true
}, {
"ProductID": 2,
"ProductName": "Chang",
"UnitPrice": 19.0000,
"Discontinued": false
}, {
"ProductID": 3,
"ProductName": "Chang",
"UnitPrice": 28.0000,
"Discontinued": false
}
];
在任何情况下,您都可以在呈现网格之前找到列数,并可以使用它来计算网格数。
您可以在 rowCallback
方法中使用 setTimeout
回调。避免 ExpressionChangedAfterItHasBeenCheckedError
错误。
public rowCallback = (context: any) => {
setTimeout(() => {
this.callCount++; // Incrementing the number
});
}
问题
如错误所述,您正在 Angular 生命周期结束后在视图中设置 属性 的值,(AfterContentHasBeenChecked)
考虑这种情况
You set the value of callCount = 0
, in your html you have <div> 0 </div>
The content is checked.
The 1st row is loaded
Now we update callCount = 1
and html <div> 1 </div>
This triggers change detection and hence the error
如果我们要实施上述方法,我们将必须通知 anguar 我们可以使用 ChangeDetectorRef
进行的此更改。现在让我们尝试用这个
来实现上面的内容
You set the value of callCount = 0
, in your html you have <div> 0 </div>
Trigger change detection
The content is checked.
The 1st row is loaded
Now we update callCount = 1
and html <div> 1 </div>
Trigger change detection
The 2nd row is loaded
Now we update callCount = 2
and html <div> 2 </div>
Trigger change detection
The 3rd row is loaded
Now we update callCount = 3
and html <div> 3 </div>
Trigger change detection
The 1st row is loaded
Now we update callCount = 4
and html <div> 4 </div>
Trigger change detection
The 2nd row is loaded
Now we update callCount = 5
and html <div> 5 </div>
Trigger change detection
The 3rd row is loaded
Now we update callCount = 6
and html <div> 6 </div>
Trigger change detection
...
This loop will never end and angular will throw error Maximum stack reached
以上是 angular 试图警告您的内容。说明你的做法有问题
解决方案
您需要一种方法来确定行数在检查内容之前
最简单的选择是计算产品数量并将其乘以每个项目中属性的最大值
import { BehaviorSubject, combineLatest, Observable, of } from "rxjs";
import { map } from "rxjs/operators";
// Create a new Observable using Behaviour subject to store current products
private gridDataSubject$ = new BehaviorSubject(products);
public gridData: Observable<any[]> = this.gridDataSubject$.asObservable();
// Extract the row count being length of the products array
private rowCount: Observable<number> = this.gridData.pipe(
map(({ length }) => length)
);
// Extract column count being the Maximum of the product with the highest number of properties
private columCount: Observable<number> = this.gridData.pipe(
map((data) => data.map(item => Object.keys(item).length)),
map(data => Math.max(...data))
);
// Define to total calls being rows X columns
public callCount: Observable<number> = combineLatest([this.columCount, this.rowCount]).pipe(map(([row, column]) => row * column))
public rowCallback = (context: any) => {};
updateValue() {
this.gridDataSubject$.next(products2);
}
public rowCallback = (context: any) => {};
updateValue() {
this.gridDataSubject$.next(products2)
}
在上面的代码中,我使用 BehaviorSubject
将数据存储为 Observable
。这样我们就可以使用 piping
和 map
运算符来获取产品的 length
属性(这实际上是 [=72= 的行数) ])
在html中,我们可以使用异步管道来订阅这个Observables
<button (click)='updateValue()'>Update</button>
<kendo-grid [data]="gridData | async" [rowClass]="rowCallback">
</kendo-grid>
<div>{{ callCount | async }}</div>
<!-- Double bind to show the current number -->
尝试点击按钮,这将更新 table 和行数
问题是您在 AfterViewChecked
之后更新视图
我看到的唯一解决方案是使用 ChangeDetectorRef:
export class AppComponent {
public gridData: any[] = products;
public callCount: number = 0;
public rowCallback = (context: any) => {
this.callCount++; // Incrementing the number
this.changeDetectorRef.detectChanges();
}
constructor(private changeDetectorRef: ChangeDetectorRef) {}
}
查看 Stackblitz here。
我想显示 callCount
的实时显示,因为 rowCallback()
在检查每个 Kendo 网格单元格时被调用。
我希望最终的 callCount
数字为 12(即 3 行 x 4 个单元格,每行 = 12)。
我遇到了典型的 ExpressionChangedAfterItHasBeenCheckedError
错误。 Telerik 网站上没有提到任何有用的内容:https://www.telerik.com/kendo-angular-ui/components/grid/api/GridComponent/#toc-rowclass
方便代码:
import { Component, ViewEncapsulation } from '@angular/core';
import { RowClassArgs } from '@progress/kendo-angular-grid';
@Component({
selector: 'my-app',
encapsulation: ViewEncapsulation.None,
styles: [`
.k-grid tr.even { background-color: #f45c42; }
.k-grid tr.odd { background-color: #41f4df; }
`],
template: `
<kendo-grid [data]="gridData" [rowClass]="rowCallback">
</kendo-grid>
<div>{{callCount}}</div> <!-- Double bind to show the current number -->
`
})
export class AppComponent {
public gridData: any[] = products;
public callCount: number = 0;
public rowCallback = (context: any) => {
this.callCount++; // Incrementing the number
}
}
const products = [{
"ProductID": 1,
"ProductName": "Chai",
"UnitPrice": 18.0000,
"Discontinued": true
}, {
"ProductID": 2,
"ProductName": "Chang",
"UnitPrice": 19.0000,
"Discontinued": false
}, {
"ProductID": 3,
"ProductName": "Chang",
"UnitPrice": 28.0000,
"Discontinued": false
}
];
我认为您不应该使用 rowClass 方法来计算 kendo 网格中的单元格数。
如行Class documentation 中指定,您应该使用动态设置Css Class 名称。我认为您可以根据以下场景使用以下方法轻松实现您的目的:
案例 1: 当您知道要在 Kendo 网格上显示的列数时,您可以创建一个 属性 将保存它的值并在模板表达式中使用它,如下所示:
import { Component, ViewEncapsulation } from '@angular/core'; import { RowClassArgs } from '@progress/kendo-angular-grid'; @Component({ selector: 'my-app', encapsulation: ViewEncapsulation.None, styles: [` .k-grid tr.even { background-color: #f45c42; } .k-grid tr.odd { background-color: #41f4df; } `], template: ` <kendo-grid [data]="gridData"> </kendo-grid> <div>{{ columnCount * gridData.length }}</div> <!-- Double bind to show the current number--> ` }) export class AppComponent { public gridData: any[] = products; public callCount: number = 0; public readonly columnCount = 4; // let say grid has 4 column (known prior) } const products = [{ "ProductID": 1, "ProductName": "Chai", "UnitPrice": 18.0000, "Discontinued": true }, { "ProductID": 2, "ProductName": "Chang", "UnitPrice": 19.0000, "Discontinued": false }, { "ProductID": 3, "ProductName": "Chang", "UnitPrice": 28.0000, "Discontinued": false } ];
Case-2: 当您不知道要在 Kendo 网格上显示的列数时,您可以在您的组件负载上动态计算它:
import { Component, ViewEncapsulation } from '@angular/core'; import { RowClassArgs } from '@progress/kendo-angular-grid'; @Component({ selector: 'my-app', encapsulation: ViewEncapsulation.None, styles: [` .k-grid tr.even { background-color: #f45c42; } .k-grid tr.odd { background-color: #41f4df; } `], template: ` <kendo-grid [data]="gridData"> </kendo-grid> <div>{{ columnCount * gridData.length }}</div> <!-- Double bind to show the current number--> ` }) export class AppComponent { public gridData: any[] = products; public callCount: number = 0; public readonly columnCount = Object.keys(gridData); } const products = [{ "ProductID": 1, "ProductName": "Chai", "UnitPrice": 18.0000, "Discontinued": true }, { "ProductID": 2, "ProductName": "Chang", "UnitPrice": 19.0000, "Discontinued": false }, { "ProductID": 3, "ProductName": "Chang", "UnitPrice": 28.0000, "Discontinued": false } ];
在任何情况下,您都可以在呈现网格之前找到列数,并可以使用它来计算网格数。
您可以在 rowCallback
方法中使用 setTimeout
回调。避免 ExpressionChangedAfterItHasBeenCheckedError
错误。
public rowCallback = (context: any) => {
setTimeout(() => {
this.callCount++; // Incrementing the number
});
}
问题
如错误所述,您正在 Angular 生命周期结束后在视图中设置 属性 的值,(AfterContentHasBeenChecked)
考虑这种情况
You set the value of
callCount = 0
, in your html you have<div> 0 </div>
The content is checked.
The 1st row is loaded
Now we updatecallCount = 1
and html<div> 1 </div>
This triggers change detection and hence the error
如果我们要实施上述方法,我们将必须通知 anguar 我们可以使用 ChangeDetectorRef
进行的此更改。现在让我们尝试用这个
You set the value of
callCount = 0
, in your html you have<div> 0 </div>
Trigger change detection The content is checked.
The 1st row is loaded
Now we updatecallCount = 1
and html<div> 1 </div>
Trigger change detection The 2nd row is loaded
Now we updatecallCount = 2
and html<div> 2 </div>
Trigger change detection The 3rd row is loaded
Now we updatecallCount = 3
and html<div> 3 </div>
Trigger change detection The 1st row is loaded
Now we updatecallCount = 4
and html<div> 4 </div>
Trigger change detection The 2nd row is loaded
Now we updatecallCount = 5
and html<div> 5 </div>
Trigger change detection The 3rd row is loaded
Now we updatecallCount = 6
and html<div> 6 </div>
Trigger change detection
...
This loop will never end and angular will throw errorMaximum stack reached
以上是 angular 试图警告您的内容。说明你的做法有问题
解决方案
您需要一种方法来确定行数在检查内容之前
最简单的选择是计算产品数量并将其乘以每个项目中属性的最大值
import { BehaviorSubject, combineLatest, Observable, of } from "rxjs";
import { map } from "rxjs/operators";
// Create a new Observable using Behaviour subject to store current products
private gridDataSubject$ = new BehaviorSubject(products);
public gridData: Observable<any[]> = this.gridDataSubject$.asObservable();
// Extract the row count being length of the products array
private rowCount: Observable<number> = this.gridData.pipe(
map(({ length }) => length)
);
// Extract column count being the Maximum of the product with the highest number of properties
private columCount: Observable<number> = this.gridData.pipe(
map((data) => data.map(item => Object.keys(item).length)),
map(data => Math.max(...data))
);
// Define to total calls being rows X columns
public callCount: Observable<number> = combineLatest([this.columCount, this.rowCount]).pipe(map(([row, column]) => row * column))
public rowCallback = (context: any) => {};
updateValue() {
this.gridDataSubject$.next(products2);
}
public rowCallback = (context: any) => {};
updateValue() {
this.gridDataSubject$.next(products2)
}
在上面的代码中,我使用 BehaviorSubject
将数据存储为 Observable
。这样我们就可以使用 piping
和 map
运算符来获取产品的 length
属性(这实际上是 [=72= 的行数) ])
在html中,我们可以使用异步管道来订阅这个Observables
<button (click)='updateValue()'>Update</button>
<kendo-grid [data]="gridData | async" [rowClass]="rowCallback">
</kendo-grid>
<div>{{ callCount | async }}</div>
<!-- Double bind to show the current number -->
尝试点击按钮,这将更新 table 和行数
问题是您在 AfterViewChecked
之后更新视图我看到的唯一解决方案是使用 ChangeDetectorRef:
export class AppComponent {
public gridData: any[] = products;
public callCount: number = 0;
public rowCallback = (context: any) => {
this.callCount++; // Incrementing the number
this.changeDetectorRef.detectChanges();
}
constructor(private changeDetectorRef: ChangeDetectorRef) {}
}