import { Component, OnInit, Input, ViewChild, Output, EventEmitter, ChangeDetectorRef, OnDestroy, SimpleChanges } from '@angular/core'; import { MatSort, Sort } from '@angular/material/sort'; import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator'; import { MatTableDataSource } from '@angular/material/table'; import { Event } from '@angular/router'; import { FormControl, FormGroup } from '@angular/forms'; import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; import { Observable, Subject, Subscription } from 'rxjs'; import { SelectionModel } from '@angular/cdk/collections'; @Component({ selector: 'lib-table', templateUrl: './table.component.html', styleUrls: ['./table.component.scss'] }) export class TableComponent implements OnInit, OnDestroy { @Input() searchMessage = 'Search'; @Input() showEmptymessage = true; @Input() loaded = false; @Input() selection = new SelectionModel(true, []); @Input() hasRefreshButton = false; @Input() hasSearchBar= true; @Input() title = ''; @Input() hasSearchbar = true; @Input() data = []; @Input() headers = []; @Input() numItems : number =0; @Input() backgroundColor = 'dark-back'; @Output() clickColumn : EventEmitter = new EventEmitter(); @Output() refreshEvent: EventEmitter = new EventEmitter(); @Output() selectedItemsEvent: EventEmitter = new EventEmitter(); @Output() onPageEvent: EventEmitter = new EventEmitter(); @Output() filterEvent: EventEmitter = new EventEmitter(); @Output() sortEvent: EventEmitter = new EventEmitter(); @Output() walkthroughEvent: EventEmitter = new EventEmitter(); @ViewChild(MatPaginator, {static: false}) paginator: MatPaginator = new MatPaginator(new MatPaginatorIntl(), ChangeDetectorRef.prototype); dataSource = new MatTableDataSource([]); displayedColumns: string[] = []; isCredential : boolean = false; tableFilter = null; userQuestionUpdate = new Subject(); subscriptions: Subscription = new Subscription(); // HTML References @ViewChild(MatSort, {static: true}) sort: MatSort = new MatSort(); constructor( private detectChanges : ChangeDetectorRef ) { this.subscriptions.add( this.userQuestionUpdate.pipe( debounceTime(400), distinctUntilChanged()) .subscribe((value:any) => { this.applyFilter(); }) ); } /** * This form group for the filter */ filterForm = new FormGroup({ filterby: new FormControl(''), }); ngOnDestroy(){ this.subscriptions.unsubscribe(); } ngOnInit() { } ngOnChanges(changes: SimpleChanges) { this.loaded ? this.walkthroughEvent.emit():''; this.dataSource.data =[]; this.dataSource.data = this.data; this.getDisplayedColumns(); // When data is deleted from table and the user is on the last page re-assigns the pageIndex to avoid overflow if(changes.numItems && this.paginator){ if(changes.numItems.currentValue < changes.numItems.previousValue && this.paginator.pageIndex === this.paginator.getNumberOfPages()-1){ if(this.paginator.getNumberOfPages()*this.paginator.pageSize > Math.ceil(changes.numItems.currentValue/this.paginator.pageSize)*this.paginator.pageSize){ this.paginator.pageIndex = this.paginator.pageIndex -1 } } } this.numItems = this.numItems ? this.numItems : this.data.length; this.dataSource.sort = this.sort; if(JSON.stringify(this.headers).includes('expirationDate')){ this.isCredential = true }else{ this.isCredential = false } } clickOnCell(header:any,element:any, index:any){ this.clickColumn.emit({header, element, index}); } /** * Filter the value of search input into data source specific column * @param event */ applyFilter() { if ( this.filterForm.value.filterby !== '') { this.tableFilter = this.filterForm.value.filterby if(this.paginator){ this.paginator.firstPage(); } this.filterEvent.emit(this.tableFilter); } else { this.tableFilter = null; this.filterEvent.emit(this.tableFilter); } } /** * Emit a sort event * @param $event The sort event */ sortData($event:any) { if ($event.active && $event.direction !== '') { this.paginator.firstPage(); this.sortEvent.emit($event) } } /** * Custom filter to search values inside of data source in columns * with nested objects. This function converts a json to string and search * the term and return a boolean when the term are found into result string. * @param data * @param filterValue */ filterPredicateFm() { return (data: any, filterValue: string): boolean => { let objectString = ''; this.displayedColumns.forEach((column :any)=> { objectString += JSON.stringify(data[column]); }); if( objectString.toLowerCase().includes(filterValue.trim().toLocaleLowerCase()) ){ return true; } return false; }; } /** * Selects all rows if they are not all selected; otherwise clear selection. */ masterToggle() { this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach((row:any) => this.selection.select(row)); this.detectChanges.detectChanges(); } /** * Refresh function. Here the event is emmitted and each component can handle the refresh function as it sees fit * */ refresh() { this.paginator.firstPage(); this.refreshEvent.emit() } /** * Whether the number of selected elements matches the total number of rows. */ isAllSelected() { const numSelected = this.selection.selected.length; const numRows = this.dataSource.data.length; return numSelected === numRows; } /** * The label for the checkbox on the passed row * @param row */ checkboxLabel(row?: any): string { if (!row) { return `${this.isAllSelected() ? 'select' : 'deselect'} all`; } this.selectedItemsEvent.emit(this.selection.selected.map((val:any)=>val._id)); return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row._id + 1}`; } /** * This function get the visible columns in base of the headers reeived and adding a * select string * * @memberof TableComponent */ getDisplayedColumns() { this.displayedColumns = []; this.displayedColumns.push('select'); this.headers.forEach((header:any) => this.displayedColumns.push(header.key)); } /** * When change de page or the number of items * * * @param $event */ pageEvent($event:any){ $event["filterby"] = this.tableFilter; this.onPageEvent.emit($event); } clickCheckbox($event:any){ $event.stopPropagation(); this.detectChanges.detectChanges(); } changeCheckbox($event:any, row:any){ $event ? this.selection.toggle(row) : null this.detectChanges.detectChanges(); } }