Skip to content
Snippets Groups Projects
shared-data-table.presentational.ts 10.5 KiB
Newer Older
import {
  ChangeDetectionStrategy,
  Component,
  Input,
  Output,
  ViewChild,
  ViewEncapsulation,
} from "@angular/core";
import {SharedAbstractPresentational} from "@app/shared/components/presentationals/shared-abstract/shared-abstract.presentational";
import {SharedPaginatorPresentational} from "@app/shared/components/presentationals/shared-paginator/shared-paginator.presentational";
import {FieldTypeEnum} from "@app/shared/enums/field-type.enum";
import {DataTableColumns} from "@app/shared/models/data-table-columns.model";
import {environment} from "@environments/environment";
import {DataTableComponentEnum} from "@shared/enums/data-table-component.enum";
import {DataTableActions} from "@shared/models/data-table-actions.model";
import {LazyLoadEvent} from "primeng/api";
import {
  BehaviorSubject,
  Observable,
} from "rxjs";
Florent Poittevin's avatar
Florent Poittevin committed
import {
  isEmptyString,
  isNullOrUndefined,
Florent Poittevin's avatar
Florent Poittevin committed
  ObjectUtil,
  ObservableUtil,
  OrderEnum,
  Paging,
  QueryParameters,
  QueryParametersUtil,
  Sort,
Florent Poittevin's avatar
Florent Poittevin committed
} from "solidify-frontend";
  selector: "dlcm-shared-data-table",
  templateUrl: "./shared-data-table.presentational.html",
  styleUrls: ["./shared-data-table.presentational.scss"],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SharedDataTablePresentational<TResource extends BaseResource> extends SharedAbstractPresentational {
  readonly TIME_BEFORE_DISPLAY_TOOLTIP: number = environment.timeBeforeDisplayTooltipOnDataTable;

  private _datas: TResource[];
  defaultSortColumn: DataTableColumns;
  set datas(value: TResource[]) {
    this._datas = value;
    this.computeIsDatasPresent();
  }

  get datas(): TResource[] {
    return this._datas;
  }

  @Input()
  skipInitialQuery: boolean = false;

  private _queryParameters: QueryParameters;
  @Input()
  columnsSkippedToClear: string[];

  @Input()
  set queryParameters(value: QueryParameters) {
    this._queryParameters = QueryParametersUtil.clone(value);
    if (isNullOrUndefined(value.paging)) {
      this._queryParameters.paging = new Paging(environment.defaultPageSize);
    }
  }

  get queryParameters(): QueryParameters {
    return this._queryParameters;
  }

  private _columns: DataTableColumns[];
  set columns(value: DataTableColumns[]) {
    this._columns = value;
    this.defaultSortColumn = this._columns.find(c => isTrue(c.isSortable) && c.order !== 0);
  }

  get columns(): DataTableColumns[] {
    return this._columns;
  }
  private _actions: DataTableActions[] = [];

  @Input()
  set actions(value: DataTableActions[]) {
    this._actions = value;
  }

  get actions(): DataTableActions[] {
    return this._actions;
  }

  private _listNewId: string[] = [];

  @Input()
  set listNewId(value: string[]) {
    this._listNewId = value;
  }

  get listNewId(): string[] {
    return this._listNewId;
  }

  @Input()
  scrollHeight: string | undefined = undefined;

  private readonly _selectBS: BehaviorSubject<TResource | undefined> = new BehaviorSubject<TResource | undefined>(undefined);
  private readonly _queryParametersBS: BehaviorSubject<QueryParameters | undefined> = new BehaviorSubject<QueryParameters | undefined>(undefined);
  readonly selectObs: Observable<TResource | undefined> = ObservableUtil.asObservable(this._selectBS);

  @Output("queryParametersChange")
  readonly queryParametersObs: Observable<QueryParameters | undefined> = ObservableUtil.asObservable(this._queryParametersBS);
Florent Poittevin's avatar
Florent Poittevin committed
  @ViewChild("paginator", {static: false})
  paginator: SharedPaginatorPresentational;
  isDatasPresent: boolean;

  get fieldTypeEnum(): typeof FieldTypeEnum {
    return FieldTypeEnum;
  }
  get dataTableComponentEnum(): typeof DataTableComponentEnum {
    return DataTableComponentEnum;
  }

  private readonly _SEPARATOR: string = ".";
  constructor() {
Florent Poittevin's avatar
Florent Poittevin committed
  private computeIsDatasPresent(): void {
    let isDatasPresent = false;
    if (!isNullOrUndefined(this.datas) && this.datas.length > 0) {
      isDatasPresent = true;
    }
    this.isDatasPresent = isDatasPresent;
  showDetail(row: TResource): void {
  getCellData(row: TResource, column: DataTableColumns): any {
    const data = this.getData(row, column);
    if (column.translate && column.filterEnum) {
      const enumValue = column.filterEnum.find(f => f.key === data + "");
      if (enumValue) {
        return enumValue.value;
      }
      return data;
    }
    return this.applyTypeToData(data, column.type);
  getData(row: TResource, column: DataTableColumns): any {
    const field = column.field;
    if (field.includes(this._SEPARATOR)) {
      return ObjectUtil.getNestedValue(row as any, column.field);
Florent Poittevin's avatar
Florent Poittevin committed
  private applyTypeToData(data: any, type: FieldTypeEnum): any {
    if (isNullOrUndefined(data)) {
      return StringUtil.stringEmpty;
    }
    if (type === FieldTypeEnum.date) {
      return DateUtil.convertDateToDateString(data);
    }
    if (type === FieldTypeEnum.datetime) {
      return DateUtil.convertDateToDateTimeString(data);
  backendSort($event: LazyLoadEvent): void {
    if (isTrue(this.skipInitialQuery)) {
      this.skipInitialQuery = false;
      return;
    }
    const queryParameters = ObjectUtil.clone(this.queryParameters);
    if (!isNullOrUndefined($event.sortField)) {
      const sort = this.adaptLazyLoadEventToSortModel($event);
      queryParameters.sort = sort;
    }
    this._queryParametersBS.next(queryParameters);
  private adaptLazyLoadEventToSortModel($event: LazyLoadEvent): Sort {
      field: this.getSortableFieldByFieldName($event.sortField),
      order: $event.sortOrder === -1 ? OrderEnum.descending : OrderEnum.ascending,
  pageChange($event: Paging): void {
    const queryParameters = ObjectUtil.clone(this.queryParameters);
    queryParameters.paging = $event;
    this._queryParametersBS.next(queryParameters);
  onValueChange(col: DataTableColumns, value: string): void {
    const fieldName = this.getFilterableField(col);
    let queryParameters = ObjectUtil.clone(this.queryParameters);
    queryParameters.search = ObjectUtil.clone(queryParameters.search);
    queryParameters.search.searchItems = ObjectUtil.clone(queryParameters.search.searchItems);
    if (isEmptyString(value) || isNullOrUndefined(value)) {
      MappingObjectUtil.delete(queryParameters.search.searchItems, fieldName);
Florent Poittevin's avatar
Florent Poittevin committed
    } else {
      MappingObjectUtil.set(queryParameters.search.searchItems, fieldName, value);
    queryParameters = QueryParametersUtil.resetToFirstPage(queryParameters);
    this._queryParametersBS.next(queryParameters);
  getValue(col: DataTableColumns): string | null {
    const value = MappingObjectUtil.get(this.queryParameters.search.searchItems, this.getFilterableField(col));
    if (value === null || value === undefined || value === StringUtil.stringEmpty) {
Florent Poittevin's avatar
Florent Poittevin committed
      return null;
    }
    return value;
  }

  private getFilterableField(col: DataTableColumns): string {
    return isNullOrUndefined(col.filterableField) ? col.field : col.filterableField;
  }

  private getSortableField(col: DataTableColumns): string {
    return isNullOrUndefined(col.sortableField) ? col.field : col.sortableField;
  }

  getSortableFieldByFieldName(fieldName: string): string {
    const column = this.columns.find(col => col.field === fieldName);
    return this.getSortableField(column);
  }

  isFilterApplied(): boolean {
    let toSkip: boolean = false;
    if (MappingObjectUtil.size(QueryParametersUtil.getSearchItems(this.queryParameters)) > 0) {
      if (this.columnsSkippedToClear === undefined || isEmptyArray(this.columnsSkippedToClear)) {
        return true;
      }
      const searchItems: Map<string, string> = MappingObjectUtil.toMap(this.queryParameters.search.searchItems);
      searchItems.forEach((value: string, key: string) => {
        if (this.columnsSkippedToClear.some(c => c.includes(key)) === false) {
  }

  clearFilters(): void {
    let queryParameters = QueryParametersUtil.clone(this.queryParameters);
    const searchItems = QueryParametersUtil.getSearchItems(queryParameters);
    if (this.columnsSkippedToClear === undefined || isEmptyArray(this.columnsSkippedToClear)) {
      MappingObjectUtil.clear(searchItems);
      MappingObjectUtil.forEach(searchItems, (value: string, key: string) => {
        if (this.columnsSkippedToClear.some(c => c.includes(key)) === false) {
          MappingObjectUtil.delete(searchItems, key);
        }
      });
    }
    queryParameters = QueryParametersUtil.resetToFirstPage(queryParameters);
    this._queryParametersBS.next(queryParameters);
  }
  callCallback($event: MouseEvent, callback: (current: TResource) => void, rowData: TResource): void {
    $event.stopPropagation();
    callback(rowData);
  }
  isNew(rowData: TResource): boolean {
    if (isNullOrUndefined(this.listNewId) || isEmptyArray(this.listNewId)) {
      return false;
    }
    return this.listNewId.includes(rowData.resId);
  }

  displayActionOnCondition(action: DataTableActions<TResource>, resource: TResource = undefined): boolean | Observable<boolean> {
    if (isNullOrUndefined(resource) || isNullOrUndefined(action.displayOnCondition)) {
      return this.atLeastOneActionRowDisplayed(action);
    }
    return action.displayOnCondition(resource);
  }

  private atLeastOneActionRowDisplayed(action: DataTableActions<TResource>): boolean | Observable<boolean> {
    if (isNullOrUndefined(this._datas) || isEmptyArray(this._datas)) {
      return false;
    }
    if (isNullOrUndefined(action.displayOnCondition)) {
      return true;
    }
    if (this._datas.some(data => !isObservable(action.displayOnCondition(data)) && isTrue(action.displayOnCondition(data)))) {
      return true;
    }
    // TODO Find a way to merge all observable
    return action.displayOnCondition(this._datas[0]);

  getWidth(column: DataTableColumns<TResource>): string {
    if (!isNullOrUndefined(column.width)) {
      return column.width;
    }
    if (column.component === DataTableComponentEnum.conformityLevelStar) {
      return "100px";
    }
    if (column.type === FieldTypeEnum.datetime) {
      return "145px";
    }
    if (column.type === FieldTypeEnum.date) {
      return "90px";
    }

    return undefined;
  }

  isColumnFilterable(): boolean {
    return !isNullOrUndefined(this.columns.find(c => isTrue(c.isFilterable)));
  }