Skip to content

feat(mat-table): Enabling content projection within material table #31848

@Aeseir

Description

@Aeseir

Feature Description

Allow for content projection within material table.
Content projection doesn't behave cleanly within material table as it does within general angular components.

For example lets assume we want table of individual contributors (IC) and table for leaders:

 // IC Table
  <ng-container matColumnDef="full_name">
    <th mat-header-cell *matHeaderCellDef> Full Name </th>
    <td mat-cell *matCellDef="let element"> {{element.first_name}} {{element.last_name}} </td>
  </ng-container>

  <ng-container matColumnDef="review_done">
    <th mat-header-cell *matHeaderCellDef> Review Done </th>
    <td mat-cell *matCellDef="let element"> {{element.review_done}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
// Leader Table
<table mat-table [dataSource]="dataSource">

  <ng-container matColumnDef="full_name">
    <th mat-header-cell *matHeaderCellDef> Full Name </th>
    <td mat-cell *matCellDef="let element"> {{element.first_name}} {{element.last_name}} </td>
  </ng-container>

  <ng-container matColumnDef="team_reviewed">
    <th mat-header-cell *matHeaderCellDef> Team Reviewed </th>
    <td mat-cell *matCellDef="let element"> {{element.team_reviewed}} </td>
  </ng-container>

  <ng-container matColumnDef="pip_count">
    <th mat-header-cell *matHeaderCellDef> PIP Count </th>
    <td mat-cell *matCellDef="let element"> {{element.pip_count}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

with content projection we could essentially have a single table housing common content:

<table mat-table [dataSource]="dataSource">

  <ng-container matColumnDef="full_name">
    <th mat-header-cell *matHeaderCellDef> Full Name </th>
    <td mat-cell *matCellDef="let element"> {{element.first_name}} {{element.last_name}} </td>
  </ng-container>

  <ng-content />

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

Then use the parent component to project the additional content as needed for example:

<app-projected-table [dataSource]="dataSource" [displayedColumns]="displayedColumns">
@if(staffListType === StaffType.IC) {
  <ng-container matColumnDef="review_done">
    <th mat-header-cell *matHeaderCellDef> Review Done </th>
    <td mat-cell *matCellDef="let element"> {{element.review_done}} </td>
  </ng-container>
}
@ else if(staffListType === StaffType.Leader) {
  <ng-container matColumnDef="team_reviewed">
    <th mat-header-cell *matHeaderCellDef> Team Reviewed </th>
    <td mat-cell *matCellDef="let element"> {{element.team_reviewed}} </td>
  </ng-container>

  <ng-container matColumnDef="pip_count">
    <th mat-header-cell *matHeaderCellDef> PIP Count </th>
    <td mat-cell *matCellDef="let element"> {{element.pip_count}} </td>
  </ng-container>
}
</app-projected-table>

Use Case

  1. Avoids duplication of markup and logic across components. You maintain one table shell and inject only what's unique.
  2. Promotes better collaboration and encapsulation. The table component handles layout and styling; consumers handle data logic for common patterns
  3. Future proof extensibility so that table components don't necessary need to be modified, only consuming components need the new logic

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: material/tablefeatureThis issue represents a new feature or feature request rather than a bug or bug fix

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions