Keywords: Angular Material | Expandable Table | when Predicate | mat-table | detailRow Property
Abstract: This article provides an in-depth technical guide for implementing expandable row functionality in Angular 4+ using Angular Material tables. It thoroughly analyzes the when predicate mechanism of mat-table components, the implementation logic of mat-row expansion, and special data structure handling. The article includes complete code examples and implementation steps, with particular emphasis on the critical role of the detailRow property and the data association mechanism between expanded rows and main rows.
Technical Background and Requirements Analysis
In modern web application development, expandable functionality in data tables has become an essential feature for enhancing user experience. Angular Material, as a mature UI component library in the Angular ecosystem, provides powerful data presentation capabilities through its table component mat-table. However, the official documentation does not directly offer built-in expandable row implementation, requiring developers to combine existing features.
Core Implementation Principles
The key to implementing expandable rows lies in utilizing the when predicate functionality of mat-table. This feature allows dynamic rendering of different row templates based on specific conditions. Specifically, two types of rows need to be defined: main rows (containing primary data) and expanded rows (containing detailed data). These row types are distinguished through an identifier property (typically named detailRow).
Data Structure Design
The data source requires special processing to add a corresponding expanded row object for each main data element. Example code:
connect(): Observable<Element[]> {
const rows = [];
data.forEach(element => rows.push(element, { detailRow: true, element }));
return Observable.of(rows);
}
This structure creates an alternating array: main row data objects followed immediately by their corresponding expanded row objects. The expanded row object contains two key properties: detailRow: true as a type identifier, and element referencing the original data for access within the expanded row.
Template Implementation
In the HTML template, two types of mat-row need to be defined:
<mat-row *matRowDef="let row; columns: displayedColumns;"
matRipple
class="element-row"
[class.expanded]="expandedElement == row"
(click)="expandedElement = row">
</mat-row>
Main rows toggle the expandedElement variable through click events, controlling which row is in the expanded state.
The definition of expanded rows is more critical:
<mat-row *matRowDef="let row; columns: ['expandedDetail']; when: isExpansionDetailRow"
[@detailExpand]="row.element == expandedElement ? 'expanded' : 'collapsed'"
style="overflow: hidden">
</mat-row>
Here, when: isExpansionDetailRow specifies that this row template is used only for rows satisfying the isExpansionDetailRow function condition.
Predicate Function Implementation
The predicate function identifies expanded rows:
isExpansionDetailRow = (i: number, row: any) => row.hasOwnProperty('detailRow');
Starting from Angular Material RC0, this function receives two parameters: row index and row data. The function checks whether the row data contains the detailRow property to determine whether to use the expanded row template.
Styling and Animation
Expand/collapse effects are achieved through CSS class binding and Angular animations:
[class.expanded]="expandedElement == row"
This adds specific style classes to the currently expanded row, while the [@detailExpand] animation binding controls the display animation of expanded rows.
Advanced Feature Integration
This solution integrates seamlessly with other Angular Material table features:
- Sorting: Expanded rows do not affect sorting logic, as the predicate function ensures they are not treated as regular data rows
- Pagination: Ensure expanded rows remain associated with their main rows during pagination
- Filtering: Filtering logic should apply only to main row data
Performance Optimization Considerations
For large datasets, consider:
- Using
trackByfunctions to optimize rendering performance - Integrating virtual scrolling
- Implementing lazy loading for expanded row content
Practical Application Recommendations
In actual projects, it is recommended to encapsulate expandable row functionality as reusable directives or components. This not only improves code reusability but also simplifies maintenance. Additionally, ensure expanded row content is responsively designed to adapt to different screen sizes.
Common Issue Resolution
Potential issues during implementation include:
- Expanded row style conflicts: Resolve through scoped CSS or
::ng-deep - Data synchronization issues: Ensure consistency in data references between main and expanded rows
- Animation performance: Optimize animation complexity to avoid impacting page performance
Conclusion
By properly utilizing the when predicate functionality of Angular Material tables and designing flexible data structures, fully functional and high-performance expandable row tables can be implemented. This solution not only meets basic expand/collapse requirements but also provides a solid foundation for more complex data presentation scenarios.