angular / angular/components

bug(cdk-table): Row differ (trackBy) does not detect row template changes

Đã đóng
#20,717 4 bình luận 1 reaction 0 người được giao Xem trên GitHub
area: cdk/table help wanted P3
Ngôn ngữ chính
TypeScript
Star
25k
Fork
6.8k
Merge trung bình
1 ngày 8 giờ
Pull request đã merge (30 ngày)
91

Mô tả

The CDK Table will detect changes in rows based on a differ which is based on the `trackBy` input function.

https://github.com/angular/components/blob/4152aec5d421d993246b1ede3803027082330492/src/cdk/table/table.ts#L517-L519

This is fine when actual data changes but it does not handle a scenario where the row template has changed but the data has not changed, which is an issue especially when working with virtual scroll.

Since the trackBy handler does not get access to the `rowDef`, only to the index and raw row data, it is not possible to workaround.
> Even if there is access to the rowDef, trackBy is not a predicate so it will be hard to return something logical there, trackBy is for identity, this one needs to happen outside of the trackBy

In some cases, one might write an alternate `trackBy` function but it will not do for all scenarios. Moreover, the `trackBy` function must be dead simple and fast, if the view port is large, showing enough rows with enough columns it becomes a bottleneck.

Usually, most performant way to do the trackBy is just return the index.

However, if the rowDef has changed, it will not be possible to detect it.

Once diff has been calculated, the call to `renderRows` will ignore the change in the row template definition

https://github.com/angular/components/blob/4152aec5d421d993246b1ede3803027082330492/src/cdk/table/table.ts#L611-L623

It will not call `_renderCellTemplateForItem` leaving it with the old template cached in the table.

#### Reproduction

https://stackblitz.com/edit/angular-qvzms7?file=src%2Fapp%2Ftable-flex-basic-example.ts

Basically, we need to have multiple row definitions (CdkRowDef) but `multiTemplateDataRows` is set to false.
We define 2 row templates with `when` predicates, where the 2 predicates are opposite, if one is on the other is off and vice versa.

Using a simple button to toggle a boolean flag we switch between the 2 predicates.

If we use a simple button it will not work, we switch the flag but it does not change the rendered template for the row.

Using the `ChangeDetectorRef` to run manual CD will not work as well because the table handles it's own diff mechanism when it comes to row diffing.

The only way (I found so far...) to force the proper row template to render is calling `_forceRenderDataRows();` which is private. The will force render the entire dataset, using the new row template. This is bad of course, as it's using a private method and it will force render ALL rows, while in real scenarios one will only want some to re-render.

#### Expected Behavior

What behavior were you expecting to see?
Being able to change the row definition template and have the CDK Table autodetect and render it.

#### Actual Behavior

What behavior did you actually see?
CDK Table fail to detect changes in the row definition template.

#### Environment

- Angular: 10+
- CDK/Material: 10+

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Bắt đầu trong src/cdk/table/table.ts, tại các phần differ và renderRows được liên kết, sau đó so sánh cách _renderCellTemplateForItem được sử dụng. Tái hiện vấn đề bằng StackBlitz được liên kết, sử dụng nhiều định nghĩa CdkRowDef và multiTemplateDataRows=false. Hoàn thành khi việc thay đổi vị từ của hàng khiến template hàng phù hợp được render mà không phụ thuộc vào phương thức private _forceRenderDataRows.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
angular, typescript
Lĩnh vực
frontend
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.