import { Observable , Subject , BehaviorSubject , Subscription } from 'rxjs' ; import { Record , RecordChangedEvent , RecordStateData , RecordRawData , BaseVirtualFieldStateData } from './record' ; import { RecordList } from './record-list' ; import { Backend } from '../base-backend' ; import { ListModel } from '../models/list-model' ; import { utilRecordModelFromRawData } from '../utils' ; import { Condition } from '../query/q' ; import { QuerySet } from '../query/queryset' ; import { SaveQuery , ChildQueryDescriber } from '../query/query' ; import { ForeignKeyVirtualField } from '../fields/foreign-key' ; import { BaseField } from '../fields/base' ; export class RelatedRecordParams { listModel ? : ListModel ; linkThrough ? : string ; } export class RelatedRecords extends RecordList { params : RelatedRecordParams ; record : Record ; backend : Backend ; isLoaded : boolean = false ; loaded = new Subject ( ) ; markedToDelete : Record [ ] = [ ] ; listModel : ListModel ; _queryset : QuerySet ; get queryset ( ) : QuerySet { if ( ! this . record . __director__ . isNew && ! this . _queryset ) { this . _queryset = this . getQueryset ( ) ; } return this . _queryset ; } constructor ( backend , record , params ? , data ? ) { super ( ) ; this . backend = backend ; this . record = record ; this . params = params ; this . listModel = this . params . listModel ? this . params . listModel : null ; this . initData ( data ) ; } initData ( data ) { this . isLoaded = false ; if ( ! data ) return ; this . isLoaded = true ; var records = [ ] ; if ( data . length ) { data . forEach ( rawRecord => { var model = utilRecordModelFromRawData ( this . backend , rawRecord ) ; records . push ( model . constructRecord ( this . record . __director__ . isNew , rawRecord ) ) ; } ) ; } super . initItems ( records ) ; } getQueryset ( ) { var condition = { } ; if ( ! this . params . linkThrough ) throw new Error ( "" ) ; var linkField = this . listModel . getField ( this . params . linkThrough ) ; if ( linkField instanceof ForeignKeyVirtualField ) { for ( let tableFieldName in linkField . keys ) { let parentRecordFieldName = linkField . keys [ tableFieldName ] ; if ( this . record [ parentRecordFieldName ] === undefined ) return undefined ; condition [ tableFieldName ] = this . record [ parentRecordFieldName ] ; } } else { condition [ this . params . linkThrough ] = this . record ; } return this . listModel . getQueryset ( ) . filter ( condition ) ; } getRecords ( update = false ) { if ( this . isLoaded && ! update ) { return Observable . of ( this . items ) ; } else if ( ! this . record . __director__ . isNew ) { return this . queryset . getRows ( ) . map ( data => { this . initItems ( data as Record [ ] ) ; this . isLoaded = true ; this . loaded . next ( null ) ; return data ; } ) ; } } add ( ... records ) { if ( ! this . params . linkThrough ) throw new Error ( `'linkThrough' is not defined to add records.` ) ; for ( let field of this . listModel . getDbFields ( ) ) { if ( field . sorter ) { field . sorter . alterNewRecord ( this . items , records ) ; } } records . forEach ( record => { if ( this . listModel . recordModels . indexOf ( record . __director__ . model . name ) == - 1 ) throw new Error ( `"${ record . __director__ . model . name }" record doesn't suit this list` ) ; if ( ! record [ this . params . linkThrough ] ) record [ this . params . linkThrough ] = this . record ; var markedToDelPos = this . markedToDelete . indexOf ( record ) ; if ( markedToDelPos !== - 1 ) { this . markedToDelete . splice ( markedToDelPos , 1 ) ; } } ) ; var result = new BehaviorSubject < null > ( null ) ; if ( ! this . isLoaded ) { this . getRecords ( ) . subscribe ( data => { super . add ( ... records ) ; result . next ( null ) ; } ) ; } else { super . add ( ... records ) ; result . next ( null ) ; } return result ; } remove ( ... records ) { records . forEach ( record => { var pos = this . items . indexOf ( record ) ; if ( pos !== - 1 ) { if ( ! record . __director__ . isNew ) this . markedToDelete . push ( record ) ; } ; } ) ; super . remove ( ... records ) ; } extractRAWData ( ) { var result = [ ] ; this . items . forEach ( record => { result . push ( record . __director__ . extractRAWData ( ) ) ; } ) ; return result ; } clear ( ) { super . clear ( ) ; } reset ( ) { this . clear ( ) ; this . isLoaded = false ; } markToDelete ( record ) { } clone ( asNew ) { var rawData ; if ( this . isLoaded ) { rawData = [ ] ; this . items . forEach ( record => { rawData . push ( record . __director__ . clone ( true , asNew , false ) ) ; } ) ; } var rrClass = Object . getPrototypeOf ( this ) . constructor ; return new rrClass ( this . backend , this . record , this . params , rawData ) ; } save ( ) { } getLinkedFields ( ) { if ( ! this . params . linkThrough ) return ; var result = [ ] ; var linkField = this . listModel . getField ( this . params . linkThrough ) ; if ( linkField instanceof ForeignKeyVirtualField ) result . push ( this . params . linkThrough , ... Object . keys ( linkField . keys ) ) ; return result ; } }