Categories
Angular Material

Angular Material — Nested Tree

Angular Material is a popular UI framework based on Material Design for Angular.

In this article, we’ll look at how to use Angular Material into our Angular project.

Nested Tree

We can add a tree with nesting withn the mat-nested-tree-node component.

For example, we can write:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatTreeModule } from '@angular/material/tree';
import { MatButtonModule } from '@angular/material/button';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatTreeModule,
    MatButtonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { NestedTreeControl } from '@angular/cdk/tree';
import { Component } from '@angular/core';
import { MatTreeNestedDataSource } from '@angular/material/tree';

interface FoodNode {
  name: string;
  children?: FoodNode[];
}

const TREE_DATA: FoodNode[] = [
  {
    name: 'Fruit',
    children: [
      { name: 'Apple' },
      { name: 'Banana' },
      { name: 'Fruit loops' },
    ]
  }, {
    name: 'Vegetables',
    children: [
      {
        name: 'Green',
        children: [
          { name: 'Broccoli' },
          { name: 'Brussels sprouts' },
        ]
      }, {
        name: 'Orange',
        children: [
          { name: 'Pumpkins' },
          { name: 'Carrots' },
        ]
      },
    ]
  },
];

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  treeControl = new NestedTreeControl<FoodNode>(node => node.children);
  dataSource = new MatTreeNestedDataSource<FoodNode>();

  constructor() {
    this.dataSource.data = TREE_DATA;
  }

  hasChild = (_: number, node: FoodNode) => !!node.children && node.children.length > 0;
}

app.component.html

<div>
  <mat-tree [dataSource]="dataSource" [treeControl]="treeControl"
    class="example-tree">
    <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
      <li class="mat-tree-node">
        <button mat-icon-button disabled></button>
        {{node.name}}
      </li>
    </mat-tree-node>
    <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">
      <li>
        <div class="mat-tree-node">
          <button mat-icon-button matTreeNodeToggle>
            <mat-icon class="mat-icon-rtl-mirror">
              {{treeControl.isExpanded(node) ? '&#8595; ' : '&#8594;'}}
            </mat-icon>
          </button>
          {{node.name}}
        </div>
        <ul [class.example-tree-invisible]="!treeControl.isExpanded(node)">
          <ng-container matTreeNodeOutlet></ng-container>
        </ul>
      </li>
    </mat-nested-tree-node>
  </mat-tree>
</div>

styles.css

.example-tree-invisible {
  display: none;
}

.example-tree ul,
.example-tree li {
  margin-top: 0;
  margin-bottom: 0;
  list-style-type: none;
}

We add the MatTreeModule to let us add the tree nodes.

The MatButtonModule lets us add the buttons for toggling the child nodes.

In app.component.ts , we have a nested array with the nested data.

Then we pass that into the MatTreeNestedDataSource constructor to create the data.

We also have to create the NestedTreecontrol instance and bind that to the mat-tree .

The hasChild method is also needed to determine if a node has child nodes.

In the app.component.html file, we show the mat-tree with the mat-tree-node to show flat tree nodes.

And the mat-nested-tree-node shows the nested tree node.

the *matTreeNodeDef are different for each.

The treeControl.isExpanded method lets us check whether the node is expanded and show content accordingly.

Conclusion

We can add a nested tree with Angular Material.

Categories
Angular Material

Angular Material — Tooltips and Tree Display

Angular Material is a popular UI framework based on Material Design for Angular.

In this article, we’ll look at how to use Angular Material into our Angular project.

Tooltips

We can add tooltips with Angular Material.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatButtonModule } from '@angular/material/button';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatTooltipModule,
    MatButtonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<div class="example-container" cdkScrollable>
  <button mat-raised-button #tooltip="matTooltip"
    matTooltip="Info about the action" matTooltipPosition="below"
    matTooltipHideDelay="100000"
    class="example-button">
    Action
  </button>
</div>

We add the tooltip by adding the matTooltip directive to the button.

matTooltipHideDelay sets the delay to hide the tooltip.

matTooltipPosition sets the position of the tooltip. Its value can be 'below' , 'above' , 'left' or 'right' .

Tooltip Class

We can set the tooltip class with the matTooltipClass attribute.

For example, we can write:

app.component.html

<div class="example-container" cdkScrollable>
  <button mat-raised-button #tooltip="matTooltip"
    matTooltip="Info about the action" matTooltipPosition="below"
    matTooltipClass="example-tooltip-red" matTooltipHideDelay="100000"
    class="example-button">
    Action
  </button>
</div>

styles.css

.example-button {
  margin-top: 16px;
}

.example-tooltip-red {
  background: #b71c1c;
}

We add the matTooltipClass attribute and set the styles for the tooltip in styles.css .

Tree

The mat-tree component lets us add a tree display to our app.

For example, we can write:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatTreeModule } from '@angular/material/tree';
import { MatButtonModule } from '@angular/material/button';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatTreeModule,
    MatButtonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { FlatTreeControl } from '@angular/cdk/tree';
import { Component } from '@angular/core';
import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree';

interface FoodNode {
  name: string;
  children?: FoodNode[];
}

const TREE_DATA: FoodNode[] = [
  {
    name: 'Fruit',
    children: [
      { name: 'Apple' },
      { name: 'Banana' },
      { name: 'Fruit loops' },
    ]
  }, {
    name: 'Vegetables',
    children: [
      {
        name: 'Green',
        children: [
          { name: 'Broccoli' },
          { name: 'Brussels sprouts' },
        ]
      }, {
        name: 'Orange',
        children: [
          { name: 'Pumpkins' },
          { name: 'Carrots' },
        ]
      },
    ]
  },
];

interface ExampleFlatNode {
  expandable: boolean;
  name: string;
  level: number;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  private _transformer = (node: FoodNode, level: number) => {
    return {
      expandable: !!node.children && node.children.length > 0,
      name: node.name,
      level: level,
    };
  }

  treeControl = new FlatTreeControl<ExampleFlatNode>(
    node => node.level, node => node.expandable);

  treeFlattener = new MatTreeFlattener(
    this._transformer, node => node.level, node => node.expandable, node => node.children);

  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);

  constructor() {
    this.dataSource.data = TREE_DATA;
  }

  hasChild = (_: number, node: ExampleFlatNode) => node.expandable;
}

app.component.html

<div>
  <mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
    <mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
      <button mat-icon-button disabled></button>
      {{node.name}}
    </mat-tree-node>
    <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding>
      <button mat-icon-button matTreeNodeToggle
        [attr.aria-label]="'Toggle ' + node.name">
        <mat-icon class="mat-icon-rtl-mirror">
          {{treeControl.isExpanded(node) ? '&#8595;' : '&#8594;'}}
        </mat-icon>
      </button>
      {{node.name}}
    </mat-tree-node>
  </mat-tree>

</div>

We add the MatTreeModule to let us add the tree display.

And the MatButtonModule lets us add the button for the tree nodes.

In app.component.ts , we add the treeControl variable that we use with the treeControl directive to bind to the mat-tree .

This way, we can manipulate the tree.

dataSource is created from the MatTreeDataSource constructor.

We also need the hasChild method to let Angular Material check if there are any child nodes in the tree.

In the button, we check is the node is expanded with the treeControl.isExpanded method.

Then we can change the tree node display accordingly.

Conclusion

We can add tooltips and a tree display with Angular Material.

Categories
Angular Material

Angular Material — Tabs and Toolbars

Angular Material is a popular UI framework based on Material Design for Angular.

In this article, we’ll look at how to use Angular Material into our Angular project.

Tabs

We can add tabs with Angular Material.

To add it, we can write:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatTabsModule } from '@angular/material/tabs';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatTabsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<div>
  <mat-tab-group mat-align-tabs="start">
    <mat-tab label="First">Content 1</mat-tab>
    <mat-tab label="Second">Content 2</mat-tab>
    <mat-tab label="Third">Content 3</mat-tab>
  </mat-tab-group>
</div>

We add the MatTabsModule into our Angular module.

Then we add the mat-tab-group and the mat-tab component to add the tabs.

The label has the tab header text.

The content between the mat-tab tags are the tab content.

The mat-align-tabs attribute lets us align the tabs to the position we want.

Other values of it can be center or end .

Toolbar

We can add a toolbar with the mat-toolbar component.

For instance, we can write:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatToolbarModule,
    MatIconModule,
    MatButtonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<div>
  <mat-toolbar>
    <button mat-icon-button class="example-icon"
      aria-label="Example icon-button with menu icon">
      <mat-icon>menu</mat-icon>
    </button>
    <span>My App</span>
    <span class="example-spacer"></span>
    <button mat-icon-button class="example-icon favorite-icon"
      aria-label="Example icon-button with heart icon">
      <mat-icon>favorite</mat-icon>
    </button>
    <button mat-icon-button class="example-icon"
      aria-label="Example icon-button with share icon">
      <mat-icon>share</mat-icon>
    </button>
  </mat-toolbar>
</div>

styles.css

.example-spacer {
  flex: 1 1 auto;
}

We add the MatIconModule , MatToolbarModule , and MatButtonModule to let us add the toolbar button icons, toolbar, and buttons respectively.

In app.component.html , we add the toolbar with the mat-toolbar component.

The mat-icon-button directive lets us add icon buttons.

In styles.css , we add the flex property to let us space out the buttons.

We can add a multi-row toolbar with:

app.component.html

<div>
  <mat-toolbar color="primary">
    <mat-toolbar-row>
      <span>Custom Toolbar</span>
    </mat-toolbar-row>

    <mat-toolbar-row>
      <span>Second Line</span>
      <span class="example-spacer"></span>
      <mat-icon class="example-icon" aria-hidden="false"
        aria-label="Example user verified icon">verified_user</mat-icon>
    </mat-toolbar-row>

    <mat-toolbar-row>
      <span>Third Line</span>
      <span class="example-spacer"></span>
      <mat-icon class="example-icon" aria-hidden="false"
        aria-label="Example heart icon">favorite</mat-icon>
      <mat-icon class="example-icon" aria-hidden="false"
        aria-label="Example delete icon">delete</mat-icon>
    </mat-toolbar-row>
  </mat-toolbar>
</div>

We add the mat-toolbar-row component into our mat-toolbar component to add the rows.

Conclusion

We can add tabs and toolbars easily with Angular Material.

Categories
Angular Material

Angular Material — Tables with Sorting, Filtering, and Pagination 

Angular Material is a popular UI framework based on Material Design for Angular.

In this article, we’ll look at how to use Angular Material into our Angular project.

Table with Filtering

We can add an input box to let us search the table.

For example, we can write:

app.component.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatTableModule } from '@angular/material/table';
import { MatInputModule } from '@angular/material/input';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatTableModule,
    MatInputModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

styles.css

table {
  width: 100%;
}

app.component.ts

import { Component } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';

export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
  { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
  { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
  { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
  { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
];

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource(ELEMENT_DATA);

  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }
}

app.component.html

<div>
  <mat-form-field>
    <mat-label>Filter</mat-label>
    <input matInput (keyup)="applyFilter($event)" placeholder="Element" #input>
  </mat-form-field>

  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
    <ng-container matColumnDef="position">
      <th mat-header-cell *matHeaderCellDef> No. </th>
      <td mat-cell *matCellDef="let element"> {{element.position}} </td>
    </ng-container>

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

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

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

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

    <tr class="mat-row" *matNoDataRow>
      <td class="mat-cell" colspan="4">No data matching the filter
        "{{input.value}}"</td>
    </tr>
  </table>
</div>

We add the MatTableModule to let us add the table with Material Design style.

MatInputModule lets us add the input box.

In app.component.ts , we added the applyFilter method to let us filter the items by setting the this.dataSource.filter method.

Then we add the table element with the input box that runs applyFilter to let us filter the items.

Sorting and Pagination

We can add sorting and pagination to our table.

For example, we can write:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatTableModule,
    MatPaginatorModule,
    MatSortModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';

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

const COLORS: string[] = [
  'maroon', 'red', 'orange', 'yellow', 'olive', 'green'
];
const NAMES: string[] = [
  'Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack'
];

const createNewUser = (id: number): UserData => {
  const name = `${NAMES[Math.round(Math.random() * (NAMES.length - 1))]} ${NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0)}`;
  return {
    id: id.toString(),
    name: name,
    progress: Math.round(Math.random() * 100).toString(),
    color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
  };
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  displayedColumns: string[] = ['id', 'name', 'progress', 'color'];
  dataSource: MatTableDataSource<UserData>;

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;

  constructor() {
    const users = Array.from({ length: 100 }, (_, k) => createNewUser(k + 1));
    this.dataSource = new MatTableDataSource(users);
  }

  ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }
}

app.component.html

<div>
  <div class="mat-elevation-z8">
    <table mat-table [dataSource]="dataSource" matSort>
      <ng-container matColumnDef="id">
        <th mat-header-cell *matHeaderCellDef mat-sort-header> ID </th>
        <td mat-cell *matCellDef="let row"> {{row.id}} </td>
      </ng-container>

      <ng-container matColumnDef="progress">
        <th mat-header-cell *matHeaderCellDef mat-sort-header> Progress </th>
        <td mat-cell *matCellDef="let row"> {{row.progress}}% </td>
      </ng-container>

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

      <ng-container matColumnDef="color">
        <th mat-header-cell *matHeaderCellDef mat-sort-header> Color </th>
        <td mat-cell *matCellDef="let row" [style.color]="row.color">
          {{row.color}} </td>
      </ng-container>

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

      <tr class="mat-row" *matNoDataRow>
        <td class="mat-cell" colspan="4">No data matching the filter
          "{{input.value}}"</td>
      </tr>
    </table>

    <mat-paginator [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>
  </div>
</div>

styles.css

table {
  width: 100%;
}

We add the MatSortModule to let us add sorting for the columns.

MatPaginatorModule adds the paginator control to let us move through the pages and select the page size.

We create the entries for the table in app.component.ts with the createNewUser function.

In the AppComponent class, we just create the data source and set it as the dataSource with the MatTableDataSource constructor.

And we set the paginator and sort properties to the paginator and sort components that we added.

The matSort directive lets us add sorting to the table.

And the mat-sort-header lets us enable sorting to the column header cells.

The mat-paginator component is added at the bottom to let us show the pagination controls

Conclusion

We can add tables with sorting, filtering, and pagination with Angular Material.

Categories
Angular Material

Angular Material — Tables

Angular Material is a popular UI framework based on Material Design for Angular.

In this article, we’ll look at how to use Angular Material into our Angular project.

Tables

We can add a table with Material Design style with Angular Material.

For example, we can write:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatTableModule } from '@angular/material/table';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatTableModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';

export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
  { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
  { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
  { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
  { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
];

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = ELEMENT_DATA;
}

app.component.html

<div>
  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
    <ng-container matColumnDef="position">
      <th mat-header-cell *matHeaderCellDef> No. </th>
      <td mat-cell *matCellDef="let element"> {{element.position}} </td>
    </ng-container>

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

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

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

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

styles.css

table {
    width: 100%;
}

We add the MatTableModule to add the table.

Then in app.component.ts , we add the table data in the ELEMENT_DATA array.

The displayColumns array has the column names.

In app.component.html , we add the mat-table directive to make the table Material Design style.

dataSource has the data table’s data source.

The ng-container components have the column definitions.

matColumnDef have the columns.

The th and td are in the ng-container and they’ll be shown in the table.

In styles.css , we set the width to 100% to make the table fill the screen.

Expandable Rows

We can add expandable rows into our table.

For example, we can write:

app.component.ts

import { Component } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';

export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: PeriodicElement[] = [
  { position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
  { position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
  { position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
  { position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
  { position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
];

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('detailExpand', [
      state('collapsed', style({ height: '0px', minHeight: '0' })),
      state('expanded', style({ height: '*' })),
      transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
    ]),
  ],
})
export class AppComponent {
  dataSource = ELEMENT_DATA;
  columnsToDisplay = ['name', 'weight', 'symbol', 'position'];
  expandedElement: PeriodicElement | null;
}

styles.css

table {
  width: 100%;
}

tr.example-detail-row {
  height: 0 !important;
}

tr.example-element-row:not(.example-expanded-row):hover {
  background: whitesmoke;
}

tr.example-element-row:not(.example-expanded-row):active {
  background: #efefef;
}

.example-element-row td {
  border-bottom-width: 0;
}

.example-element-detail {
  overflow: hidden;
  display: flex;
}

.example-element-diagram {
  min-width: 80px;
  border: 2px solid black;
  padding: 8px;
  font-weight: lighter;
  margin: 8px 0;
  height: 104px;
}

.example-element-symbol {
  font-weight: bold;
  font-size: 40px;
  line-height: normal;
}

.example-element-description {
  padding: 16px;
}

.example-element-description-attribution {
  opacity: 0.5;
}

app.component.html

<div>
  <table mat-table [dataSource]="dataSource" multiTemplateDataRows
    class="mat-elevation-z8">
    <ng-container matColumnDef="{{column}}"
      *ngFor="let column of columnsToDisplay">
      <th mat-header-cell *matHeaderCellDef> {{column}} </th>
      <td mat-cell *matCellDef="let element"> {{element[column]}} </td>
    </ng-container>

    <ng-container matColumnDef="expandedDetail">
      <td mat-cell *matCellDef="let element"
        [attr.colspan]="columnsToDisplay.length">
        <div class="example-element-detail"
          [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
          <div class="example-element-diagram">
            <div class="example-element-position"> {{element.position}} </div>
            <div class="example-element-symbol"> {{element.symbol}} </div>
            <div class="example-element-name"> {{element.name}} </div>
            <div class="example-element-weight"> {{element.weight}} </div>
          </div>
        </div>
      </td>
    </ng-container>

  <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
    <tr mat-row *matRowDef="let element; columns: columnsToDisplay;"
      class="example-element-row"
      [class.example-expanded-row]="expandedElement === element"
      (click)="expandedElement = expandedElement === element ? null : element">
    </tr>
    <tr mat-row *matRowDef="let row; columns: ['expandedDetail']"
      class="example-detail-row"></tr>
  </table>
</div>

We keep app.module.ts the same as before.

In app.component.ts , we add the detailExpand animation to show an animation when we toggle the table row.

In app.component.html , we added a new ng-container with the matColumnDef set to expandedDetail .

It spans the whole table’s width.

The detailExpand animation is used in this div.

The columns property in the bottom is set to [‘expandedDetail’] to add the detail column.

Conclusion

We can add basic tables and tables with expanded rows with Angular Material.