Categories
JavaScript Basics

Highlights of JavaScript — Popup Windows and Input Validation

To learn JavaScript, we must learn the basics.

In this article, we’ll look at the most basic parts of the JavaScript language.

Opening Popup Windows

We can open popup windows with the window.open method.

For example, we can write:

const monkeyWindow = window.open();

To add some content to the window, we can write:

const monkeyWindow = window.open();
const windowContent = `<h1>hello world</h1>`;
monkeyWindow.document.write(windowContent);

We call document.write on the monkeyWindow to show the content.

Also, we can show the content of a URL with the location.assign method:

const monkeyWindow = window.open();
monkeyWindow.location.assign("https://www.google.com");

Now we should see Google displayed in the new window.

We can also write:

const monkeyWindow = window.open();
monkeyWindow.location.href = "https://www.google.com";

to do the same thing.

To close a window, we can write:

const monkeyWindow = window.open();
monkeyWindow.location.href = "https://www.google.com";
monkeyWindow.close();

Controlling the Window’s Size and Location

We can set the popup window’s size and location with a string in the 3rd argument of window.open .

For example, we can write:

const monkeyWindow = window.open("https://www.google.com", "win1", "width=420,height=380");

Now the window is 420px in width and 380px for height.

To change its location, we can write:

const monkeyWindow = window.open("https://www.google.com", "win1", "width=420,height=380,left=200,top=100");

to add the left and top keys to set the position.

Form Validation

We can add form validation to a form.

For example, we can write the following HTML:

<form onSubmit="return checkForLastName();">
  Please enter your last name.<br>
  <input type="text" id="lastNameField">
  <input type="submit" value="Submit">
</form>

And we can add the following JavaScript to add the validation:

const checkForLastName = () => {
  if (document.getElementById("lastNameField").value.length === 0) {
    alert("Please enter your last name");
    return false;
  }
}

In the checkForLastName function, we get the value of the input with the ID lastNameField .

If the value ‘s length is 0, then we show the alert.

The return in the onSubmit attribute value makes sure that we prevent the default submit behavior along with the return false in checkForLastName so that we can run the JavaScript validation.

To improve the user experience, we can call focus on the element:

const checkForLastName = () => {
  const lastNameField = document.getElementById("lastNameField")
  if (lastNameField.value.length === 0) {
    alert("Please enter your last name");
    lastNameField.focus();
    return false;
  }
}

We call focus on the input element so that the user can type in the text without clicking on the input.

Also, we can add background color so that the user can see the input field more easily:

const checkForLastName = () => {
  const lastNameField = document.getElementById("lastNameField")
  if (lastNameField.value.length === 0) {
    alert("Please enter your last name");
    lastNameField.focus();
    lastNameField.style.background = "yellow";
    return false;
  }
  lastNameField.style.background = "white";
}

We set the background color by setting the lastNameField.style.background property.

Conclusion

We can open windows with our own content.

Also, we should add form validation for fields so that users always input valid data.

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.