Components: [Table] Error when using ng-content

Created on 11 Sep 2017  路  13Comments  路  Source: angular/components

Bug, feature request, or proposal:

Bug

What is the expected behavior?

md-table should wait until the ng-content replaced.

What is the current behavior?

md-table throws Cannot read property 'getColumnsDiff' of undefined.

What are the steps to reproduce?

Put ng-content inside md-table.

https://plnkr.co/edit/RpOncq9lbwPcjAGjXSyR?p=preview

What is the use-case or motivation for changing an existing behavior?

Anyone that want to create a component with configurable md-table inside cannot do it.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular: 4.4.0-RC.0 in the plunker, but this is not working with any version.
Material: 2.0.0-beta.10

P3 materiatable

Most helpful comment

Updated link to the table-wrapped example: here

All 13 comments

I have the same issue. Just want to create component that will unite table, paginator and loading spinner. Now I have to make copy of the same code in each component that have tables

@wermetal It's possible to create a common component that would have a table, paginator, and spinner. It seems this issue is related more to reusing columns?

@leibale Can you share more about your desired use case? Do you want to create a table that comes with pre-built columns defined but be able to add more columns into the table from ng-content?

@andrewseguin exactly

@andrewseguin
for example i want to make a component to simplify using table (facade pattern) like this:

<eq-table [model]="model3">
  <div eq-cell-detail><div>hello!</div></div>
</eq-table>

and inside the component:

<mat-table #table [dataSource]="dataSource" matSort>
  <ng-container [matColumnDef]="column.name" *ngFor="let column of model.columns">
    <mat-header-cell *matHeaderCellDef matSortHeader>{{column.title}}</mat-header-cell>
    <mat-cell *matCellDef="let element">{{element[column.name]}} </mat-cell>
  </ng-container>
      <!-- Detail Column -->
      <ng-container matColumnDef="details">      
          <mat-cell *matCellDef="let row">
            <ng-container *ngIf="currentRow!==null">
              <ng-content select="[eq-cell-detail]"></ng-content>
            </ng-container>
          </mat-cell>
        </ng-container>
  <mat-header-row *matHeaderRowDef="model.displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: model.displayedColumns;" matRipple [style.borderBottomColor]="currentRow == row ? 'transparent' : ''"
            (click)="currentRow = row;"></mat-row>
    <mat-row *matRowDef="let row; columns: ['details']; when: isDetailRow"
            [@detailExpand]="row.data == currentRow ? 'expanded' : 'collapsed'"
    style="overflow: hidden">
  </mat-row>
</mat-table>

Quick update on this PR - it is now possible to directly register columns and rows to the table which means it is possible to create wrapper tables and columns.

Is there any documentation on how this can be done? I'm trying to create a basic table component (stackblitz version which allows for column and row definitions as content but I always get * Could not find column with id ""* errors even when setting the column and row definitions in ngAfterContentInit.

update I found a good sample in the demo app here. Please ignore the comment (I'll leave it up with the link for others).

for the sample demo.
Although there is some 'manually', at least it satisfies the demand.
well done.

Thanks for the demo, it is working. I would like to go a bit further with my table component and give the option to define the template of the cell through ng-content.

My table:

<my-table [data]="data" [multisort]="true" [pagination]="true">
  <my-table-column name="id" sortable>
    test id
  </my-table-column>
  <my-table-column name="title">
    test title
  </my-table-column>
</my-table>

My column:

<ng-container matColumnDef>
  <th mat-header-cell *matHeaderCellDef> {{label || name}} </th>
  <td mat-cell *matCellDef="let data"> 
    <ng-content></ng-content>
  </td>
</ng-container>

This is not working, only the last row is displaying the value. How can I make it work and if it is possible how can I pass the data to the ng-content?

@catlabs I ran into the same issues when I was trying this approach to apply pipes to cell data. Unsure of how I should handle this.

Update: I am applying pipes now on the dataAccessor.

I injected the pipe into my custom table component.

constructor(
  private _datePipe: DatePipe
) { }

getCreatedDate = (data: ViewContactResource) => this._datePipe.transform(data.audit.created, 'short');

Note: Remember that the pipe must be provided in a module, or for custom pipes you can decorate them with the following (Angular 6+).

@Injectable({
  providedIn: 'root'
})

'root' may be replaced with a module if you don't need to provide it everywhere.

@catlabs

Finally managed to get ng-content to work inside the column wrapper component.

This is the result.

<app-data-table>

    ...

    <app-data-table-column name="test">
        <ng-template let-data="data">
            {{'Test - ' + data.id}}
        </ng-template>
    </app-data-table-column>

    ...

</app-data-table>

All rows are now displaying their id so data is being passed successfully. This is just a basic example, I needed this for my 'Actions' column which contains icons with routerLinks or click events.

Here is what I did.

For ng-content I used ngTemplateOutlet which also allows 'data' to be passed by attaching a context object (See: https://angular.io/api/common/NgTemplateOutlet).

Column wrapper html file:

...
  <td mat-cell
      *matCellDef="let data">

    <ng-content *ngTemplateOutlet="templateRef; context:{data: data}"
                [ngIf]="templateRef"></ng-content>

    <ng-container *ngIf="!templateRef">
      {{getData(data)}}
    </ng-container>

  </td>
...

In the ts file just add templateRef as a ContentChild.
Column wrapper ts:

  @ContentChild(TemplateRef) templateRef: TemplateRef<any>;

Hope this helps.

Sources I used to get this far:
https://stackoverflow.com/questions/47835488/how-can-i-project-content-to-mat-table-column
https://blog.angular-university.io/angular-ng-template-ng-container-ngtemplateoutlet/

Update: Using ContentChild instead of passing template ref as an input.

update I found a good sample in the demo app here. Please ignore the comment (I'll leave it up with the link for others).

Any update on this. The link cited above is now a 404. It would be great to be able to project column and row defs via a template.

Update

I found the table-wrapped example and you are able to produce a basic wrapped component following these steps. As in one level away with the mat-table being the direct child of the wrapper and the content being projected directly through ng-content as indicated.

However

It is still not possible to pass the content into the mat-table from a TemplateRef. This results in the same undefined columns error. Doing some more digging, this appears to be related to how angular/core handles the DOM with ContentChildren not getting children from ngTemplateOutlet; #14842

Is there a work around here? Without being able to piece together more complex behaviors we are left out in the cold trying to use Atomic design principles and there is a lot of code duplication.

Updated link to the table-wrapped example: here

update I found a good sample in the demo app here. Please ignore the comment (I'll leave it up with the link for others).

Any update on this. The link cited above is now a 404. It would be great to be able to project column and row defs via a template.

Update

I found the table-wrapped example and you are able to produce a basic wrapped component following these steps. As in one level away with the mat-table being the direct child of the wrapper and the content being projected directly through ng-content as indicated.

However

It is still not possible to pass the content into the mat-table from a TemplateRef. This results in the same undefined columns error. Doing some more digging, this appears to be related to how angular/core handles the DOM with ContentChildren not getting children from ngTemplateOutlet; #14842

Is there a work around here? Without being able to piece together more complex behaviors we are left out in the cold trying to use Atomic design principles and there is a lot of code duplication.

Were you ever able to find a way to pass content from a TemplateRef?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jeffbcross picture jeffbcross  路  126Comments

bboehm86 picture bboehm86  路  57Comments

abdulkareemnalband picture abdulkareemnalband  路  165Comments

kara picture kara  路  94Comments

Daugoh picture Daugoh  路  79Comments