angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.37k stars 6.75k forks source link

fr(table): separate cell definition from the cell-content definition #19225

Open DmitryEfimenko opened 4 years ago

DmitryEfimenko commented 4 years ago

Proposal

Unless there's another way to achieve my goal (see the Use Case), I'd like to split what's currently known as CdkCellDef object into a CdkCellDef and a CdkCellContentDef. The CdkCellDef would serve the purpose of a viewContainer where the content of the cell goes.

Use Case

I'm working on a library that will extend cdk-table with extra functionality. My library does not "know" what flavor of a table the consumer application is using. It could be a cdk-table, a mat-table, or any other table that extends cdk-table.

The library I'm working on needs to be able to add a column to the table. The column needs to match the column styles from the consuming app. However, there is no @ContentChild(CellTemplateDefinition) that I could query that would give me just the template of the cell. Here's a concrete example:

Consumer application has the following table:

<cool-table-feature>
  <table mat-table [dataSource]="dataSource">
    <ng-container matColumnDef="name">
      <th mat-header-cell *matHeaderCellDef>Name</th>
      <td mat-cell *matCellDef="let row">{{ row.name }}</td>
    </ng-container>

    <ng-container matColumnDef="weight">
      <th mat-header-cell *matHeaderCellDef>Weight</th>
      <td mat-cell *matCellDef="let row">{{ row.weight }}</td>
    </ng-container>

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

There's no way I can use information from this table to add my custom column while using cell definition from the child table. Hopefully the code example below illustrates the issue:

@Component({
  selector: 'cool-table-feature',
  template: `
    <ng-content></ng-content>

    <ng-container cdkColumnDef="myColumn" #myColumn>
      <th cdk-header-cell *cdkHeaderCellDef>My header</th>
      <td cdk-cell *cdkCellDef="let row">My content</td>
    <ng-container>
  `
})
export class CoolFeatureComponent<T> {
  constructor(private table: CdkTable<T>) {}

  // my custom column definition. I need to make sure it's using the cell template from the child table
  @ViewChild('myColumn', { read: CdkColumnDef }) columnDefToAdd: CdkColumnDef;

  // this will get the first cell template, which is for the "Name" column
  @ContentChild(CdkCellDef) cellDef: CdkCellDef;
  @ContentChild(CdkHeaderCellDef) headerCellDef: CdkHeaderCellDef;

  @ContentChild(CdkRowDef) rowDef: CdkRowDef;
  @ContentChild(CdkHeaderRowDef) headerRowDef: CdkHeaderRowDef;

  ngAfterViewInit() {
    // I need a way to use the same cell template as in the consumer app.
    // However, the below will also get the cell content, overriding the content I want to use
    this.columnDefToAdd.cell.template = this.cellDef.tempalte;
    this.columnDefToAdd.headerCell.template = this.headerCellDef.tempalte;
    this.table.addColumnDef(this.columnDefToAdd);

    // given that above code somehow worked, which it didn't, update the displayed cols:
    const currentCols = this.rowDef.columns;
    const currentHeaderCols = this.headerRowDef.columns;
    this.rowDef.columns = ['myColumn', ...currentCols];
    this.headerRowDef.columns = ['myColumn', ...currentHeaderCols];
  }
}

Proposed API:

The table would have to be created as follows:

<table mat-table [dataSource]="dataSource">
  <ng-container matColumnDef="name">
    <ng-container *matHeaderCellContent>Name</ng-container>
    <ng-container *matCellContent="let row">{{ row.name }}</ng-container>
  </ng-container>

  <ng-container matColumnDef="name">
    <ng-container *matHeaderCellContent>Weight</ng-container>
    <ng-container *matCellContent="let row">{{ row.weight }}</ng-container>

    <-- optionally custommize the cell def --/>
    <td mat-cell *matCellDef="let row" class="custom"></td>
  </ng-container>

  <-- these are default cell definitions. Angular material could provide these out of the box so that user does not have to type them, but could in case they need to be different --/>
  <th mat-header-cell *matHeaderCellDef></th>
  <td mat-cell *matCellDef="let row"></td>

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

This way the templates for the cells are separated from the templates for cell contents. The matCellDef serves the purpose of the viewContainer, the same way as the matRowDef.

I know that this particular example means a breaking change. I'm not sure if it's possible to achieve this without a breaking change. Thoughts?

angular-robot[bot] commented 2 years ago

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

angular-robot[bot] commented 2 years ago

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.