Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2-4 Mat-Table row in another component

I would like to know if I can place the mat table rows in another component different from the table component, here is an example:

<mat-table #table [dataSource]="dataSource" class="table" matSort>
<user-table-row> </user-table-row>

  <ng-container matColumnDef="profile">
    <mat-header-cell *matHeaderCellDef mat-sort-header class="header-cell"> Profile Type </mat-header-cell>
    <mat-cell *matCellDef="let user" class="cell"> {{user.profile.type}} </mat-cell>
  </ng-container>

  <ng-container matColumnDef="status">
    <mat-header-cell *matHeaderCellDef mat-sort-header class="header-cell"> Status </mat-header-cell>
    <mat-cell *matCellDef="let user" class="cell"> {{user.profile.status}} </mat-cell>
  </ng-container>


<!-- Header and Row Declarations -->
  <mat-header-row *matHeaderRowDef="['createdAt', 'email', 'profile', 'status', 'isApproved', 'actions']" class="header-row"></mat-header-row>
  <mat-row *matRowDef="let row; columns: ['createdAt', 'email', 'profile', 'status', 'isApproved', 'actions']" class="row"></mat-row>
</mat-table>

How do I make something like this:

<mat-table #table [dataSource]="dataSource" class="table" matSort>
  <user-table-row> </user-table-row>
</mat-table>

Is it possible, anyone have done this before?

like image 304
Pedro Fernandes Avatar asked Nov 23 '17 16:11

Pedro Fernandes


Video Answer


1 Answers

I've created a custom TableComponent which uses a @Directive with ng-template to define the columns. That's pretty flexible and abstracts away all the material stuff. So you can get a common table design all across your application. You just have to define everything once inside this component.

I'll include only the relevant parts.

The @Directive is used to get a mapping between ng-template and the Material Table column name.

@Directive({ selector: 'ng-template[rowColumn]' })
export class TableRowColumnDirective {
    @Input() rowColumn: string;
    constructor(public templateRef : TemplateRef<any>) { }
}

The @Component fetches all ng-templates and build a Map to access the ng-templates by their column names.

@Component({
    selector: 'my-table',
    templateUrl: './table.component.html'
})
export class TableComponent<T> {
    @ContentChildren(TableRowColumnDirective) rowColumnDirectives: QueryList<TableRowColumnDirective>;

    displayedColumns: ['foo', 'bar'];

    // I use a Map here only, because it's much easier to access the
    // provided ng-templates within table.component.html
    rowColumnTemplates: Map<string, TableRowColumnDirective>;

    ngAfterContentInit() {
        // ArrayUtils.toMap is just a little helper function, which
        // converts an array to a Map. In this case the value of
        // `rowColumn` will become the key of the map
        this.rowColumnTemplates = ArrayUtils.toMap(this.rowColumnDirectives.toArray(), val => val.rowColumn);
    }
}

The @Component's HTML simply adds mat-header-cell and mat-cell using an *ngFor loop. The contents of the cells will then be filled with the provided ng-templates.

<mat-table ...>
    <ng-container *ngFor="let column of displayedColumns" [matColumnDef]="column">
        <mat-header-cell *matHeaderCellDef>
            HEADER CONTENT
        </mat-header-cell>
        <mat-cell *matCellDef="let row">
            <ng-container [ngTemplateOutlet]="rowColumnTemplates.get(column).templateRef" [ngTemplateOutletContext]="{$implicit: row}"></ng-container>
        </mat-cell>
    </ng-container>

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

The table can then be used like this:

<my-table>
    <!-- rowColumn matches the displayedColumns array -->
    <ng-template rowColumn="foo" let-row>{{row.profile.type}}</ng-template>
    <ng-template rowColumn="bar" let-customRowName>{{row.profile.status}}</ng-template>
</my-table>

Now you can add another @Directive for the Header Column. In my provided example the Header will always be HEADER CONTENT. It's basically just a copy of the TableRowColumnDirective.

I know this isn't exactly what you were looking for, but I guess you can see how you can inject custom rows into a mat-table. Take it as a starting point and I'm sure, you will be able to build a custom solution that fits your needs.

like image 127
Benjamin M Avatar answered Sep 28 '22 00:09

Benjamin M