File

src/lib/table-data-source.directive.ts

Implements

OnInit OnDestroy

Metadata

Index

Properties
Methods
Inputs
Accessors

Constructor

constructor(matTable: CdkTable, cdr: ChangeDetectorRef, sourceMethod: Method<[] | any | TableEvent | any> | null, sourceDataSource: AbstractTableDataSource | null, adapterFactory: any, matSort: MatSort | null, tableFilter: TableFilterService | null, _tableFilter: FilterLike | null)
Parameters :
Name Type Optional
matTable CdkTable<Data> No
cdr ChangeDetectorRef No
sourceMethod Method<[] | any | TableEvent | any> | null No
sourceDataSource AbstractTableDataSource<Data> | null No
adapterFactory any No
matSort MatSort | null No
tableFilter TableFilterService | null No
_tableFilter FilterLike | null No

Inputs

dataSource
Type : AbstractTableDataSource<Data>
id
Type : string
Required :  true
paginator
Type : MatPaginator
parameters
Type : Observable<Record<string, any>>
rxapTableDataSource
Type : AbstractTableDataSource | string

Methods

Public refresh
refresh()
Returns : void
Public reset
reset()
Returns : void
Public retry
retry()
Returns : void

Properties

Protected _subscription
Default value : new Subscription()
Public Readonly error$
Default value : new Subject<unknown>()
Public Readonly hasError$
Default value : new ToggleSubject()
Public Readonly loading$
Default value : new ToggleSubject(true)
Public Optional method
Type : Method<Data[] | TableEvent>

Accessors

setDataSource
setsetDataSource(dataSource: AbstractTableDataSource | string)
Parameters :
Name Type Optional
dataSource AbstractTableDataSource<Data> | string No
Returns : void
lastRefreshed
getlastRefreshed()
remoteMethod
getremoteMethod()
Returns : Method | undefined
sourceRemoteMethod
getsourceRemoteMethod()
Returns : Method | null
import { CdkTable } from '@angular/cdk/table';
import {
  ChangeDetectorRef,
  Directive,
  Inject,
  InjectionToken,
  Input,
  isDevMode,
  OnDestroy,
  OnInit,
  Optional,
} from '@angular/core';
import type { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { pipeDataSource } from '@rxap/data-source';
import { PaginatorLike } from '@rxap/data-source/pagination';
import {
  AbstractTableDataSource,
  DynamicTableDataSource,
  FilterLike,
  RXAP_TABLE_METHOD,
  SortLike,
  TableEvent,
} from '@rxap/data-source/table';
import { Method } from '@rxap/pattern';
import { ToggleSubject } from '@rxap/rxjs';
import {
  Observable,
  Subject,
  Subscription,
} from 'rxjs';
import { tap } from 'rxjs/operators';
import { TableFilterService } from './table-filter/table-filter.service';

export { RXAP_TABLE_METHOD } from '@rxap/data-source/table';

/**
 * @deprecated use TABLE_METHOD instead
 */
export const TABLE_REMOTE_METHOD = RXAP_TABLE_METHOD;
export const TABLE_REMOTE_METHOD_ADAPTER_FACTORY = new InjectionToken(
  'table-remote-method-adapter-factory',
);
export const RXAP_TABLE_FILTER = new InjectionToken('rxap/material-table-system/table-filter');
export const TABLE_DATA_SOURCE = new InjectionToken('table-data-source');

export type TableRemoteMethodAdapterFactory<
  Data extends Record<string, any> = Record<string, any>
> = (
  method: Method,
  paginator?: PaginatorLike,
  sort?: SortLike | null,
  filter?: FilterLike | null,
  parameters?: Observable<Record<string, any>>,
) => Method<Data[], TableEvent>;

@Directive({
  selector: 'table[mat-table][rxapTableDataSource],mat-table[rxapTableDataSource]',
  exportAs: 'rxapTableDataSource',
  standalone: true,
})
export class TableDataSourceDirective<Data extends Record<string, any> = any>
  implements OnInit, OnDestroy {
  /**
   * @deprecated use dataSource instead
   */
  // eslint-disable-next-line @angular-eslint/no-input-rename
  @Input('rxapTableDataSource')
  public set setDataSource(dataSource: AbstractTableDataSource<Data> | '') {
    if (typeof dataSource !== 'string') {
      this.dataSource = dataSource;
    }
  }

  @Input()
  public paginator?: MatPaginator;

  public readonly loading$ = new ToggleSubject(true);

  @Input({ required: true })
  public id!: string;
  @Input()
  public parameters?: Observable<Record<string, any>>;
  @Input()
  public dataSource?: AbstractTableDataSource<Data>;

  public method?: Method<Data[], TableEvent>;

  public readonly hasError$ = new ToggleSubject();

  public readonly error$ = new Subject<unknown>();

  public get lastRefreshed(): Date | null {
    return this.dataSource?.lastRefreshed ?? null;
  }

  /**
   * @deprecated use method instead
   */
  public get remoteMethod(): Method<Data[], TableEvent> | undefined {
    return this.method;
  }

  /**
   * @deprecated use sourceMethod instead
   */
  public get sourceRemoteMethod(): Method<
    Data[] | any,
    TableEvent | any
  > | null {
    return this.sourceMethod;
  }

  protected _subscription = new Subscription();

  private readonly adapterFactory: TableRemoteMethodAdapterFactory<Data> | null =
    null;

  constructor(
    @Inject(CdkTable)
    private readonly matTable: CdkTable<Data>,
    protected readonly cdr: ChangeDetectorRef,
    @Optional()
    @Inject(RXAP_TABLE_METHOD)
    private readonly sourceMethod: Method<Data[] | any,
      TableEvent | any> | null,
    @Optional()
    @Inject(TABLE_DATA_SOURCE)
    private readonly sourceDataSource: AbstractTableDataSource<Data> | null,
    @Optional()
    @Inject(TABLE_REMOTE_METHOD_ADAPTER_FACTORY)
      adapterFactory: any,
    @Optional()
    @Inject(MatSort)
    private readonly matSort: MatSort | null,
    @Optional()
    @Inject(TableFilterService)
    private readonly tableFilter: TableFilterService | null,
    @Optional()
    @Inject(RXAP_TABLE_FILTER)
    private readonly _tableFilter: FilterLike | null,
  ) {
    this.matTable.trackBy = this.trackBy;
    this.adapterFactory = adapterFactory;
    this.retry = this.retry.bind(this);
    this.refresh = this.refresh.bind(this);
    this.reset = this.reset.bind(this);
  }

  private trackBy(index: number, item: Data): string | number {
    return item['uuid'] ?? index;
  }

  public ngOnInit() {
    const tableFilter = this._tableFilter ?? this.tableFilter ?? undefined;
    if (!this.dataSource) {
      if (this.sourceDataSource) {
        this.dataSource = this.sourceDataSource;
      } else if (this.sourceMethod) {
        if (this.adapterFactory) {
          this.method = this.adapterFactory(
            this.sourceMethod,
            this.paginator,
            this.matSort,
            tableFilter,
            this.parameters,
          );
        } else {
          this.method = this.sourceMethod;
        }
        this.dataSource = new DynamicTableDataSource<Data>(
          this.method,
          this.paginator,
          this.matSort,
          tableFilter,
          this.parameters,
          this.method.metadata ?? { id: this.id },
        );
      }
      if (!this.dataSource) {
        throw new Error(
          'The TABLE_DATA_SOURCE and RXAP_TABLE_METHOD token are not defined!',
        );
      }
    }
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.matSort ?? undefined;
    this.dataSource.filter = tableFilter;
    this.dataSource.parameters = this.parameters;
    if (this.dataSource instanceof DynamicTableDataSource) {
      this.dataSource.setPaginator(this.paginator, this.id);
      this.dataSource.setSort(this.matSort, this.id);
      this.dataSource.setFilter(tableFilter, this.id);
      this.dataSource.setParameters(this.parameters, this.id);
    }
    this._subscription.add(
      this.dataSource.loading$.pipe(
        tap(loading => this.loading$.next(!!loading)),
      ).subscribe(),
    );
    this._subscription.add(
      this.dataSource.hasError$.pipe(
        tap(hasError => this.hasError$.next(!!hasError)),
      ).subscribe(),
    );
    this._subscription.add(
      this.dataSource.error$.pipe(
        tap(error => this.error$.next(error)),
      ).subscribe(),
    );
    // create the id property for the mat table component.
    // the instance of the mat table component is used as viewer object
    // with the set of the id property it is possible to use the same data source
    // instance for multiple table component simultaneously
    // on connect the data source can then use the correct paginator/matSort/tableFilter/parameters instance
    // to create the TableEvent objects
    Reflect.set(this.matTable, 'id', this.id);
    this.matTable.dataSource = pipeDataSource(this.dataSource, tap(rowList => {
      if (rowList.some((element: any) => !element.__metadata__)) {
        if (isDevMode()) {
          console.debug('Ensure to use the NormalizeTableRow function to normalize the table row!');
        }
        rowList.forEach((element: any) => {
          element.__metadata__ ??= {};
          element.__metadata__.loading$ ??= new ToggleSubject();
        });
      }
    }));
  }

  public ngOnDestroy() {
    this._subscription?.unsubscribe();
  }

  public refresh() {
    this.dataSource?.refresh();
  }

  public retry() {
    this.dataSource?.retry();
  }

  public reset() {
    if (this.tableFilter) {
      this.tableFilter.reset();
    } else {
      this.dataSource?.reset();
    }
  }

}


results matching ""

    No results matching ""