简体   繁体   中英

Angular Material data table column sort by date range

How to sort a date column based on the date range? Angular material data table. I am working a project now facing a problem on how to date column sort data based on the date range fromDate and toDate using with filterPredicate or any other option in mat-table.

The date column will be shown in between date range. Please refer screenshot and look at the project in stackblitz here在此处输入图片说明

https://stackblitz.com/edit/angular-pkkvbd-cdtxwz-date-range-filter?embed=1&file=app/table-filtering-example.ts

If I selected 1 Jan 2019 to 31 Dec 2020 the data will be shown all the between date results

TL;DR: https://stackblitz.com/edit/angular-pkkvbd-cdtxwz-date-range-filter-jzlwxr?file=app/table-filtering-example.ts

You want to not only sorting but also filtering your table. These are separated concerns in angular material, considering you are using angular material. You will have to provide the filtering components first and use that data to implement a filterPredicate function manually , yes manually.

Filtering:

export class TableFilteringExample {
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource(ELEMENT_DATA);

  applyFilter(filterValue: string) {
    this.dataSource.filterPredicate = filterPeriod;
  }

  filterPeriod(data: T, filter: string) {
    return data.referenceDate > startDateFilter.value() && data.referenceDate < endDateFilter.value();
  }
}

Sorting functions are also available for your material.table component, but this component comes out-of-the-box for you. Refer to https://material.angular.io/components/table/overview#sorting on how to use the MatSort component properly:

<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
...

then, in your .ts component, you will refer to the matSort as:

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

You need to complete several steps if you want to complete the story and a lettle of reading to understand how to adapt the material components to your case. I suggest you to start your jorney here https://material.angular.io/components/table/overview#sorting

In order to achieve your expected result, you need to change the dataSource type. Also you need a method to rebuild your array of items based on the user's date range selection.

Your xxx.component.ts should look like this.

import { Component } from '@angular/core';
import { MatTableDataSource } from '@angular/material';
import { DatePipe } from '@angular/common';
import {FormControl, FormGroup} from '@angular/forms';
import * as moment from 'moment';

export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  DOB: Date;
  created: Date;
}

const ELEMENT_DATA: PeriodicElement[] = [
  { position: 1, name: 'Hydrogen', weight: 1.0079, DOB: new Date(2016, 11, 24), created: new Date(2015, 15, 24) },
  { position: 2, name: 'Helium', weight: 4.0026, DOB: new Date(2018, 18, 24), created: new Date(2018, 11, 24) },
  { position: 3, name: 'Lithium', weight: 6.941, DOB: new Date(1993, 6, 12), created: new Date(1999, 12, 15) },
  { position: 4, name: 'Beryllium', weight: 9.0122, DOB: new Date(2001, 7, 6), created: new Date(2011, 10, 6) },
  { position: 5, name: 'Boron', weight: 10.811, DOB: new Date(2020, 5, 9), created: new Date(2020, 5, 9) },
  { position: 6, name: 'Carbon', weight: 12.0107, DOB: new Date(2008, 7, 14), created: new Date(2008, 7, 14) },
  { position: 7, name: 'Nitrogen', weight: 14.0067, DOB: new Date(1998, 11, 18), created: new Date(1998, 11, 18) },
  { position: 8, name: 'Oxygen', weight: 15.9994, DOB: new Date(2002, 2, 24), created: new Date(2002, 2, 24) },
  { position: 9, name: 'Fluorine', weight: 18.9984, DOB: new Date(2006, 4, 29), created: new Date(2006, 4, 29) },
  { position: 10, name: 'Neon', weight: 20.1797, DOB: new Date(2040, 5, 30), created: new Date(2040, 5, 30) },
];

/**
 * @title Table with filtering
 */
@Component({
  selector: 'table-filtering-example',
  styleUrls: ['table-filtering-example.css'],
  templateUrl: 'table-filtering-example.html',
})
export class TableFilteringExample {
  displayedColumns: string[] = ['position', 'name', 'weight', 'DOB', 'founded'];
  dataSource = ELEMENT_DATA;
  pipe: DatePipe;

filterForm = new FormGroup({
    fromDate: new FormControl(),
    toDate: new FormControl(),
});

get fromDate() { return this.filterForm.get('fromDate'); }
get toDate() { return this.filterForm.get('toDate'); }

  constructor() {
  }

  getDateRange(value) {
    // getting date from calendar
    const fromDate = value.fromDate
    const toDate = value.toDate
    const tempData = <any>this.dataSource;
    let selectedItems: PeriodicElement[] = [];
    if(fromDate !== '' && toDate !== '') {
              tempData.forEach((item, index) => {
            if (item.DOB >= fromDate && item.DOB <= toDate) {
                selectedItems.push(item);
            }
        });

        this.dataSource = selectedItems;
    }
  }


  applyFilter(filterValue: string) {
    // this.dataSource.filter = filterValue.trim().toLowerCase();
  }
}

You can use filter function.

getDateRange(value) {
    this.dataSource.data = ELEMENT_DATA;
    const fromDate = value.fromDate
    const toDate = value.toDate
    this.dataSource.data = this.dataSource.data.filter(e=>e.DOB > fromDate && e.DOB < toDate ) ;
  }

The filter() method creates a new array with all elements that pass the test implemented by the provided function.

More information about filter function

Stackblitz example based on your example.

If you want to apply custom behavior for filtering and sorting then you can apply something like this:

Custom filtering example (inside your constructor):

this.dataSourceUPs.filterPredicate = (data: UPs, filter: string) => {
  filter = filter.trim().toLowerCase();
  return this.contains(data.nombreUP, filter) || this.contains(data.ciudad, filter) || this.contains(Number(data.fechaInicio.split("T")[0].split("-")[2]), filter);
}

Custom Sorting example:

compare(a: number | string, b: number | string, isAsc: boolean) {
    return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
sortDataUPs(sort: Sort) {
    const data = this.dataSourceUPs.data.slice();
    if (!sort.active || sort.direction === '') {
      this.dataSourceUPs.data = data;
      return;
    }

    this.dataSourceUPs.data = data.sort((a, b) => {
      const isAsc = sort.direction === 'asc';
      switch (sort.active) {
        case 'periodo': return this.compare(a.periodo, b.periodo, isAsc);
        case 'nombreUP': return this.compare(a.nombreUP, b.nombreUP, isAsc);
        case 'ciudad': return this.compare(a.ciudad, b.ciudad, isAsc);
        case 'programa': return this.compare(a.nombreEdicion + '-' + a.nombrePrograma, b.nombreEdicion + '-' + b.nombrePrograma, isAsc);
        default: return 0;
      }
    });
  }

The case are the values of what you put into the matColumnDef in your html ng-container, which are the column names you put into your displayedColumns array. The only thing left to do is to apply a binding to your table like this:

<table mat-table [dataSource]="dataSourceUPs" class="mat-elevation-z8" style="width: 100%;" matSort (matSortChange)="sortDataUPs($event)">

You can note that the case "programa" is customized. Most likely you will need to make the proper importation statements, like for example for the Sort type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM