UNPKG

jsdk-offical

Version:

JSDK is the most comprehensive TypeScript framework, like JDK.

410 lines (346 loc) 14.6 kB
/** * @project JSDK * @license MIT * @website https://github.com/fengboyue/jsdk * * @version 2.0.0 * @author Frank.Feng */ /// <reference path="../lang/System.ts"/> /// <reference path="Model.ts"/> module JS { export namespace model { let J = Jsons; export type ListModelEvents = 'dataupdating'|'dataupdated'|'rowadded'|'rowremoved'|'validated'|'rowvalidated'|'loading'|'loadsuccess'|'loadfailure'|'loaderror'; export interface ListModelListeners<M> { /** * @event (this:ListModel, e:CustomEvent, newData:JsonObject[], oldData:JsonObject[]) */ dataupdating: EventHandler2<M, JsonObject[], JsonObject[]> /** * @event (this:ListModel, e:CustomEvent, newData:JsonObject[], oldData:JsonObject[]) */ dataupdated: EventHandler2<M, JsonObject[], JsonObject[]> /** * @event (this:ListModel, e:CustomEvent, newRows:JsonObject[], index:number) */ rowadded: EventHandler2<M, JsonObject[], number> /** * @event (this:ListModel, e:CustomEvent, removedRow:JsonObject, index:number) */ rowremoved: EventHandler2<M, JsonObject, number> /** * @event (this:ListModel, e:CustomEvent, result:ValidateResult, data:JsonObject[]) */ validated: EventHandler2<M, ValidateResult, JsonObject[]> /** * @event (this:ListModel, e:CustomEvent, result:ValidateResult, row:JsonObject, index:number) */ rowvalidated: EventHandler3<M, ValidateResult, JsonObject, number> /** * @event (this:ListModel, e:CustomEvent, request:HttpRequest) */ loading: EventHandler1<M, HttpRequest> /** * @event (this:ListModel, e:CustomEvent, result:ResultSet<any>) */ loadsuccess: EventHandler1<M, ResultSet<any>> /** * @event (this:ListModel, e:CustomEvent, result:ResultSet<any>) */ loadfailure: EventHandler1<M, ResultSet<any>> /** * @event (this:ListModel, e:CustomEvent, res:HttpResponse|Error) */ loaderror: EventHandler1<M, HttpResponse|Error> }; export type Sorter = { field: string, //The property of field to sort by. Required unless fn is provided dir?: 'asc' | 'desc', sort?: (record1: any, record2: any) => number //used in local sort } export class ListModelConfig { autoLoad?: boolean = false; readonly listeners?: ListModelListeners<this>; sorters?: Array<Sorter>; dataQuery?: string | HttpRequest; iniData?: JsonObject[]; } /** * A model for list data. */ @klass('JS.model.ListModel') export class ListModel { protected _config: ListModelConfig; protected _data: JsonObject[] = []; protected _eventBus = new EventBus(this); private _isD = false; constructor(cfg?: ListModelConfig) { this._config = this._initConfig(cfg); let listeners = this._config.listeners; if (listeners) J.forEach(<JsonObject>listeners, (v: EventHandler, key: ListModelEvents) => { this.on(key, v); }) if (this._config.iniData) this.setData(this._config.iniData); if (this._config.autoLoad) this.reload(); } protected _initConfig(cfg?:ListModelConfig){ return J.union(new ListModelConfig(), cfg) } protected _check() { if (this.isDestroyed()) throw new RefusedError('The model was destroyed!'); } public addSorter(field: string, dir?: 'asc' | 'desc') { this._check(); let newSorter: Sorter = { field: field, dir: dir ? dir : 'asc' }, has = false, sorters = this._config.sorters; if (!sorters) sorters = []; sorters.some((sorter: Sorter) => { if (newSorter.field == sorter.field) { has = true; if (newSorter.sort) sorter.sort = newSorter.sort; sorter.dir = newSorter.dir; return true; } return false; }); if (!has) sorters.push(newSorter); this._config.sorters = sorters; } public removeSorter(field: string) { this._check(); let sorters = this._config.sorters; if (!sorters) return; sorters.remove(item=> { return item.field == field; }) } public clearSorters() { this._check(); this._config.sorters = []; } public sort(field: string, dir?: 'desc' | 'asc'): Promise<any> { this._check(); this.addSorter(field, dir); return this.reload(); } public getSorterBy(fieldName: string): Sorter { let sorters = this._config.sorters; if (!sorters) return null; let sorter: Sorter = null; sorters.some((srt: Sorter) => { let is = srt.field === fieldName; if (is) sorter = srt; return is; }); return sorter; } private _sortParams(): JsonObject { let sorters = this._config.sorters; if (!sorters) return null; let s = ''; sorters.forEach((sorter: Sorter) => { s += `${sorter.field} ${sorter.dir ? sorter.dir : 'asc'},`; }); s = s.slice(0, s.length - 1); return { sorters: s } } public reload() { return this.load(this._config.dataQuery); } private _modelKlass:Klass<Model> = null; public modelKlass():Klass<Model> public modelKlass(klass:Klass<Model>):this public modelKlass(klass?:Klass<Model>){ if(arguments.length==0) return this._modelKlass; this._modelKlass = klass return this } /** * load using its configured query. */ public load<R=JsonObject[]>(quy: string | HttpRequest, silent?:boolean): Promise<ResultSet<R>> { this._check(); let me = this, query = <HttpRequest>J.union(Http.toRequest(this._config.dataQuery),Http.toRequest(quy)); query.data = J.union(<JsonObject>query.data,this._sortParams()); this._fire('loading', [query]); this._config.dataQuery = query; return new JsonProxy().execute<R>(query).then(function (result: ResultSet<R>) { if (result.success()) { me.setData(<any>result.data(), silent); me._fire('loadsuccess', [result]); } else { me._fire('loadfailure', [result]); } return Promise.resolve(<any>result); }).catch(function (err: HttpResponse|Error) { if(Types.ofKlass(err, Error)) JSLogger.error('['+(<Error>err).name+']'+(<Error>err).message); me._fire('loaderror', [err]); }); } public getData(): JsonObject[] { return this.isEmpty()?null:this._data; } /** * Read data into model. * @param data * @param silent ignore events */ public setData(data: JsonObject[], silent?: boolean) { this._check(); let newData = data, oldData = J.clone(this._data); if (!silent) this._fire('dataupdating', [newData, oldData]); this._data = data||[]; if (!silent) this._fire('dataupdated', [newData, oldData]); return this; } public iniData(): any public iniData(data: any): this public iniData(d?: any): any { let cfg = this._config; if (arguments.length == 0) return cfg.iniData; cfg.iniData = d; return this } public reset() { return this.setData(this.iniData()); } /** * Add new data into the tail. * @param records * @param silent ignore events */ public add(records: JsonObject | JsonObject[], silent?: boolean) { return this.insert(this._data.length, records, silent); } /** * Insert new data into the index. * @param index * @param records * @param silent ignore events */ public insert(index: number, records: JsonObject | JsonObject[], silent?: boolean) { if (!records) return this; this._check(); this._data = this._data||[]; let models = Arrays.toArray(records); this._data.add(models, index); if (!silent) this._fire('rowadded', [models, index]); return this; } public getRowModel<T extends Model>(index:number, klass?: Klass<T>): T{ if (index < 0 || index >= this.size()) return null; let d = this._data[index]; if(!d) return null; let k = klass||this._modelKlass; if(!k) throw new NotFoundError('The model klass not found!'); return Class.newInstance<T>(k).setData(d,true) } public getModels<T extends Model>(klass?: Klass<T>): T[]{ if(this.size()==0) return null; let k = klass||this._modelKlass; if(!k) throw new NotFoundError('The model klass not found!'); let mds:T[] = []; this._data.forEach((d,i)=>{ mds[i] = Class.newInstance<T>(k).setData(d,true) }) return mds } public getRowById(id: number | string): JsonObject { return this.getRow(this.indexOfId(id)); } public getRow(index: number): JsonObject { if (index < 0 || index >= this.size()) return null; return this._data[index] || null; } public indexOfId(id: number | string): number { if (!id || this.size()==0) return -1; let idName = 'id'; if(this._modelKlass && Types.subklassOf(this._modelKlass, Model)) {//如果modelKlass是Model的子类,则获取其id名称 let model = Class.newInstance<Model>(this._modelKlass), field = model.getIdField(); if(field) idName = field.alias(); } let index = -1; this._data.some((obj, i) => { let ret = obj[idName] == id; if (ret) index = i; return ret; }) return index; } public removeAt(index: number, silent?: boolean) { this._check(); if (this.size()==0) return this; const obj = this._data[index]; if (obj) { this._data.remove(index); if (!silent) this._fire('rowremoved', [obj, index]); } return this; } public clear(silent?: boolean) { return this.setData(null, silent); } public validate(): string | boolean { if (this.size()==0) return true; let rst = new ValidateResult(), str = ''; this._data.forEach(m => { let ret = m.validate(rst); if (ret !== true) str += (str ? '|' : '') + ret; }); this._fire('validated', [this._data, rst]); return str || true; } public validateRow(index:number): string | boolean { let row = this.getRow(index); if(!row) return null; let rst = row.validate(); this._fire('rowvalidated', [rst, row, index]); return rst } public size() { return !this._data?0:this._data.length } public isEmpty() { return this.size() == 0 } public clone() { let model = Class.newInstance<this>(this.className, J.clone(this._config)); model.setData(this.getData()); return model } protected _fire(type: string, args?: any[]) { this._eventBus.fire(type, args); } public on<H=EventHandler<this>>(type: string, fn: H, once?:boolean) { this._check(); this._eventBus.on(type, fn, once); return this } public off(type?: string) { this._check(); this._eventBus.off(<any>type); return this } public destroy() { if (this._isD) return; this._eventBus.destroy(); this._eventBus = null; this._data = null; this._isD = true; } public isDestroyed() { return this._isD } } } } //预定义短类名 import ListModel = JS.model.ListModel; import ListModelConfig = JS.model.ListModelConfig; import ListModelEvents = JS.model.ListModelEvents; import ListModelListeners = JS.model.ListModelListeners;