File

src/app/shared/widgets/data-list/data-list.service.ts

Index

Widget inputs
Widget outputs
Properties
Methods

Constructor

constructor(_authHttp: HttpClient, _progressbarService: ProgressbarService, notificationService: CustomNotificationService, translateService: TranslateService, _columnService: DataListColumnsService, websocketService: WebsocketService, localstorageShownAttributesEntry: LocalStorageEntry, userService: UserService)
Parameters :
Name Type Optional
_authHttp HttpClient no
_progressbarService ProgressbarService no
notificationService CustomNotificationService no
translateService TranslateService no
_columnService DataListColumnsService no
websocketService WebsocketService no
localstorageShownAttributesEntry LocalStorageEntry no
userService UserService no

Methods

Private _buildDataUrl
_buildDataUrl(queryString: string, locale: string, dataType: string, viewmode: string, virtualizationArgs: any, sortingArgs: ISortingExpression[], filteringArgs: IFilteringExpressionsTree, attributes: string[], pageIndex: number, force: boolean)
Parameters :
Name Type Optional Default value
queryString string no
locale string no
dataType string no
viewmode string no
virtualizationArgs any no
sortingArgs ISortingExpression[] no
filteringArgs IFilteringExpressionsTree no
attributes string[] no
pageIndex number no
force boolean no false
Returns : string
Public appendPaging
appendPaging(queryString: , virtualizationArgs: any, pageIndex: )
Parameters :
Name Type Optional
queryString no
virtualizationArgs any no
pageIndex no
Returns : any
Public buildDataUrlWithOutPaging
buildDataUrlWithOutPaging(queryString: string, locale: string, dataType: string, viewmode: string, sortingArgs: ISortingExpression[], filteringArgs: IFilteringExpressionsTree, attributes: string[], force: boolean)
Parameters :
Name Type Optional Default value
queryString string no
locale string no
dataType string no
viewmode string no
sortingArgs ISortingExpression[] no
filteringArgs IFilteringExpressionsTree no
attributes string[] no
force boolean no false
Returns : string
Public doMapData
doMapData(data: , isTree?: boolean)
Parameters :
Name Type Optional
data no
isTree boolean yes
Returns : literal type
getStartIndex
getStartIndex(chunkSize: )
Parameters :
Name Optional
chunkSize no
Returns : any
getTemplatedFilteredResults
getTemplatedFilteredResults(inputurl: , showProgressBar: boolean)
Parameters :
Name Type Optional Default value
inputurl no
showProgressBar boolean no true
Public initialize
initialize(grid: IgxGridBaseDirective, eagerLoading: boolean, configuration: DataListConfiguration, changeCallback: any, cdr: ChangeDetectorRef, dataType?: any)
Parameters :
Name Type Optional
grid IgxGridBaseDirective no
eagerLoading boolean no
configuration DataListConfiguration no
changeCallback any no
cdr ChangeDetectorRef no
dataType any yes
Returns : void
Private Static isExpressionsTree
isExpressionsTree(operand: IFilteringExpressionsTree | IFilteringExpression)
Parameters :
Name Type Optional
operand IFilteringExpressionsTree | IFilteringExpression no
Returns : IFilteringExpressionsTree
Public loadData
loadData(inputurl: string, locale: string, dataType: string, viewMode: string, attributes: string[], virtualizationArgs?: IForOfState, sortingArgs?: ISortingExpression[], filteringArgs?: IFilteringExpressionsTree, resetData?: boolean, cb?: (undefined) => void, force?: boolean)
Parameters :
Name Type Optional
inputurl string no
locale string no
dataType string no
viewMode string no
attributes string[] no
virtualizationArgs IForOfState yes
sortingArgs ISortingExpression[] yes
filteringArgs IFilteringExpressionsTree yes
resetData boolean yes
cb function yes
force boolean yes
Returns : any
Public mapData
mapData(data: )
Parameters :
Name Optional
data no
Returns : literal type
Private mapFilterExpression
mapFilterExpression(expression: IFilteringExpression)
Parameters :
Name Type Optional
expression IFilteringExpression no
Returns : string
Private mapFilterTree
mapFilterTree(tree: IFilteringExpressionsTree)
Parameters :
Name Type Optional
tree IFilteringExpressionsTree no
Returns : string
Static mapFilterTypeValue
mapFilterTypeValue(condition: , fieldName: , value: )
Parameters :
Name Optional
condition no
fieldName no
value no
Returns : any
Private mapMainAsset
mapMainAsset(data: any)
Parameters :
Name Type Optional
data any no
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
Private prepareAndLoadData
prepareAndLoadData(reset: , force: )
Parameters :
Name Optional Default value
reset no true
force no false
Returns : void
Public processData
processData(reset: )
Parameters :
Name Optional
reset no
Returns : void
Public processTileViewData
processTileViewData(virtualization: )
Parameters :
Name Optional
virtualization no
Returns : void
Public reloadGrid
reloadGrid(force: boolean)
Parameters :
Name Type Optional Default value
force boolean no false
Returns : void
Private reselectRows
reselectRows(data: any[], isTree: boolean)
Parameters :
Name Type Optional
data any[] no
isTree boolean no
Returns : void
Public reset
reset()
Returns : void
Public sendRequest
sendRequest(url: string, startIndex: number, pageIndex: number, endIndex: number, cb: )
Parameters :
Name Type Optional
url string no
startIndex number no
pageIndex number no
endIndex number no
cb no
Returns : void
setDataType
setDataType(dataType: string, reload: boolean)
Parameters :
Name Type Optional
dataType string no
reload boolean no
Returns : void
setEagerLimit
setEagerLimit(eagerLimit: number)
Parameters :
Name Type Optional
eagerLimit number no
Returns : void
setNextPage
setNextPage(getPage: boolean)
Parameters :
Name Type Optional
getPage boolean no
Returns : void
setPageLimit
setPageLimit(pageLimit: number)
Parameters :
Name Type Optional
pageLimit number no
Returns : void
setViewmode
setViewmode(viewmode: string, reload: )
Parameters :
Name Type Optional Default value
viewmode string no
reload no true
Returns : void
Public updateData
updateData(data: any, startIndex: number, force: , endIndex?: number, pageIndex?: number)
Parameters :
Name Type Optional Default value
data any no
startIndex number no
force no false
endIndex number yes
pageIndex number yes
Returns : void
updateHierarchicalDataTotalCount
updateHierarchicalDataTotalCount()
Returns : void
Public updateLink
updateLink(link: string, loadData: boolean, force: boolean)
Parameters :
Name Type Optional Default value
link string no
loadData boolean no true
force boolean no false
Returns : void

Properties

Private _cachedData
_cachedData: any[]
Type : any[]
Private _cdr
_cdr: ChangeDetectorRef
Type : ChangeDetectorRef
Protected _data
_data: BehaviorSubject<any[]>
Type : BehaviorSubject<any[]>
Private _dataObservable
_dataObservable: Observable<any[]>
Type : Observable<any[]>
Private _eagerLimit
_eagerLimit: number
Type : number
Private _getNextPage
_getNextPage: boolean
Type : boolean
Private _grid
_grid: IgxGridBaseDirective
Type : IgxGridBaseDirective
Private _link
_link: string
Type : string
Private _linkWithoutPaging
_linkWithoutPaging:
Default value : new Subject<String>()
Private _pageLimit
_pageLimit: number
Type : number
Private _prevRequest
_prevRequest: any
Type : any
Private _products
_products: string[]
Type : string[]
Private _selectedLocale
_selectedLocale: string
Type : string
Private _unsubscribe
_unsubscribe:
Default value : NgUnsubscribe.create()
Private _viewmode
_viewmode: string
Type : string
Public cacheId
cacheId: string
Type : string
Private changeCallback
changeCallback: any
Type : any
Public configuration
configuration: DataListConfiguration
Type : DataListConfiguration
Public data
data: Observable<any[]>
Type : Observable<any[]>
Public dataLoaded
dataLoaded:
Default value : new Subject<any[]>()
Public dataTotalSubject
dataTotalSubject:
Default value : new BehaviorSubject<number>(null)
Public dataType
dataType: string
Type : string
Private debounceSendRequest
debounceSendRequest: Subject<any>
Type : Subject<any>
Default value : new Subject<any>()
Private inputLinkEvent
inputLinkEvent:
Default value : new Subject<String>()
Private inputLinkEventObservable
inputLinkEventObservable:
Default value : this.inputLinkEvent.asObservable()
Public isEagerLoading
isEagerLoading: boolean
Type : boolean
Default value : false
Public linkWithoutPaging
linkWithoutPaging:
Default value : this._linkWithoutPaging.asObservable()
Public recommendedAttributes
recommendedAttributes:
Default value : new BehaviorSubject<RecommendedAttributes>( null )
Private refreshSub
refreshSub:
Public results
results: Result[]
Type : Result[]
Public totalSubject
totalSubject:
Default value : new BehaviorSubject<number>(null)
Private triggerSearchObs
triggerSearchObs:
Default value : this.triggerSearchSubject.pipe( filter(() => { if (!this.configuration.languageSelection) { return true; } return this.selectedLocale != null; }), //Debounce by 100ms to take the latest uri if multiple get inputted without every one starting a request debounceTime(100), takeUntil(this._unsubscribe) )
Private triggerSearchSubject
triggerSearchSubject:
Default value : new Subject<boolean>()

Accessors

products
getproducts()
setproducts(value: )
Parameters :
Name Optional
value no
Returns : void
dataArray
getdataArray()
viewmode
getviewmode()
selectedLocale
getselectedLocale()
setselectedLocale(locale: string)
Parameters :
Name Type Optional
locale string no
Returns : void
displayedAttributes
getdisplayedAttributes()
cachedData
getcachedData()
grid
getgrid()
import {
  debounceTime,
  distinctUntilChanged,
  filter,
  map,
  takeUntil,
  tap,
} from "rxjs/operators";
import { ChangeDetectorRef, Injectable, isDevMode } from "@angular/core";
import { BehaviorSubject, Observable, Subject } from "rxjs";
import { HttpClient } from "@angular/common/http";
import {
  DataListConfiguration,
  RecommendedAttributes,
  Result,
  ResultResource,
} from "../interfaces/list.interfaces";
import { ProgressbarService } from "../../components/progressbar/progressbar.service";
import {
  FilteringLogic,
  IFilteringExpression,
  IFilteringExpressionsTree,
  IForOfState,
  IgxGridBaseDirective,
  IgxGridComponent,
  ISortingExpression,
  SortingDirection,
  IgxHierarchicalGridComponent,
  IgxForOfDirective,
} from "@infragistics/igniteui-angular";
import { ListComponentWidget } from "../list/list.component";

import * as uriTemplates_ from "uri-templates";
import { NgUnsubscribe } from "../../ng-unsubscribe";
import { DataListColumnsService } from "./data-list-columns.service";
import { CustomNotificationService } from "../../components/notification/customnotification.service";
import { TranslateService } from "@ngx-translate/core";
import { deepCopy } from "../../components/util";
import { WebsocketService } from "../../components/services/websocket.service";
import { UserService } from "../../components/user/user.service";
import { LocalStorageEntry } from "../../components/local-storage/local-storage.service";
import { ViewAttributeComponent } from "../../components/edit-attribute/view-attribute.component";
import { Attribute, Attributes } from "../../components/edit-attribute";

const uriTemplates = uriTemplates_;
declare let $;

export enum SortOrder {
  ASC = "asc",
  DESC = "desc",
  NONE = "",
}

const EMPTY_STRING: string = "";

const TOOLBOX_EDIT_BAR_ACTIVE = { "@toolbox-edit-bar": "active" };
const TOOLBOX_ACTIVE_CONTEXT = {
  "@toolbox-active-context": ["sequential", "bulk"],
};

export enum FILTER_OPERATION {
  CONTAINS = "contains",
  STARTS_WITH = "startswith",
  ENDS_WITH = "endswith",
  EQUALS = "eq",
  DOES_NOT_EQUAL = "ne",
  DOES_NOT_CONTAIN = "not contains",
  GREATER_THAN = "gt",
  LESS_THAN = "lt",
  LESS_THAN_EQUAL = "le",
  GREATER_THAN_EQUAL = "ge",
}

@Injectable()
export class DataListService {
  private _unsubscribe = NgUnsubscribe.create();

  public results: Result[];
  private inputLinkEvent = new Subject<String>();
  private inputLinkEventObservable = this.inputLinkEvent.asObservable();

  protected _data: BehaviorSubject<any[]>;
  private _cachedData: any[];
  public totalSubject = new BehaviorSubject<number>(null);
  public dataTotalSubject = new BehaviorSubject<number>(null);
  public recommendedAttributes = new BehaviorSubject<RecommendedAttributes>(
    null
  );

  private refreshSub;

  // The list of productNos that is represented by the dataset in _data
  private _products: string[];

  private _linkWithoutPaging = new Subject<String>();
  public linkWithoutPaging = this._linkWithoutPaging.asObservable();

  private triggerSearchSubject = new Subject<boolean>();
  private triggerSearchObs = this.triggerSearchSubject.pipe(
    filter(() => {
      if (!this.configuration.languageSelection) {
        return true;
      }
      return this.selectedLocale != null;
    }),
    //Debounce by 100ms to take the latest uri if multiple get inputted without every one starting a request
    debounceTime(100),
    takeUntil(this._unsubscribe)
  );

  private _cdr: ChangeDetectorRef;
  private _grid: IgxGridBaseDirective;
  private changeCallback: any;

  private _link: string;

  private _selectedLocale: string;

  public dataType: string;
  private _viewmode: string;

  private _prevRequest: any;

  private _dataObservable: Observable<any[]>;
  public data: Observable<any[]>;

  public configuration: DataListConfiguration;
  public isEagerLoading: boolean = false;
  public cacheId: string;
  private _pageLimit: number;
  private _eagerLimit: number;
  private _getNextPage: boolean;
  private debounceSendRequest: Subject<any> = new Subject<any>();

  public dataLoaded = new Subject<any[]>();

  constructor(
    protected _authHttp: HttpClient,
    private _progressbarService: ProgressbarService,
    private notificationService: CustomNotificationService,
    private translateService: TranslateService,
    private _columnService: DataListColumnsService,
    private websocketService: WebsocketService,
    private localstorageShownAttributesEntry: LocalStorageEntry,
    private userService: UserService
  ) {
    this._data = new BehaviorSubject<any[]>([]);
    this._dataObservable = this._data
      .asObservable()
      .pipe(takeUntil(this._unsubscribe));

    this.data = this._dataObservable;
  }

  public initialize(
    grid: IgxGridBaseDirective,
    eagerLoading: boolean,
    configuration: DataListConfiguration,
    changeCallback: any,
    cdr: ChangeDetectorRef,
    dataType?: any
  ) {
    this._cdr = cdr;
    this._grid = grid;
    this.changeCallback = changeCallback;
    this.configuration = configuration;
    this.isEagerLoading = eagerLoading;
    this.setDataType(dataType || this.configuration.dataType, false);
    if (!configuration.dynamicColumnsFromData) {
      this._columnService.columns
        .pipe(takeUntil(this._unsubscribe))
        .subscribe((cols) => {
          if (this._link) {
            this.reloadGrid();
          }
        });
    }

    this.debounceSendRequest
      .pipe(takeUntil(this._unsubscribe), debounceTime(500))
      .subscribe(([url, startIndex, pageIndex, endIndex, cb]) => {
        if (url) {
          this.sendRequest(url, startIndex, pageIndex, endIndex, cb);
        }
      });

    this.triggerSearchObs.subscribe((force) => {
      if (!this._link) {
        return;
      }
      this.prepareAndLoadData(true, force);
    });
  }

  set products(value) {
    this._products = value;
  }

  get products() {
    return this._products;
  }

  get dataArray(): any[] {
    return this._data.value;
  }

  get viewmode(): string {
    return this._viewmode;
  }

  setPageLimit(pageLimit: number) {
    this._pageLimit = pageLimit;
  }

  setNextPage(getPage: boolean) {
    this._getNextPage = getPage;
  }

  setEagerLimit(eagerLimit: number) {
    this._eagerLimit = eagerLimit;
  }

  setDataType(dataType: string, reload: boolean) {
    this.dataType = dataType;
    if (reload) {
      this.reloadGrid();
    }

    this._columnService.dataType = dataType;
  }

  setViewmode(viewmode: string, reload = true) {
    this._viewmode = viewmode;
    if (reload) {
      this.reloadGrid();
    }
  }

  get selectedLocale(): string {
    return this._selectedLocale;
  }

  set selectedLocale(locale: string) {
    this._selectedLocale = locale;
    if (this._link) {
      this.reloadGrid();
    }
  }

  get displayedAttributes(): string[] {
    return this._columnService.displayedAttributes;
  }

  get cachedData(): any[] {
    return this._cachedData;
  }

  public updateLink(
    link: string,
    loadData: boolean = true,
    force: boolean = false
  ) {
    if (!link) {
      this.reset();
      return;
    }

    this._link = link;

    if (loadData) {
      this.triggerSearchSubject.next(force);
      // this.prepareAndLoadData();
    }
  }

  public reloadGrid(force: boolean = false) {
    this.updateLink(this._link, true, force);
  }

  public reset() {
    if (this._prevRequest) {
      this._prevRequest.unsubscribe();
      this._progressbarService.requestFinished();
    }
    if (this._data.value.length !== 0) {
      this._grid.navigateTo(0, 0);
      this._products = [];
      this._link = null;
      this._data.next([]);
      if (this.changeCallback) {
        this.changeCallback([]);
      }
    }

    this._columnService.updateColumns([], true);
    this._cachedData = null;
    this._link = null;
    this.totalSubject.next(null);
    this.dataTotalSubject.next(null);
  }

  private prepareAndLoadData(reset = true, force = false) {
    let chunkSize = this.isEagerLoading
      ? 0
      : Math.ceil(this.grid.nativeElement.offsetHeight / this._grid.rowHeight);
    let virtualization = {
      chunkSize: chunkSize,
      startIndex: this.getStartIndex(chunkSize),
    };

    this.loadData(
      this._link,
      this._selectedLocale,
      this.dataType,
      this._viewmode,
      this.displayedAttributes,
      virtualization,
      this.isEagerLoading ? null : this._grid.sortingExpressions,
      this.isEagerLoading ? null : this._grid.filteringExpressionsTree,
      reset,
      (data) => {
        this.changeCallback(data);
      },
      force
    );
  }

  public processData(reset) {
    if (!this._link || this.isEagerLoading) {
      return;
    }

    this.prepareAndLoadData(reset);
  }

  public processTileViewData(virtualization) {
    this.loadData(
      this._link,
      this._selectedLocale,
      this.dataType,
      this._viewmode,
      this.displayedAttributes,
      virtualization,
      this.isEagerLoading ? null : this._grid.sortingExpressions,
      this.isEagerLoading ? null : this._grid.filteringExpressionsTree,
      false,
      (data) => {
        this.changeCallback(data);
      }
    );
  }

  getTemplatedFilteredResults(
    inputurl,
    showProgressBar: boolean = true
  ): Observable<ResultResource> {
    if (showProgressBar) {
      this._progressbarService.addRequest();
    }

    return this._authHttp.get<any>(inputurl, {}).pipe(
      debounceTime(400),
      distinctUntilChanged(),
      tap(
        (res) => {
          if (showProgressBar) {
            this._progressbarService.requestFinished();
          }
          if (res.messages) {
            res.messages.forEach((entry) => {
              this.notificationService.fromResponse(entry);
            });
          }
          return res;
        },
        (err) => {
          if (showProgressBar) {
            this._progressbarService.requestFinished();
          }
        }
      ),
      map((res) => {
        return <ResultResource>res;
      })
    );
  }

  public loadData(
    inputurl: string,
    locale: string,
    dataType: string,
    viewMode: string,
    attributes: string[],
    virtualizationArgs?: IForOfState,
    sortingArgs?: ISortingExpression[],
    filteringArgs?: IFilteringExpressionsTree,
    resetData?: boolean,
    cb?: (any) => void,
    force?: boolean
  ): any {
    const startIndex = virtualizationArgs.startIndex;
    let endIndex = virtualizationArgs.chunkSize + startIndex;

    let pageIndex: number;
    let areAllItemsInCache = true;

    if (resetData || this.isEagerLoading) {
      let url = this._buildDataUrl(
        inputurl,
        locale,
        dataType,
        viewMode,
        virtualizationArgs,
        sortingArgs,
        filteringArgs,
        attributes,
        startIndex,
        force
      );

      this._progressbarService.addRequest();

      if (this._prevRequest) {
        this._prevRequest.unsubscribe();
        this._progressbarService.requestFinished();
      }

      this._prevRequest = this._authHttp
        .get<any>(url)
        .pipe(
          debounceTime(400),
          tap(
            (res) => {
              this._progressbarService.requestFinished();
              if (res.messages) {
                res.messages.forEach((entry) => {
                  this.notificationService.fromResponse(entry);
                });
              }

              if (res.emptyMessage) {
                this.grid.resourceStrings.igx_grid_emptyGrid_message =
                  res.emptyMessage;
              }

              return res;
            },
            (err) => {
              this._progressbarService.requestFinished();
            }
          ),
          map((res) => {
            return <ResultResource>res;
          })
        )

        .subscribe(
          (data: any) => {
            let mapped = this.mapData(data);
            if (this.grid.selectedRows.length > 0) {
              let selectedRows = deepCopy(this.grid.selectedRows);
              let identifiers = mapped.data.map((row) => row.identifier);
              let missingRows = selectedRows.filter(
                (value) => -1 === identifiers.indexOf(value)
              );
              if (missingRows.length > 0) {
                this.grid.deselectRows(missingRows);
              }
            }

            this._prevRequest = null;
            this.totalSubject.next(
              data.customTotal ? data.customTotal : mapped.total
            );
            if (this.isEagerLoading) {
              this._data.next(mapped.data);
            } else {
              if (
                (this._grid instanceof IgxGridComponent ||
                  this._grid instanceof IgxHierarchicalGridComponent) &&
                this._grid.totalItemCount != data.total
              ) {
                this._grid.totalItemCount = data.total;
              }

              this._cachedData = new Array<any>(data["total"]).fill({
                cache: true,
              });
              this.updateData(mapped.data, startIndex, false, endIndex);
              this.dataTotalSubject.next(mapped.total);
              this._cdr.detectChanges();
              this._grid.cdr.detectChanges();
            }

            if (cb) {
              cb(mapped.data);
            }

            this.dataLoaded.next(mapped.data);

            if (this._columnService.summaries.length !== 0) {
              this._grid.enableSummaries(this._columnService.summaries);
            }
          },
          (err) => console.log("HTTP Error", err)
        );

      return;
    }

    if (this._grid instanceof IgxHierarchicalGridComponent) {
      let limit = this.cachedData.length;

      if (endIndex === limit - 1) {
        endIndex = limit;
      }
    }

    const data = this._cachedData.slice(startIndex, endIndex);
    this._data.next(data);
    if (cb) {
      cb(data);
    }
    this._grid.cdr.markForCheck();

    if (this._prevRequest) {
      this._prevRequest.unsubscribe();
      this._prevRequest = null;
      this._progressbarService.requestFinished();
    }

    for (let i = startIndex; i < endIndex; i++) {
      if (this._cachedData[i] === null || this._cachedData[i].cache) {
        pageIndex = i;
        areAllItemsInCache = false;
        break;
      }
    }
    if (!areAllItemsInCache) {
      let url = this._buildDataUrl(
        inputurl,
        locale,
        dataType,
        viewMode,
        virtualizationArgs,
        sortingArgs,
        filteringArgs,
        attributes,
        pageIndex
      );
      this.debounceSendRequest.next([url, startIndex, pageIndex, endIndex, cb]);
    }
  }

  public sendRequest(
    url: string,
    startIndex: number,
    pageIndex: number,
    endIndex: number,
    cb
  ) {
    this._progressbarService.addRequest();

    this._prevRequest = this._authHttp
      .get<any>(url)
      .pipe(
        debounceTime(200),
        distinctUntilChanged(),
        map((res) => {
          if (res.messages) {
            res.messages.forEach((entry) => {
              this.notificationService.fromResponse(entry);
            });
          }
          return <ResultResource>res;
        })
      )
      .subscribe(
        (data: any) => {
          this._progressbarService.requestFinished();

          let mapped = this.mapData(data);
          if (
            (this._grid instanceof IgxGridComponent ||
              this._grid instanceof IgxHierarchicalGridComponent) &&
            this._grid.totalItemCount != data.total
          ) {
            this._grid.totalItemCount = data.total;
          }

          this.updateData(mapped.data, startIndex, false, endIndex, pageIndex);
          if (cb) {
            cb(mapped.data);
          }
          this._cdr.detectChanges();
          this._grid.cdr.detectChanges();
        },
        (err) => console.log("HTTP Error", err)
      );
  }

  public updateData(
    data: any,
    startIndex: number,
    force = false,
    endIndex?: number,
    pageIndex?: number
  ) {
    if (force) {
      this._cachedData = new Array(data.length).fill({ cache: true });
    }
    let cacheIndex = this._getNextPage && pageIndex ? pageIndex : startIndex;
    if (data) {
      for (let i = 0; i < data.length; i++) {
        this._cachedData[i + cacheIndex] = data[i];
      }
      const cachedData = this._cachedData.slice(startIndex, endIndex);
      this._data.next(cachedData);
    }
  }

  updateHierarchicalDataTotalCount() {
    if (
      !this.isEagerLoading &&
      this._grid instanceof IgxHierarchicalGridComponent
    ) {
      this._grid.totalItemCount =
        this._cachedData.length + this._grid.expansionStates.size;
    }
  }

  public doMapData(data, isTree?: boolean): { total: number; data: any } {
    if (data.messages) {
      data.messages.forEach((entry) => {
        this.notificationService.fromResponse(entry);
      });
    }
    const total = data.total;
    const typedData = data._embedded[data.type];

    if (typedData[0]) {
      // create a prototype object that will be shared between all
      // rows loaded at this point. the prototype keeps track of
      // all required #source and #value properties to reduce the
      // required memory
      const rowPrototype = Attributes.createRowPrototype();

      for (const row of typedData) {
        this.mapMainAsset(row);
        Attributes.mapRowAttributes(row, rowPrototype);
      }

      this.reselectRows(typedData, isTree);
    }

    return { total, data: typedData };
  }

  public mapData(data): { total: number; data: any[] } {
    if (this.refreshSub) {
      this.refreshSub.unsubscribe();
      this.refreshSub = null;
    }
    this.cacheId = data.cacheId || null;
    if (this.cacheId) {
      this.refreshSub = this.websocketService
        .createSub(
          `/topic/cache/${this.userService.currentUser}/${this.cacheId}/refreshed`
        )
        .pipe(takeUntil(this._unsubscribe))
        .subscribe((data) => {
          this.reloadGrid();
        });
    }

    if (data.products) {
      this._products = data.products;
    } else {
      this._products = null;
    }
    if (data.messages) {
      data.messages.forEach((entry) => {
        this.notificationService.fromResponse(entry);
      });
    }

    const recommendedAttributes = data._embedded.recommendedAttributes;
    if (recommendedAttributes) {
      this.recommendedAttributes.next(recommendedAttributes);
    }

    const dynamicColumnsFromData = this._columnService.dynamicColumnsFromData;
    if (dynamicColumnsFromData || data._embedded.forcedAttributes) {
      const columns: any[] = data._embedded.columns || [];
      const forceAttributes: any[] = data._embedded.forcedAttributes || [];

      if (forceAttributes.length > 0) {
        this._columnService.shownAttributes = forceAttributes;
        if (this.localstorageShownAttributesEntry) {
          this.localstorageShownAttributesEntry.value = JSON.stringify(
            forceAttributes
          );
        }
      }

      if (dynamicColumnsFromData && columns.length === 0) {
        const link = data?._links?.self?.href;
        let message = `DataList is in dynamicColumnsFromData mode, but no columns were found in response`;
        if (link) {
          message += `, request url is ${link}`;
        }

        if (isDevMode()) {
          console.error(message);
        } else {
          console.debug(message);
        }
      }

      const update = [...columns];

      forceAttributes.forEach((attribute) => {
        let matchingColumn = update.find(
          (column) => column.identifier === attribute.identifier
        );

        if (matchingColumn == null) {
          // attributes forced to be displayed require a matching column definition
          return;
        }

        // prefer using the column's description, if it is set
        Object.assign(matchingColumn, attribute, {
          description: matchingColumn.description || attribute.description,
        });
      });

      this._columnService.updateColumns(update, true);

      const groups = data._embedded.groups;
      if (dynamicColumnsFromData && groups) {
        this._columnService.updateGroups(groups, true);
      }
    }

    return this.doMapData(data);
  }

  private mapMainAsset(data: any) {
    if (!data.hasOwnProperty("_embedded")) {
      return;
    }

    let embedded = data["_embedded"];
    if (!embedded.hasOwnProperty("main-asset")) {
      return;
    }

    let asset = embedded["main-asset"];
    data["main-asset"] = asset;

    asset.toString = function () {
      return this._links.default.href;
    };
    asset.valueOf = function () {
      return this._links.default.href;
    };
  }

  private reselectRows(data: any[], isTree: boolean) {
    const primaryKey = this.configuration.primaryKey;
    const selectedData = this.grid.selectedRows;

    if (primaryKey && selectedData.length !== 0) {
      if (isTree) {
        const gridData = this.grid.data;
        data = [...data, ...gridData];
      }
      const dataIds = data.map((row) => row[primaryKey]);
      const deselectRows = [];

      selectedData.forEach((id) => {
        if (dataIds.indexOf(id) === -1) {
          deselectRows.push(id);
        }
      });

      if (deselectRows.length !== 0) {
        this.grid.deselectRows(deselectRows);
      }
    }
  }

  public appendPaging(queryString, virtualizationArgs: any, pageIndex) {
    let queryParams =
      queryString.indexOf("?") >= 0
        ? ListComponentWidget.QUERY_PARAMS_PAGING_CONTD
        : ListComponentWidget.QUERY_PARAMS_PAGING_START;
    queryString += queryParams;
    let uriParams: any = {};
    if (virtualizationArgs) {
      uriParams.offset = this._getNextPage
        ? pageIndex
        : virtualizationArgs.startIndex;
      uriParams.limit = this.isEagerLoading
        ? this._eagerLimit
          ? this._eagerLimit
          : "ALL"
        : this._pageLimit || virtualizationArgs.chunkSize || 16;
    }

    let template = uriTemplates(queryString);
    return template.fill(uriParams);
  }

  public buildDataUrlWithOutPaging(
    queryString: string,
    locale: string,
    dataType: string,
    viewmode: string,
    sortingArgs: ISortingExpression[],
    filteringArgs: IFilteringExpressionsTree,
    attributes: string[],
    force: boolean = false
  ): string {
    let queryParams =
      queryString.indexOf("?") >= 0
        ? ListComponentWidget.QUERY_PARAMS_CONTD
        : ListComponentWidget.QUERY_PARAMS_START;
    queryString =
      queryString.indexOf("filter*") >= 0
        ? queryString
        : queryString + queryParams;
    let uriParams: any = {
      viewmode,
      type: dataType,
    };
    uriParams.attributes = attributes.map((identifier) =>
      Attributes.fromField(identifier)
    );
    uriParams["data-locale"] = locale;
    uriParams["type"] = dataType;

    if (force) {
      uriParams["force-reload"] = force;
    }

    let filter = EMPTY_STRING;

    if (sortingArgs && sortingArgs.length > 0) {
      let sorts = [];
      sortingArgs.forEach((sort) => {
        let sortingDirection: string;
        switch (sort.dir) {
          case SortingDirection.Asc:
            sortingDirection = "asc";
            break;
          case SortingDirection.Desc:
            sortingDirection = "desc";
            break;
          default:
            sortingDirection = SortOrder.NONE;
        }

        if (sortingDirection == SortOrder.NONE) {
          return;
        }

        sorts.push(sort.fieldName + ":" + sortingDirection);
      });

      uriParams.sort = sorts;
    }

    if (filteringArgs && filteringArgs.filteringOperands.length > 0) {
      uriParams.filter = [this.mapFilterTree(filteringArgs)];
    }

    let template = uriTemplates(queryString);
    return template.fill(uriParams);
  }

  private mapFilterTree(tree: IFilteringExpressionsTree): string {
    let operator = tree.operator == FilteringLogic.And ? "and" : "or";
    let filters = tree.filteringOperands
      .map((operand) => {
        if (DataListService.isExpressionsTree(operand)) {
          return this.mapFilterTree(operand);
        } else {
          return this.mapFilterExpression(operand);
        }
      })
      .join(",");

    return `${operator}(${filters})`;
  }

  private mapFilterExpression(expression: IFilteringExpression): string {
    let value = expression.searchVal;
    let condition = expression.condition.name;
    let fieldName = expression.fieldName;
    return DataListService.mapFilterTypeValue(condition, fieldName, value);
  }

  private static isExpressionsTree(
    operand: IFilteringExpressionsTree | IFilteringExpression
  ): operand is IFilteringExpressionsTree {
    return operand.hasOwnProperty("operator");
  }

  private _buildDataUrl(
    queryString: string,
    locale: string,
    dataType: string,
    viewmode: string,
    virtualizationArgs: any,
    sortingArgs: ISortingExpression[],
    filteringArgs: IFilteringExpressionsTree,
    attributes: string[],
    pageIndex: number,
    force: boolean = false
  ): string {
    const uriWithoutPaging = this.buildDataUrlWithOutPaging(
      queryString,
      locale,
      dataType,
      viewmode,
      sortingArgs,
      filteringArgs,
      attributes,
      force
    );
    this._linkWithoutPaging.next(uriWithoutPaging);
    return this.appendPaging(uriWithoutPaging, virtualizationArgs, pageIndex);
  }

  getStartIndex(chunkSize) {
    if (this.isEagerLoading) {
      return 0;
    }
    if (this._grid instanceof IgxGridComponent) {
      return this._grid.virtualizationState.startIndex;
    } else {
      let startIndex = this._grid.verticalScrollContainer.state.startIndex;

      if (this._grid instanceof IgxHierarchicalGridComponent) {
        if (startIndex > 0) {
          let index = startIndex + this._grid.expansionStates.size;
          let limit = this.cachedData.length - chunkSize;

          return index < limit ? index : limit - 1;
        } else {
          return 0;
        }
      }
      return startIndex;
    }
  }

  static mapFilterTypeValue(condition, fieldName, value) {
    let filterString;

    switch (condition) {
      case "object":
      case "contains": {
        filterString =
          fieldName + ":" + FILTER_OPERATION.CONTAINS + ":" + '"' + value + '"';
        break;
      }
      case "startsWith": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.STARTS_WITH +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "endsWith": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.ENDS_WITH +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "equals": {
        filterString =
          fieldName + ":" + FILTER_OPERATION.EQUALS + ":" + '"' + value + '"';
        break;
      }
      case "doesNotEqual": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.DOES_NOT_EQUAL +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "doesNotContain": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.DOES_NOT_CONTAIN +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "greaterThan": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.GREATER_THAN +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "greaterThanOrEqualTo": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.GREATER_THAN_EQUAL +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "lessThan": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.LESS_THAN +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "lessThanOrEqualTo": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.LESS_THAN_EQUAL +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "empty": {
        filterString =
          fieldName + ":" + FILTER_OPERATION.EQUALS + ":" + '"' + value + '"';
        break;
      }
      case "notEmpty": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.GREATER_THAN +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
      case "null": {
        filterString =
          fieldName + ":" + FILTER_OPERATION.EQUALS + ":" + '"' + value + '"';
        break;
      }
      case "notNull": {
        filterString =
          fieldName +
          ":" +
          FILTER_OPERATION.DOES_NOT_EQUAL +
          ":" +
          '"' +
          value +
          '"';
        break;
      }
    }

    return filterString;
  }

  public get grid() {
    return this._grid;
  }

  ngOnDestroy(): void {
    if (this._prevRequest) {
      this._progressbarService.requestFinished();
      this._prevRequest.unsubscribe();
    }

    this._unsubscribe.destroy();
  }
}

results matching ""

    No results matching ""