2

I looked around and found no answer that supports n number of tables in one page where each table has its own filter, sort and pagination.

On https://material.angular.io/ they have given an example of one table for a page.

This is what I've written for one table-

HTML

<mat-form-field>
    <mat-label>Filter</mat-label>
    <input matInput (keyup)="applyFilter($event)" placeholder="Ex. Mia">
</mat-form-field>

<table mat-table [dataSource]="dataSource" multiTemplateDataRows matSort>

    <ng-container matColumnDef="{{column}}" *ngFor="let column of columnsToDisplay">
        <th mat-header-cell *matHeaderCellDef mat-sort-header> {{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="table-element-detail" [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
                <div class="table-element-diagram">
                    <div class="table-element-position"> {{element.position}} </div>
                    <div class="table-element-symbol"> {{element.symbol}} </div>
                    <div class="table-element-name"> {{element.name}} </div>
                    <div class="table-element-weight"> {{element.weight}} </div>
                </div>
                <div class="table-element-description">
                    {{element.description}}
                    <span class="table-element-description-attribution"> -- Wikipedia </span>
                </div>
            </div>
        </td>
    </ng-container>

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

</table>

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

TS

import { Component, ViewChild, OnInit, AfterViewInit } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { animate, state, style, transition, trigger } from '@angular/animations';

@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 implements OnInit, AfterViewInit {

    @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
    @ViewChild(MatSort, { static: true }) sort: MatSort;

    columnsToDisplay = ['name', 'weight', 'symbol', 'position'];
    expandedElement: PeriodicElement | null;
    dataSource: MatTableDataSource<PeriodicElement>;

    constructor() { }

    ngOnInit() {
        this.dataSource = new MatTableDataSource(ELEMENT_DATA);
    }

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

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

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

const ELEMENT_DATA: PeriodicElement[] = [
    {
        position: 1,
        name: 'Hydrogen',
        weight: 1.0079,
        symbol: 'H',
        description: `Hydrogen is a chemical element with symbol H and atomic number 1. With a standard
            atomic weight of 1.008, hydrogen is the lightest element on the periodic table.`
    }, {
        position: 2,
        name: 'Helium',
        weight: 4.0026,
        symbol: 'He',
        description: `Helium is a chemical element with symbol He and atomic number 2. It is a
            colorless, odorless, tasteless, non-toxic, inert, monatomic gas, the first in the noble gas
            group in the periodic table. Its boiling point is the lowest among all the elements.`
    }, {
        position: 3,
        name: 'Lithium',
        weight: 6.941,
        symbol: 'Li',
        description: `Lithium is a chemical element with symbol Li and atomic number 3. It is a soft,
            silvery-white alkali metal. Under standard conditions, it is the lightest metal and the
            lightest solid element.`
    }, {
        position: 4,
        name: 'Beryllium',
        weight: 9.0122,
        symbol: 'Be',
        description: `Beryllium is a chemical element with symbol Be and atomic number 4. It is a
            relatively rare element in the universe, usually occurring as a product of the spallation of
            larger atomic nuclei that have collided with cosmic rays.`
    }, {
        position: 5,
        name: 'Boron',
        weight: 10.811,
        symbol: 'B',
        description: `Boron is a chemical element with symbol B and atomic number 5. Produced entirely
            by cosmic ray spallation and supernovae and not by stellar nucleosynthesis, it is a
            low-abundance element in the Solar system and in the Earth's crust.`
    }, {
        position: 6,
        name: 'Carbon',
        weight: 12.0107,
        symbol: 'C',
        description: `Carbon is a chemical element with symbol C and atomic number 6. It is nonmetallic
            and tetravalent—making four electrons available to form covalent chemical bonds. It belongs
            to group 14 of the periodic table.`
    }, {
        position: 7,
        name: 'Nitrogen',
        weight: 14.0067,
        symbol: 'N',
        description: `Nitrogen is a chemical element with symbol N and atomic number 7. It was first
            discovered and isolated by Scottish physician Daniel Rutherford in 1772.`
    }, {
        position: 8,
        name: 'Oxygen',
        weight: 15.9994,
        symbol: 'O',
        description: `Oxygen is a chemical element with symbol O and atomic number 8. It is a member of
            the chalcogen group on the periodic table, a highly reactive nonmetal, and an oxidizing
            agent that readily forms oxides with most elements as well as with other compounds.`
    }, {
        position: 9,
        name: 'Fluorine',
        weight: 18.9984,
        symbol: 'F',
        description: `Fluorine is a chemical element with symbol F and atomic number 9. It is the
            lightest halogen and exists as a highly toxic pale yellow diatomic gas at standard
            conditions.`
    }, {
        position: 10,
        name: 'Neon',
        weight: 20.1797,
        symbol: 'Ne',
        description: `Neon is a chemical element with symbol Ne and atomic number 10. It is a noble gas.
            Neon is a colorless, odorless, inert monatomic gas under standard conditions, with about
            two-thirds the density of air.`
    },
];

CSS

table {
    width: 100%;
}

tr.table-detail-row {
    height: 0;
}

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

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

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

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

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

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

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

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

The code above is mostly taken from https://material.angular.io/ has no issues and works wonders. Now, all I'm looking for is something that could make this table work inside *ngFor supporting filter, sort and pagination.

1 Answer 1

4

You're going to need to use @ViewChildren instead of @ViewChild and make use of QueryList to read childrens effectively.

You'll also need ngAfterViewInit() once the initialization of a component's view is completed to update paginator and sort.

Please read between the lines for comments.

TS

import { Component, ViewChild, OnInit, AfterViewInit, QueryList, ViewChildren } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { animate, state, style, transition, trigger } from '@angular/animations';

@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 implements OnInit, AfterViewInit {

    // @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
    // @ViewChild(MatSort, { static: true }) sort: MatSort;

    // using ViewChildren and QueryList
    @ViewChildren(MatPaginator) paginator = new QueryList<MatPaginator>();
    @ViewChildren(MatSort) sort = new QueryList<MatSort>();

    // columnsToDisplay = ['name', 'weight', 'symbol', 'position'];
    // expandedElement: PeriodicElement | null;
    // dataSource: MatTableDataSource<PeriodicElement>;

    // our loop runs on this
    tables: any = [];

    constructor() { }

    ngOnInit() {
        // filling in each table data for table 1, 2, and 3
        // use it however you'd like to
        [1, 2, 3].forEach(tableNumber => {
            this.tables.push({
                columnsToDisplay: ['name', 'weight', 'symbol', 'position'],
                expandedElement: null,
                dataSource: new MatTableDataSource(ELEMENT_DATA),
                paginator: null,
                sort: null
            });
        });
    }

    ngAfterViewInit(): void {
        // this.dataSource.paginator = this.paginator;
        // this.dataSource.sort = this.sort;
        for (let i = 0; i < this.tables.length; i++) {
            this.tables[i].dataSource.paginator = this.paginator.toArray()[i];
            this.tables[i].dataSource.sort = this.sort.toArray()[i];
        }
    }

    // dataSource is a parameter here
    applyFilter(event: Event, dataSource: any) {
        const filterValue = (event.target as HTMLInputElement).value;
        dataSource.filter = filterValue.trim().toLowerCase();
        if (dataSource.paginator) {
            dataSource.paginator.firstPage();
        }
    }
}

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

const ELEMENT_DATA: PeriodicElement[] = [
    {
        position: 1,
        name: 'Hydrogen',
        weight: 1.0079,
        symbol: 'H',
        description: `Hydrogen is a chemical element with symbol H and atomic number 1. With a standard
            atomic weight of 1.008, hydrogen is the lightest element on the periodic table.`
    }, {
        position: 2,
        name: 'Helium',
        weight: 4.0026,
        symbol: 'He',
        description: `Helium is a chemical element with symbol He and atomic number 2. It is a
            colorless, odorless, tasteless, non-toxic, inert, monatomic gas, the first in the noble gas
            group in the periodic table. Its boiling point is the lowest among all the elements.`
    }, {
        position: 3,
        name: 'Lithium',
        weight: 6.941,
        symbol: 'Li',
        description: `Lithium is a chemical element with symbol Li and atomic number 3. It is a soft,
            silvery-white alkali metal. Under standard conditions, it is the lightest metal and the
            lightest solid element.`
    }, {
        position: 4,
        name: 'Beryllium',
        weight: 9.0122,
        symbol: 'Be',
        description: `Beryllium is a chemical element with symbol Be and atomic number 4. It is a
            relatively rare element in the universe, usually occurring as a product of the spallation of
            larger atomic nuclei that have collided with cosmic rays.`
    }, {
        position: 5,
        name: 'Boron',
        weight: 10.811,
        symbol: 'B',
        description: `Boron is a chemical element with symbol B and atomic number 5. Produced entirely
            by cosmic ray spallation and supernovae and not by stellar nucleosynthesis, it is a
            low-abundance element in the Solar system and in the Earth's crust.`
    }, {
        position: 6,
        name: 'Carbon',
        weight: 12.0107,
        symbol: 'C',
        description: `Carbon is a chemical element with symbol C and atomic number 6. It is nonmetallic
            and tetravalent—making four electrons available to form covalent chemical bonds. It belongs
            to group 14 of the periodic table.`
    }, {
        position: 7,
        name: 'Nitrogen',
        weight: 14.0067,
        symbol: 'N',
        description: `Nitrogen is a chemical element with symbol N and atomic number 7. It was first
            discovered and isolated by Scottish physician Daniel Rutherford in 1772.`
    }, {
        position: 8,
        name: 'Oxygen',
        weight: 15.9994,
        symbol: 'O',
        description: `Oxygen is a chemical element with symbol O and atomic number 8. It is a member of
            the chalcogen group on the periodic table, a highly reactive nonmetal, and an oxidizing
            agent that readily forms oxides with most elements as well as with other compounds.`
    }, {
        position: 9,
        name: 'Fluorine',
        weight: 18.9984,
        symbol: 'F',
        description: `Fluorine is a chemical element with symbol F and atomic number 9. It is the
            lightest halogen and exists as a highly toxic pale yellow diatomic gas at standard
            conditions.`
    }, {
        position: 10,
        name: 'Neon',
        weight: 20.1797,
        symbol: 'Ne',
        description: `Neon is a chemical element with symbol Ne and atomic number 10. It is a noble gas.
            Neon is a colorless, odorless, inert monatomic gas under standard conditions, with about
            two-thirds the density of air.`
    },
];

HTML

<!-- adding loop -->
<ng-container *ngFor="let table of tables; index as tableIndex;">

    <mat-form-field>
        <mat-label>Filter</mat-label>
        <!-- updating applyFilter with current table's dataSource parameter -->
        <input matInput (keyup)="applyFilter($event, table.dataSource)" placeholder="Ex. Mia">
    </mat-form-field>

    <!-- using current table's dataSource -->
    <table mat-table [dataSource]="table.dataSource" multiTemplateDataRows matSort>

        <!-- using current table's columnsToDisplay -->
        <ng-container matColumnDef="{{column}}" *ngFor="let column of table.columnsToDisplay">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> {{column}} </th>
            <td mat-cell *matCellDef="let element"> {{element[column]}} </td>
        </ng-container>

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

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

    </table>

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

</ng-container>

Code above works if paginator and sort are sequential. If they are not and any other paginator or sort is used in between, this will fail. To prevent this from happening, you can have dynamic id's set to them and then assign them to their corresponding datasource.

There are also other ways to do the same thing. e.g. using another component that has table and component-interaction. But that will require your table to follow the same data structure or mostly depends on how you use it.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.