26
votes

I am trying to implement Material2 data table. But I am not able to understand how to use it in proper way.

import {Component, ElementRef, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/observable/fromEvent';

@Component({
  selector: 'table-filtering-example',
  styleUrls: ['table-filtering-example.css'],
  templateUrl: 'table-filtering-example.html',
})
export class TableFilteringExample {
  displayedColumns = ['userId', 'userName', 'progress', 'color'];
  exampleDatabase = new ExampleDatabase();
  dataSource: ExampleDataSource | null;

  @ViewChild('filter') filter: ElementRef;

  ngOnInit() {
    this.dataSource = new ExampleDataSource(this.exampleDatabase);
    Observable.fromEvent(this.filter.nativeElement, 'keyup')
        .debounceTime(150)
        .distinctUntilChanged()
        .subscribe(() => {
          if (!this.dataSource) { return; }
          this.dataSource.filter = this.filter.nativeElement.value;
        });
  }
}

/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
  'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
  'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
  'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];

export interface UserData {
  id: string;
  name: string;
  progress: string;
  color: string;
}

/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
  get data(): UserData[] { return this.dataChange.value; }

  constructor() {
    // Fill up the database with 100 users.
    for (let i = 0; i < 100; i++) { this.addUser(); }
  }

  /** Adds a new user to the database. */
  addUser() {
    const copiedData = this.data.slice();
    copiedData.push(this.createNewUser());
    this.dataChange.next(copiedData);
  }

  /** Builds and returns a new User. */
  private createNewUser() {
    const name =
        NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
        NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';

    return {
      id: (this.data.length + 1).toString(),
      name: name,
      progress: Math.round(Math.random() * 100).toString(),
      color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
    };
  }
}

/**
 * Data source to provide what data should be rendered in the table. Note that the data source
 * can retrieve its data in any way. In this case, the data source is provided a reference
 * to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
 * the underlying data. Instead, it only needs to take the data and send the table exactly what
 * should be rendered.
 */
export class ExampleDataSource extends DataSource<any> {
  _filterChange = new BehaviorSubject('');
  get filter(): string { return this._filterChange.value; }
  set filter(filter: string) { this._filterChange.next(filter); }

  constructor(private _exampleDatabase: ExampleDatabase) {
    super();
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<UserData[]> {
    const displayDataChanges = [
      this._exampleDatabase.dataChange,
      this._filterChange,
    ];

    return Observable.merge(...displayDataChanges).map(() => {
      return this._exampleDatabase.data.slice().filter((item: UserData) => {
        let searchStr = (item.name + item.color).toLowerCase();
        return searchStr.indexOf(this.filter.toLowerCase()) != -1;
      });
    });
  }

  disconnect() {}
}

Above is the code of datatable that is very confusing for me. Even their documentation is very poor. Can someone explain what is the flow of above code?

Please ignore if you feel question is too basic to ask?

4

4 Answers

23
votes

The code from your example is the definition for a generic table, using the new cdk component in the material2 specification. you must keep in mind the md-table is the visual implementation of the cdk-table, so you need to declare a cdk with a model compatible with the md-model in HTML.

For example:

I declare a cdk-table with following implementation:

  1. First, the dependencies:

The new CDK component in Material2, using:

import { DataSource } from '@angular/cdk';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
  1. Definition of the structure of the HTML in TS:

I define a displayedColumns array, the items are the columns in my HTML table, in order:

displayedColumns = ['userId', 'userName', 'progress'];

A database of the type ExampleDatabase (an object with a manual definition particular):

exampleDatabase = new ExampleDatabase();

Finally, i declare a dataSource, this is the origin of my data. It is an object with a manual definition or data null.

dataSource: ExampleDataSource | null;

In the ngOnInit() method, I simply declare that my dataSource is a new ExampleDataSource with parameter my exampleDataBase.

Good, now to implement the rest of the code:

First, declare an interface for the DataBase. This is very important for maintaining the congruence of the data, the database must respect a defined scheme. In this example, the database has three columns: id, name and progress:

export interface UserData {
  id: number;
  name: string;
  progress: string;
}

The next point is create a class (Object) ExampleDatabase with the definition of the data in my DataBase. You could create a service for connecting to an actual database (PostgreSQL, MongoDB), get the real data and create the objects for the cdk-datatable in another method, however in this example we are using an in memory database emulated at run time.

export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
  get data(): UserData[] { return this.dataChange.value; }

  constructor() {
    // Fill up the database with 100 users.
    for (let i = 0; i < 100; i++) { this.addUser(); }
  }

  /** Adds a new user to the database. */
  addUser() {
    const copiedData = this.data.slice();
    copiedData.push(this.createNewUser());
    this.dataChange.next(copiedData);
  }

  /** Builds and returns a new User. */
  private createNewUser() {

    return {
      id: 1,
      name: 'example',
      progress: Math.round(Math.random() * 100).toString()
    };
  }
}

Good, finally i create a second class with the definition of my DataSource.

export class ExampleDataSource extends DataSource<any> {
  constructor(private _exampleDatabase: ExampleDatabase) {
    super();
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<UserData[]> {
    return this._exampleDatabase.dataChange;
  }

  disconnect() { }
}

This method makes sure the data is in the correct format, and releases the "connection" to the DataBase (in memory) to get the data in it.

Finally, use the md-table component or cdk-table component in the HTML. The md-table component uses the material design style, and the cdk-table uses a generic style..

md-table:

<div class="example-container mat-elevation-z8">
  <md-table #table [dataSource]="dataSource">

    <!-- ID Column -->
    <ng-container cdkColumnDef="userId">
      <md-header-cell *cdkHeaderCellDef> ID </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
    </ng-container>

    <!-- Progress Column -->
    <ng-container cdkColumnDef="progress">
      <md-header-cell *cdkHeaderCellDef> Progress </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.progress}}% </md-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container cdkColumnDef="userName">
      <md-header-cell *cdkHeaderCellDef> Name </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.name}} </md-cell>
    </ng-container>

    <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
    <md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
  </md-table>
</div>

cdk-table:

<div class="example-container mat-elevation-z8">
  <cdk-table #table [dataSource]="dataSource" class="example-table">

    <!-- ID Column -->
    <ng-container cdkColumnDef="userId">
      <cdk-header-cell *cdkHeaderCellDef class="example-header-cell"> ID </cdk-header-cell>
      <cdk-cell *cdkCellDef="let row" class="example-cell"> {{row.id}} </cdk-cell>
    </ng-container>

    <!-- Progress Column -->
    <ng-container cdkColumnDef="progress">
      <cdk-header-cell *cdkHeaderCellDef class="example-header-cell"> Progress </cdk-header-cell>
      <cdk-cell *cdkCellDef="let row" class="example-cell"> {{row.progress}}% </cdk-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container cdkColumnDef="userName">
      <cdk-header-cell *cdkHeaderCellDef class="example-header-cell"> Name </cdk-header-cell>
      <cdk-cell *cdkCellDef="let row" class="example-cell"> {{row.name}} </cdk-cell>
    </ng-container>

    <cdk-header-row *cdkHeaderRowDef="displayedColumns" class="example-header-row"></cdk-header-row>
    <cdk-row *cdkRowDef="let row; columns: displayedColumns;" class="example-row"></cdk-row>
  </cdk-table>
</div>

The rest of implementations, search, menus, checkboxes, etc, is your responsibility to implement the logic for manipulating the information.

Use the documentation about cdk-table for more details:

https://material.angular.io/guide/cdk-table

Result:

enter image description here

Do me saber and achievement, I understand my explanation, and I apologize for my English. I am learning.

6
votes

Here is the customized code created for view attendance, right now i have hard coded the data , you can call service instead of that for getting dynamic data.

app.component.ts

import { Component, OnInit, ElementRef, ViewEncapsulation, ViewChild } from '@angular/core';
import { DataSource } from '@angular/cdk';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { MdPaginator, MdSort } from '@angular/material';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';
declare let d3: any;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})


export class AppComponent implements OnInit {

  displayedColumns = ['shiftDate', 'swipeIn', 'swipeOut', 'duration', 'status'];
  exampleDatabase = new ExampleDatabase();
  dataSource: ExampleDataSource | null;

  @ViewChild(MdPaginator) paginator: MdPaginator;
  @ViewChild(MdSort) sort: MdSort;

  ngOnInit() {
    this.dataSource = new ExampleDataSource(this.exampleDatabase, this.paginator, this.sort);
  }
}

export interface attendanceData {
  shiftDate: string;
  swipeIn: string;
  swipeOut: string;
  duration: string;
  status: string;
}


/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<attendanceData[]> = new BehaviorSubject<attendanceData[]>([]);
  get data(): attendanceData[] {

    let data = [
      {
        "shiftDate": "17-July-2017",
        "swipeIn": "10:00 AM",
        "swipeOut": "06:00 PM",
        "duration": "8 Hours",
        "status": "PRESENT"

      },
      {
        "shiftDate": "16-July-2017",
        "swipeIn": "9:00 AM",
        "swipeOut": "5:00 AM",
        "duration": "7 Hours",
        "status": "PRESENT"
      }

    ];

    return data;
  }

  constructor() {

    this.dataChange.next(this.data);
  }

}

export class ExampleDataSource extends DataSource<any> {
  _filterChange = new BehaviorSubject('');
  get filter(): string { return this._filterChange.value; }
  set filter(filter: string) { this._filterChange.next(filter); }

  constructor(private _exampleDatabase: ExampleDatabase, private _paginator: MdPaginator, private _sort: MdSort) {
    super();
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<attendanceData[]> {
    const displayDataChanges = [
      this._exampleDatabase.dataChange,
      this._paginator.page,
      this._sort.mdSortChange
    ];

    return Observable.merge(...displayDataChanges).map(() => {
      // const data = this._exampleDatabase.data.slice();
      const data = this.getSortedData();
      // Grab the page's slice of data.
      const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
      return data.splice(startIndex, this._paginator.pageSize);
    });
  }

  disconnect() { }

  getSortedData(): attendanceData[] {
    const data = this._exampleDatabase.data.slice();
    if (!this._sort.active || this._sort.direction == '') { return data; }

    return data.sort((a, b) => {
      let propertyA: number | string = '';
      let propertyB: number | string = '';

      switch (this._sort.active) {
        case 'shiftDate': [propertyA, propertyB] = [a.shiftDate, b.shiftDate]; break;
        case 'swipeIn': [propertyA, propertyB] = [a.swipeIn, b.swipeIn]; break;
        case 'swipeOut': [propertyA, propertyB] = [a.swipeOut, b.swipeOut]; break;
        case 'duration': [propertyA, propertyB] = [a.duration, b.duration]; break;
      }

      let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
      let valueB = isNaN(+propertyB) ? propertyB : +propertyB;

      return (valueA < valueB ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1);
    });
  }
}

app.component.html

<div class="example-container mat-elevation-z8">
  <md-table #table [dataSource]="dataSource" mdSort>
    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- ID Column -->
    <ng-container cdkColumnDef="shiftDate">
      <md-header-cell *cdkHeaderCellDef md-sort-header> Shift Date </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.shiftDate}} </md-cell>
    </ng-container>

    <!-- Progress Column -->
    <ng-container cdkColumnDef="swipeIn">
      <md-header-cell *cdkHeaderCellDef md-sort-header> Swipe In </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.swipeIn}}% </md-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container cdkColumnDef="swipeOut">
      <md-header-cell *cdkHeaderCellDef> Swipe Out </md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.swipeOut}} </md-cell>
    </ng-container>

    <!-- Color Column -->
    <ng-container cdkColumnDef="duration">
      <md-header-cell *cdkHeaderCellDef>Duration</md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.duration}} </md-cell>
    </ng-container>


    <!-- Color Column -->
    <ng-container cdkColumnDef="status">
      <md-header-cell *cdkHeaderCellDef>Status</md-header-cell>
      <md-cell *cdkCellDef="let row"> {{row.status}} </md-cell>
    </ng-container>

    <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
    <md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>
  </md-table>

    <md-paginator #paginator
                [length]="exampleDatabase.data.length"
                [pageIndex]="0"
                [pageSize]="25"
                [pageSizeOptions]="[5, 10, 25, 100]">
  </md-paginator>

</div>

app.module.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { MaterialModule, MdTableModule  } from '@angular/material';
import { FlexLayoutModule } from '@angular/flex-layout';
import { CdkTableModule } from '@angular/cdk';


import { AppComponent } from './app.component';



@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserAnimationsModule,
    CdkTableModule,
    BrowserModule,
    MaterialModule, MdTableModule,
    FlexLayoutModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
3
votes

Here is an article written by me.you will find everything you need to know in here. https://medium.com/@fnote/md-tables-angular-material-be2c45947955

lets take the code piece by piece.you will find the complete code here https://material.angular.io/components/table/overview

import {Component} from '@angular/core';
import {DataSource} from '@angular/cdk';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';

import the necessary packages you require. Make sure you install angular cdk while you are installing angular material. Before proceeding it is better to mention what is a data source what is an observable and what is a behavior subject and also rxjs.

RXJS is a hot topic in frond end development these days .It is a javascript library for reactive programming ( Reactive programming is just a way of building software applications. Essentially, your software is built to “react” to changes that happen (like click events, data being fetched, etc)) using Observables, to make it easier to compose asynchronous or callback-based code. cdk is imported because md-tables are built on top of it. Cdk tables are the foundation to md tables.

What is an observable?

The ability of observables being able to handle multiple values over time makes them a good candidate for working with real-time data, events and any sort of stream you can think of. Observables gives better control when working with in-flow of values from a stream. Observable is a wrapper around a datasource , data source is a stream of values possibly emits multiple values over time we want to do something whenever a new value occurs. We connect observer and observable through a subscription. Subscription says there is someone listening to these stream of values. An observer subscribes to an Observable. An Observable emits items or sends notifications to its observers by calling the observers’ methods. observer implements up to 3 methods.they are complete() ,next() and onerror(). next() method will be called by the observable whenever a new value is emitted.whenever observable throws an error onerror() method is invoked.when observable is done and when it knows there won’t be any more new values in the future it calls complete() method.

I strongly recommend you follow the series by academind on youtube about RXJS library to get a deeper understanding about RXJS capabilities, observers, observables, subjects, behavior subjects and subscriptions.

Let’s continue with the code

@Component({
selector: 'table-basic-example',
styleUrls: ['table-basic-example.css'],
templateUrl: 'table-basic-example.html',
})

This is regular angular 4.selector is the name by which outside parties refer to our component .and style Url is the location our /file this component refer for styling purposes and basic html of the component is found in the template url.

export class TableBasicExample {
displayedColumns = ['userId', 'userName', 'progress', 'color'];
exampleDatabase = new ExampleDatabase();
dataSource: ExampleDataSource | null;
ngOnInit() {
this.dataSource = new ExampleDataSource(this.exampleDatabase);
}
}

Here first we make an array of all the columns of our table we need to display on the browser (column headings ) this array is referred again from the html file.

And further we create an instance of example database class and an instance of datasource which carries no data at the beginning. Then the data in the example database is injected to this datasource to fill it as it is initially empty Ng onitint is a life cycle hook that is called by angular in order to mark the completion of creation of the component.

const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 
'purple','fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 
'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 
'Jack','Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 
'Jasper','Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];

The we have 2 arrays .these fill up our example database

export interface UserData {
id: string;
name: string;
progress: string;
color: string;
}

Here you have an interface defined. The variables mentioned here will eventually become the columns of our tables.

export class ExampleDatabase {
 dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>
([]);
get data(): UserData[] { return this.dataChange.value; }
constructor() {
// Fill up the database with 100 users.
for (let i = 0; i < 100; i++) { this.addUser(); }
}
addUser() {
const copiedData = this.data.slice();
copiedData.push(this.createNewUser());
this.dataChange.next(copiedData);
}
private createNewUser() {
const name =
NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';
return {
id: (this.data.length + 1).toString(),
name: name,
progress: Math.round(Math.random() * 100).toString(),
color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
};
}
}

This is the example database that the datasource uses to retrieve data for the table. You only have 2 arrays defined but in fact 4 columns to be displayed. You can fill 2 columns from the data in the arrays but the data for the other 2 columns here must be generated inside this class.

So what is a subject? Observable can’t emit values itself but we want to be able to do it ourselves .but if we need to emit new values ourselves we have to go with a subject. Subject is an observable it inherits from observable but we can call the next() method on it manually do that we can trigger a emitting of a new value. Therefore subject is an active observable. It is an observer in addition to being an observable so you can also send values to a subject in addition to subscribing to it.

What in the world is a behavior subject? Behavior subject is a special kind of a subject but it has an initial value unlike subjects. It needs an initial value as it must always return a value on subscription even if it hasn’t received a next() Rx.subject() subscription won’t get anything initially Rx.behaviorsubject(‘a’) subscription get ‘a’ initially

Don’t worry about the code written with const name…we have about 20 names in the names array but we need 100 distinct names.So here we change their initials in a random pattern.

Progress is also calculated randomly.

Data change is a variable of type behavior subject.

this.dataChange.next(copiedData); 

..whenever a new user gets pushed in to the array that datachange is notified to the subscribers. Datachange is a sort of a Stream that emits whenever the data has been modified. Make a variable called datachange that is a behavior subject that has an array of initial values Make an array called copied data Create a new user with 4 properties. Users are created like objects here with these 4 properties being their attributes. Call the next method with the new user added Call the next method on the special observable and subject emits.add user() adds a user to the database while create user method builds a user object with 4 distinct attributes. Data source to provide what data should be rendered in the table. Note that the data source can retrieve its data in any way. In this case, the data source is provided a reference to a common data base, ExampleDatabase. Datasource only take the data and send the table exactly what should be rendered, nothing else.

export class ExampleDataSource extends DataSource<any> {
constructor(private _exampleDatabase: ExampleDatabase) {
super();
}
connect(): Observable<UserData[]> {
return this._exampleDatabase.dataChange;
}
 disconnect() {}
 }

Connecting the table to data source

Data is provided to the table through a DataSource.

Connect function connects a collection viewer such as a data table to a datasource. Connect function is called by the table to get the stream containing the data that should be rendered. Parameters has to be given to this connect function. When the table receives a data source, it calls the DataSource’s connect function which returns an observable that emits an array of data. Whenever the data source emits data to this stream, the table will update. Because the data source provides this stream, it bears the responsibility of triggering table updates. This can be based on anything: web socket connections, user interaction, model updates, time-based intervals, etc. Most commonly, updates will be triggered by user interactions like sorting and pagination. Disconnect function breaks the connection between the table and the datasource. Lets look at the HTML file or our template. Cell templates

<ng-container cdkColumnDef="color">
     <md-header-cell *cdkHeaderCellDef>Color</md-header-cell>
     <md-cell *cdkCellDef="let row" [style.color]="row.color"> {{row.color}} 
</md-cell>
</ng-container>

Firstly columns of the table are defined.with the directive cdkColumnDef each column is given a name.this is the name from which this particular column in the table is referred from other places.each column then goes on to define a header cell template and a data cell template.header cell provides and displays the name of the column and cell template retrieves data that should be displayed and displays them below the header in rows. cdkCellDef exports row context tables header row and data row are defined below Row Templates are given below,

 <md-header-row *cdkHeaderRowDef="displayedColumns"></md-header-row>
 <md-row *cdkRowDef="let row; columns: displayedColumns;"></md-row>

‘Displayed columns’ array is in your .ts (typescript file) and color is a column or an element of the array (as per our example). These row templates looks at the name given to the cdkColumnDef and finds the specific columns to render. The cdkRowDef also exports row context. Rendered content of the row comes from the cell templates not from the row templates. For further reading on this check this out Angular Material

Edit description material.angular.io how to generate required Columns dynamically ?

<ng-container *ngFor="let col of displayedColumns" cdkColumnDef= {{col}}>
<md-header-cell *cdkHeaderCellDef md-sort-header > {{ col }} </md-header-
cell>
 <md-cell  *cdkCellDef="let row"> {{row[col]}} </md-cell>
</ng-container>

compare this piece of code with the one before the last .here we loop through the displayedColumns array and column names are assigned in the process therefore we generate required columns looping through the array instead of defining all required columns by hand in the HTML file. With this I will conclude this explanation, md tables further offers you features like pagination, sorting and filtering. The official documentation includes examples that you can refer to in order to add these features to your md table.

Now you know what happens behind the scenes in a md table.

2
votes

I had lot of troubles trying to use this approach:

import { DataSource } from '@angular/cdk/collections';

....

I could get the table, but to sort the columns was impossible because sort wasn't a known property of Datasource, etc, etc

finally i noted that was using "@angular/material": "^5.0.0-rc0",

Current, y I'm working with MatTableDataSource

IMPORTS

import {MatTableDataSource} from '@angular/material';

CLASS VARIABLES

private ELEMENT_DATA: reportInterface[] = [];
public tbDataSource;
public displayedColumns;

and in the constructor

this.dataService.getReport().subscribe(results => {
if(!results)return;
this.ELEMENT_DATA = results;
this.displayedColumns = [.............];
this.tbDataSource = new MatTableDataSource(this.ELEMENT_DATA);
this.tbDataSource.sort = this.sort;
});

and here is my filter function

applyFilter(filterValue: string) {
this.tbDataSource.filter = filterValue;
}

I think this is faster and easier