import { isNullOrUndefined , NumberFormatOptions , DateFormatOptions , extend } from '@syncfusion/ej2-base' ; import { Query , DataManager , Predicate , Deferred , UrlAdaptor } from '@syncfusion/ej2-data' ; import { IDataProcessor , IGrid , DataStateChangeEventArgs , DataSourceChangedEventArgs , PendingState } from '../base/interface' ; import { ReturnType } from '../base/type' ; import { SearchSettingsModel , PredicateModel , SortDescriptorModel } from '../base/grid-model' ; import { setFormatter , getDatePredicate , getColumnByForeignKeyValue } from '../base/util' ; import { AggregateRowModel , AggregateColumnModel } from '../models/models' ; import * as events from '../base/constant' ; import { ValueFormatter } from '../services/value-formatter' ; import { ServiceLocator } from '../services/service-locator' ; import { Column , ColumnModel } from '../models/column' ; import { CheckBoxFilter } from '../actions/checkbox-filter' ; import { SortDirection } from '../base/enum' ; hidden export class Data implements IDataProcessor { public dataManager : DataManager ; protected parent : IGrid ; protected serviceLocator : ServiceLocator ; protected dataState : PendingState = { isPending : false , resolver : null , group : [ ] } ; hidden constructor ( parent ? , serviceLocator ? ) { this . parent = parent ; this . serviceLocator = serviceLocator ; this . initDataManager ( ) ; if ( this . parent . isDestroyed || this . getModuleName ( ) === 'foreignKey' ) { return ; } this . parent . on ( events . rowsAdded , this . addRows , this ) ; this . parent . on ( events . rowsRemoved , this . removeRows , this ) ; this . parent . on ( events . dataSourceModified , this . initDataManager , this ) ; this . parent . on ( events . destroy , this . destroy , this ) ; this . parent . on ( events . updateData , this . crudActions , this ) ; this . parent . on ( events . addDeleteAction , this . getData , this ) ; } protected getModuleName ( ) { return 'data' ; } return { void } private initDataManager ( ) { let gObj = this . parent ; this . dataManager = gObj . dataSource instanceof DataManager ? < DataManager > gObj . dataSource : ( isNullOrUndefined ( gObj . dataSource ) ? new DataManager ( ) : new DataManager ( gObj . dataSource ) ) ; gObj . query = gObj . query instanceof Query ? gObj . query : new Query ( ) ; } return { Query } hidden public generateQuery ( skipPage ? ) { let gObj = this . parent ; let query = gObj . query . clone ( ) ; if ( this . parent . columnQueryMode === 'ExcludeHidden' ) { query . select ( ( < Column [ ] > this . parent . getColumns ( ) ) . filter ( ( column ) => ! ( column . isPrimaryKey !== true && column . visible === false || column . field === undefined ) ) . map ( ( column ) => column . field ) ) ; } else if ( this . parent . columnQueryMode === 'Schema' ) { let selectQueryFields = [ ] ; let columns = this . parent . columns ; for ( let i = 0 ; i < columns . length ; i ++ ) { selectQueryFields . push ( ( columns [ i ] as Column ) . field ) ; } query . select ( selectQueryFields ) ; } this . filterQuery ( query ) ; this . searchQuery ( query ) ; this . aggregateQuery ( query ) ; this . sortQuery ( query ) ; this . pageQuery ( query , skipPage ) ; this . groupQuery ( query ) ; return query ; } protected aggregateQuery ( query , isForeign ? ) { this . parent . aggregates . forEach ( ( row ) => { row . columns . forEach ( ( column ) => { let types = column . type instanceof Array ? column . type : [ column . type ] ; types . forEach ( ( type ) => query . aggregate ( type . toLowerCase ( ) , column . field ) ) ; } ) ; } ) ; return query ; } protected pageQuery ( query , skipPage ? ) { let gObj = this . parent ; if ( ( gObj . allowPaging || gObj . enableVirtualization ) && skipPage !== true ) { gObj . pageSettings . currentPage = Math . max ( 1 , gObj . pageSettings . currentPage ) ; if ( gObj . pageSettings . pageCount <= 0 ) { gObj . pageSettings . pageCount = 8 ; } if ( gObj . pageSettings . pageSize <= 0 ) { gObj . pageSettings . pageSize = 12 ; } query . page ( gObj . pageSettings . currentPage , gObj . pageSettings . pageSize ) ; } return query ; } protected groupQuery ( query ) { let gObj = this . parent ; if ( gObj . allowGrouping && gObj . groupSettings . columns . length ) { let columns = gObj . groupSettings . columns ; for ( let i = 0 , len = columns . length ; i < len ; i ++ ) { let column = this . getColumnByField ( columns [ i ] ) ; let isGrpFmt = column . enableGroupByFormat ; let format = column . format ; if ( isGrpFmt ) { query . group ( columns [ i ] , this . formatGroupColumn . bind ( this ) , format ) ; } else { query . group ( columns [ i ] , null ) ; } } } return query ; } protected sortQuery ( query ) { let gObj = this . parent ; if ( ( gObj . allowSorting || gObj . allowGrouping ) && gObj . sortSettings . columns . length ) { let columns = gObj . sortSettings . columns ; let sortGrp = [ ] ; for ( let i = columns . length - 1 ; i > - 1 ; i -- ) { let col = this . getColumnByField ( columns [ i ] . field ) ; if ( col ) { col . setSortDirection ( columns [ i ] . direction ) ; } let fn = col . sortComparer && ! this . isRemote ( ) ? ( col . sortComparer as Function ) . bind ( col ) : columns [ i ] . direction ; if ( gObj . groupSettings . columns . indexOf ( columns [ i ] . field ) === - 1 ) { query . sortBy ( col . field , fn ) ; } else { sortGrp . push ( { direction : < SortDirection > fn , field : col . field } ) ; } } for ( let i = 0 , len = sortGrp . length ; i < len ; i ++ ) { query . sortBy ( sortGrp [ i ] . field , sortGrp [ i ] . direction ) ; } } return query ; } protected searchQuery ( query ) { let sSettings = this . parent . searchSettings ; let fields = sSettings . fields . length ? sSettings . fields : this . parent . getColumns ( ) . map ( ( f ) => f . field ) ; let predicateList = [ ] ; let needForeignKeySearch = false ; if ( this . parent . searchSettings . key . length ) { needForeignKeySearch = this . parent . getForeignKeyColumns ( ) . some ( ( col ) => fields . indexOf ( col . field ) > - 1 ) ; if ( needForeignKeySearch && ! ( ( < { getModulename ? : Function ; } > this . dataManager . adaptor ) . getModulename && ( < { getModulename ? : Function ; } > this . dataManager . adaptor ) . getModulename ( ) === 'ODataV4Adaptor' ) ) { fields . forEach ( ( columnName ) => { let column = this . getColumnByField ( columnName ) ; if ( column . isForeignColumn ( ) ) { predicateList = this . fGeneratePredicate ( column , predicateList ) ; } else { predicateList . push ( new Predicate ( column . field , sSettings . operator , sSettings . key , sSettings . ignoreCase , sSettings . ignoreAccent ) ) ; } } ) ; query . where ( Predicate . or ( predicateList ) ) ; } else { query . search ( sSettings . key , fields , sSettings . operator , sSettings . ignoreCase , sSettings . ignoreAccent ) ; } } return query ; } protected filterQuery ( query , column ? , skipFoerign ? ) { let gObj = this . parent ; let predicateList = [ ] ; let fPredicate = { } ; let actualFilter = [ ] ; let foreignColumn = this . parent . getForeignKeyColumns ( ) ; if ( gObj . allowFiltering && gObj . filterSettings . columns . length ) { let columns = column ? column : gObj . filterSettings . columns ; let colType = { } ; for ( let col of gObj . getColumns ( ) as Column [ ] ) { colType [ col . field ] = col . filter . type ? col . filter . type : gObj . filterSettings . type ; } let checkBoxCols = [ ] ; let defaultFltrCols = [ ] ; for ( let col of columns ) { if ( colType [ col . field ] === 'CheckBox' || colType [ col . field ] === 'Excel' ) { checkBoxCols . push ( col ) ; } else { defaultFltrCols . push ( col ) ; } } if ( checkBoxCols . length ) { let excelPredicate = CheckBoxFilter . getPredicate ( checkBoxCols ) ; for ( let prop of Object . keys ( excelPredicate ) ) { let col = getColumnByForeignKeyValue ( prop , foreignColumn ) ; if ( col && ! skipFoerign ) { predicateList = this . fGeneratePredicate ( col , predicateList ) ; actualFilter . push ( col ) ; } else { predicateList . push ( < Predicate > excelPredicate [ prop ] ) ; } } } if ( defaultFltrCols . length ) { for ( let col of defaultFltrCols ) { let column = this . getColumnByField ( col . field ) || getColumnByForeignKeyValue ( col . field , this . parent . getForeignKeyColumns ( ) ) ; let sType = column . type ; if ( getColumnByForeignKeyValue ( col . field , foreignColumn ) && ! skipFoerign ) { actualFilter . push ( col ) ; predicateList = this . fGeneratePredicate ( column , predicateList ) ; } else { if ( sType !== 'date' && sType !== 'datetime' ) { predicateList . push ( new Predicate ( col . field , col . operator , col . value , ! col . matchCase , this . parent . filterSettings . ignoreAccent ) ) ; } else { predicateList . push ( getDatePredicate ( col , sType ) ) ; } } } } if ( predicateList . length ) { query . where ( Predicate . and ( predicateList ) ) ; } else { this . parent . notify ( events . showEmptyGrid , { } ) ; } } return query ; } private fGeneratePredicate ( col , predicateList ) { let fPredicate = { } ; if ( col ) { this . parent . notify ( events . generateQuery , { predicate : fPredicate , column : col } ) ; if ( fPredicate . predicate . predicates . length ) { predicateList . push ( fPredicate . predicate ) ; } } return predicateList ; } param { Query } query return { Promise < Object > } hidden public getData ( args = { requestType : '' } , query ? ) { let key = this . getKey ( args . foreignKeyData && Object . keys ( args . foreignKeyData ) . length ? args . foreignKeyData : this . parent . getPrimaryKeyFieldNames ( ) ) ; if ( this . parent . dataSource && 'result' in this . parent . dataSource ) { let def = this . eventPromise ( args , query , key ) ; return def . promise ; } else { let crud ; switch ( args . requestType ) { case 'delete' : query = query ? query : this . generateQuery ( ) ; let len = Object . keys ( args . data ) . length ; if ( len === 1 ) { crud = this . dataManager . remove ( key , args . data [ 0 ] , query . fromTable , query ) as Promise < Object > ; } else { let changes = { addedRecords : [ ] , deletedRecords : [ ] , changedRecords : [ ] } ; changes . deletedRecords = < Object [ ] > args . data ; crud = this . dataManager . saveChanges ( changes , key , query . fromTable , query . requiresCount ( ) ) as Promise < Object > ; } break ; case 'save' : query = query ? query : this . generateQuery ( ) ; args . index = isNullOrUndefined ( args . index ) ? 0 : args . index ; crud = this . dataManager . insert ( args . data , query . fromTable , query , args . index ) as Promise < Object > ; break ; } if ( crud && ! Array . isArray ( crud ) && ! crud . hasOwnProperty ( 'deletedRecords' ) ) { return crud . then ( ( result ) => { return this . insert ( query , args ) ; } ) ; } else { return this . insert ( query , args ) ; } } } private insert ( query , args ) { if ( ( < { requestType ? : string ; } > args ) . requestType === 'save' ) { this . parent . notify ( events . recordAdded , args ) ; } return this . executeQuery ( query ) ; } private executeQuery ( query ) { if ( this . dataManager . ready ) { let deferred = new Deferred ( ) ; let ready = this . dataManager . ready ; ready . then ( ( e ) => { ( < Promise < Object > > this . dataManager . executeQuery ( query ) ) . then ( ( result ) => { deferred . resolve ( result ) ; } ) ; } ) . catch ( ( e ) => { deferred . reject ( e ) ; } ) ; return deferred . promise ; } else { return this . dataManager . executeQuery ( query ) ; } } private formatGroupColumn ( value , field ) { let gObj = this . parent ; let serviceLocator = this . serviceLocator ; let column = this . getColumnByField ( field ) ; let date = value as Date ; if ( ! column . type ) { column . type = date . getDay ? ( date . getHours ( ) > 0 || date . getMinutes ( ) > 0 || date . getSeconds ( ) > 0 || date . getMilliseconds ( ) > 0 ? 'datetime' : 'date' ) : typeof ( value ) ; } if ( isNullOrUndefined ( column . getFormatter ( ) ) ) { setFormatter ( serviceLocator , column ) ; } let formatVal = ValueFormatter . prototype . toView ( value , column . getFormatter ( ) ) ; return formatVal ; } private crudActions ( args ) { let query = this . generateQuery ( ) ; let promise = null ; let pr = 'promise' ; let key = this . getKey ( args . foreignKeyData && Object . keys ( args . foreignKeyData ) . length ? args . foreignKeyData : this . parent . getPrimaryKeyFieldNames ( ) ) ; if ( this . parent . dataSource && 'result' in this . parent . dataSource ) { this . eventPromise ( args , query , key ) ; } switch ( args . requestType ) { case 'save' : promise = this . dataManager . update ( key , args . data , query . fromTable , query , args . previousData ) as Promise < Object > ; break ; } args [ pr ] = promise ; this . parent . notify ( events . crudAction , args ) ; } hidden public saveChanges ( changes , key , original ) { let query = this . generateQuery ( ) . requiresCount ( ) ; if ( 'result' in this . parent . dataSource ) { let state ; state = this . getStateEventArgument ( query ) ; let deff = new Deferred ( ) ; let args = { requestType : 'batchsave' , changes : changes , key : key , query : query , endEdit : deff . resolve } ; this . setState ( { isPending : true , resolver : deff . resolve } ) ; this . parent . trigger ( events . dataSourceChanged , args ) ; return deff . promise ; } else { let promise = this . dataManager . saveChanges ( changes , key , query . fromTable , query , original ) as Promise < Object > ; return promise ; } } private getKey ( keys ) { if ( keys && keys . length ) { return keys [ 0 ] ; } return undefined ; } hidden public isRemote ( ) { return this . dataManager . dataSource . offline !== true && this . dataManager . dataSource . url !== undefined ; } private addRows ( e ) { for ( let i = e . records . length ; i > 0 ; i -- ) { this . dataManager . dataSource . json . splice ( e . toIndex , 0 , e . records [ i - 1 ] ) ; } } private removeRows ( e ) { let json = this . dataManager . dataSource . json ; this . dataManager . dataSource . json = json . filter ( ( value , index ) => e . records . indexOf ( value ) === - 1 ) ; } private getColumnByField ( field ) { let col ; return ( ( < { columnModel ? : Column [ ] ; } > this . parent ) . columnModel ) . some ( ( column ) => { col = column ; return column . field === field ; } ) && col ; } protected destroy ( ) { if ( this . parent . isDestroyed ) { return ; } this . parent . off ( events . rowsAdded , this . addRows ) ; this . parent . off ( events . rowsRemoved , this . removeRows ) ; this . parent . off ( events . dataSourceModified , this . initDataManager ) ; this . parent . off ( events . destroy , this . destroy ) ; this . parent . off ( events . updateData , this . crudActions ) ; this . parent . off ( events . addDeleteAction , this . getData ) ; } public getState ( ) { return this . dataState ; } public setState ( state ) { return this . dataState = state ; } public getStateEventArgument ( query ) { let adaptr = new UrlAdaptor ( ) ; let dm = new DataManager ( { url : '' , adaptor : new UrlAdaptor } ) ; let state = adaptr . processQuery ( dm , query ) ; let data = JSON . parse ( state . data ) ; return extend ( data , state . pvtData ) ; } private eventPromise ( args , query ? , key ? ) { let state ; let dataArgs = args ; state = this . getStateEventArgument ( query ) ; let def = new Deferred ( ) ; let deff = new Deferred ( ) ; if ( args . requestType !== undefined && this . dataState . isDataChanged !== false ) { state . action = < { } > args ; if ( args . requestType === 'save' || args . requestType === 'delete' ) { let editArgs = args ; editArgs . key = key ; editArgs . state = state ; this . setState ( { isPending : true , resolver : deff . resolve } ) ; dataArgs . endEdit = deff . resolve ; this . parent . trigger ( events . dataSourceChanged , editArgs ) ; deff . promise . then ( ( e ) => { this . setState ( { isPending : true , resolver : def . resolve , group : state . group , aggregates : state . aggregates } ) ; this . parent . trigger ( events . dataStateChange , state ) ; } ) ; } else { this . setState ( { isPending : true , resolver : def . resolve , group : state . group , aggregates : state . aggregates } ) ; this . parent . trigger ( events . dataStateChange , state ) ; } } else { this . setState ( { } ) ; def . resolve ( this . parent . dataSource ) ; } return def ; } }