import { SelectionChange , SelectionModel } from '@angular/cdk/collections' ; import { ComponentType } from '@angular/cdk/portal' ; import { AfterViewInit , Directive , ElementRef , OnDestroy , OnInit , ViewChild } from '@angular/core' ; import { MatPaginator } from '@angular/material/paginator' ; import { MatSort } from '@angular/material/sort' ; import { MatTableDataSource } from '@angular/material/table' ; import { untilDestroy } from '@ngx-starter-kit/ngx-utils' ; import { parseISO } from 'date-fns' ; import { Observable } from 'rxjs' ; import { concatMap , filter , map } from 'rxjs/operators' ; import { EntityFormComponent } from './entity-form.component' ; import { Entity , EntityColumnDef } from './entity.model' ; import { EntityService } from './entity.service' ; @ Directive ( ) export abstract class EntitiesComponent < TEntity extends Entity , TService extends EntityService < TEntity > > implements OnInit , OnDestroy , AfterViewInit { dataSource = new MatTableDataSource < TEntity > ( [ ] ) ; selection = new SelectionModel < TEntity > ( false , [ ] ) ; @ ViewChild ( MatPaginator , { static : true } ) paginator : MatPaginator ; @ ViewChild ( MatSort , { static : true } ) sort : MatSort ; @ ViewChild ( 'filter' , { static : true } ) filterRef : ElementRef ; readonly loading$ ; readonly columns : Array < EntityColumnDef < TEntity > > ; readonly title ? : string = null ; readonly showToolbar ? : boolean = false ; readonly showColumnFilter ? : boolean = false ; readonly showActionColumn ? : boolean = false ; readonly maxSelectable ? : number = 1 ; readonly actionColumn ? : string = 'Actions' ; readonly selectColumn ? : string = 'select' ; readonly formRef ? : ComponentType < EntityFormComponent < TEntity > > ; getNewEntity ? ( ) ; filterPredicate ? ( entity , filter ) ; constructor ( protected entityService ) { this . loading$ = entityService . loading$ ; } ngOnInit ( ) { this . selection = new SelectionModel < TEntity > ( this . maxSelectable > 1 , [ ] ) ; this . update ( ) . subscribe ( ) ; if ( this . filterPredicate ) { this . dataSource . filterPredicate = this . filterPredicate ; } if ( this . maxSelectable > 1 ) { this . selection . changed . pipe ( filter ( ( sc ) => sc . added . length > 0 ) , filter ( ( _ ) => this . selection . selected . length > this . maxSelectable ) , untilDestroy ( this ) ) . subscribe ( ( _ ) => this . selection . deselect ( this . selection . selected . shift ( ) ) ) ; } } ngOnDestroy ( ) { } ngAfterViewInit ( ) { this . dataSource . sort = this . sort ; this . dataSource . paginator = this . paginator ; } getById ( id ) { return this . entityService . getById ( id ) ; } delete ( item ) { return this . entityService . delete ( item . id ) . pipe ( concatMap ( ( _ ) => this . update ( ) ) ) ; } updateOrCreate ( entity , id ) { if ( id ) { return this . entityService . put ( id , entity ) . pipe ( concatMap ( ( _ ) => this . update ( ) ) ) ; } else { return this . entityService . post ( entity ) . pipe ( concatMap ( ( _ ) => this . update ( ) ) ) ; } } /**
     * Overwrite this method, to get the data your way.
     */ getData ( ) { return this . entityService . getAll ( ) ; } protected update ( ) { return this . getData ( ) . pipe ( map ( ( result ) => { this . dataSource = new MatTableDataSource < TEntity > ( result ) ; this . dataSource . sort = this . sort ; this . dataSource . paginator = this . paginator ; } ) ) ; } /** Whether all filtered rows are selected. */ isAllFilteredRowsSelected ( ) { return this . dataSource . filteredData . every ( ( data ) => this . selection . isSelected ( data ) ) ; } /** Whether the selection it totally matches the filtered rows. */ isMasterToggleChecked ( ) { return ( this . selection . hasValue ( ) && this . isAllFilteredRowsSelected ( ) && this . selection . selected . length >= this . dataSource . filteredData . length ) ; } /**
     * Whether there is a selection that doesn't capture all the
     * filtered rows there are no filtered rows displayed.
     */ isMasterToggleIndeterminate ( ) { return this . selection . hasValue ( ) && ( ! this . isAllFilteredRowsSelected ( ) || ! this . dataSource . filteredData . length ) ; } /** Selects all filtered rows if they are not all selected; otherwise clear selection. */ masterToggle ( ) { if ( this . isMasterToggleChecked ( ) ) { this . selection . clear ( ) ; } else { this . dataSource . filteredData . forEach ( ( data ) => this . selection . select ( data ) ) ; } } applyFilter ( filterValue ) { filterValue = filterValue . trim ( ) ; filterValue = filterValue . toLowerCase ( ) ; this . dataSource . filter = filterValue ; } get displayedColumns ( ) : string [ ] { let displayedColumns = this . columns . filter ( column => column . visible ) . map ( x => x . property ) ; if ( this . maxSelectable > 0 ) { displayedColumns . unshift ( this . selectColumn ) ; } if ( this . showActionColumn ) { displayedColumns = displayedColumns . concat ( this . actionColumn ) ; } return displayedColumns ; } selectRow ( entity ) { if ( this . maxSelectable > 0 ) { this . selection . toggle ( entity ) ; if ( this . selection . isSelected ( entity ) ) { this . showDetails ( entity ) ; } else { this . showDetails ( undefined ) ; } } } getRouteAnimation ( outlet ) { return outlet . activatedRouteData . depth || 5 ; } /**
     * will be called with entity or undefined
     */ showDetails ( entity ) { } toggleColumnVisibility ( column , event ) { event . stopPropagation ( ) ; event . stopImmediatePropagation ( ) ; column . visible = ! column . visible ; } protected stringToDate ( date ) { function isString ( x ) { return Object . prototype . toString . call ( x ) === '[object String]' ; } return isString ( date ) ? parseISO ( date as string ) : ( date as number | Date ) ; } }