@rdkmaster/jigsaw-labs
Version:
Jigsaw, the next generation component set for RDK
239 lines (201 loc) • 7.81 kB
text/typescript
import {EventEmitter} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import "rxjs/add/operator/map";
import {Subscription} from "rxjs/Subscription";
import {ComponentDataHelper, DataReviser, HttpClientOptions, IAjaxComponentData, IEmittable} from "./component-data";
import {CallbackRemoval} from "../utils/common-utils";
/**
* 关于Jigsaw数据体系详细介绍,请参考`IComponentData`的说明
*/
export abstract class AbstractGeneralCollection<T = any> implements IAjaxComponentData, IEmittable {
/**
* 将一个任意类型的数据转为一个当前类型的数据对象。
*
* $demo = tree/basic
*
* @param {T} data 原始数据
* @returns {AbstractGeneralCollection<T>} 返回持有输入的原始数据的当前数据对象
*/
public abstract fromObject(data: T): AbstractGeneralCollection<T>;
/**
* 调用在`onAjaxSuccess`里注册的所有回调函数。
*
* @param {any} data 服务端返回的数据
*/
protected abstract ajaxSuccessHandler(data): void;
/**
* 用于发起网络请求,在调用`fromAjax()`之前必须设置好此值。
*/
public http: HttpClient;
public dataReviser: DataReviser;
/**
* 与`busy`具有相同功能
*/
protected _busy: boolean;
get busy(): boolean {
return this._busy;
}
/**
* 安全地调用`dataReviser`函数。
*
* @param originData
* @return {any}
*/
protected reviseData(originData: any): any {
if (!this.dataReviser) {
return originData;
}
try {
const revisedData = this.dataReviser(originData);
if (revisedData == undefined) {
console.error('a dataReviser function should NOT return undefined,' +
'use null is you do not have any valid value!' +
'Jigsaw is ignoring this result and using the original value.');
return originData;
} else {
return revisedData;
}
} catch (e) {
console.error('revise data error: ' + e);
console.error(e.stack);
return originData;
}
}
public fromAjax(url?: string): void;
public fromAjax(options?: HttpClientOptions): void;
/**
* @internal
*/
public fromAjax(optionsOrUrl?: HttpClientOptions | string): void {
if (!this.http) {
console.error('set a valid HttpClient instance to the http attribute before invoking fromAjax()!');
return;
}
if (this._busy) {
this.ajaxErrorHandler(null);
return;
}
this.ajaxStartHandler();
const op = HttpClientOptions.prepare(optionsOrUrl);
this.http.request(op.method, op.url, op)
.map(res => this.reviseData(res))
.subscribe(
data => this.ajaxSuccessHandler(data),
error => this.ajaxErrorHandler(error),
() => this.ajaxCompleteHandler()
);
}
protected componentDataHelper: ComponentDataHelper = new ComponentDataHelper();
public refresh(): void {
this.componentDataHelper.invokeRefreshCallback();
}
public onRefresh(callback: (thisData: AbstractGeneralCollection<T>) => void, context?: any): CallbackRemoval {
return this.componentDataHelper.getRefreshRemoval({fn: callback, context: context});
}
public onAjaxStart(callback: (data: T) => void, context?: any): CallbackRemoval {
return this.componentDataHelper.getAjaxStartRemoval({fn: callback, context: context});
}
public onAjaxSuccess(callback: (data: T) => void, context?: any): CallbackRemoval {
return this.componentDataHelper.getAjaxSuccessRemoval({fn: callback, context: context});
}
public onAjaxError(callback: (error: Response) => void, context?: any): CallbackRemoval {
return this.componentDataHelper.getAjaxErrorRemoval({fn: callback, context: context});
}
public onAjaxComplete(callback: () => void, context?: any): CallbackRemoval {
return this.componentDataHelper.getAjaxCompleteRemoval({fn: callback, context: context});
}
/**
* 调用在`onAjaxStart`里注册的所有回调函数。
*/
protected ajaxStartHandler(): void {
this._busy = true;
this.componentDataHelper.invokeAjaxStartCallback();
}
/**
* 调用在`onAjaxError`里注册的所有回调函数。
*/
protected ajaxErrorHandler(error: Response): void {
if (!error) {
const reason = 'the data collection is busy now!';
console.warn('get data from paging server error!! detail: ' + reason);
error = new Response(reason, {status: 409, statusText: reason});
} else {
console.error('get data from paging server error!! detail: ' + error['message']);
this._busy = false;
}
this.componentDataHelper.invokeAjaxErrorCallback(error);
}
/**
* 调用在`onAjaxComplete`里注册的所有回调函数。
*/
protected ajaxCompleteHandler(): void {
console.log('get data from paging server complete!!');
this._busy = false;
this.componentDataHelper.invokeAjaxCompleteCallback();
}
public destroy(): void {
this._emitter.unsubscribe();
this._emitter = null;
this.componentDataHelper.clearCallbacks();
this.componentDataHelper = null;
this.dataReviser = null;
}
private _emitter = new EventEmitter<any>();
public emit(value?: any): void {
this._emitter.emit(value);
}
public subscribe(callback?: (value:any) => void): Subscription {
return this._emitter.subscribe(callback);
}
public unsubscribe() {
this._emitter.unsubscribe();
}
}
/**
* 这是Jigsaw数据体系中两大分支之一:通用的key-value(即JSON对象)的集合类型的基类。
*
* 关于Jigsaw数据体系详细介绍,请参考`IComponentData`的说明
*/
export class GeneralCollection<T> extends AbstractGeneralCollection<T> {
[index: string]: any;
protected ajaxSuccessHandler(data: T): void {
this.fromObject(data);
this._busy = false;
this.componentDataHelper.invokeAjaxSuccessCallback(data);
}
protected propList: string[] = [];
public fromObject(data: T): GeneralCollection<T> {
if (data instanceof GeneralCollection) {
console.error("unable to make data from another GeneralCollection instance!");
return;
}
let needRefresh = false;
this.propList.forEach(prop => {
needRefresh = true;
delete this[prop];
});
this.propList.splice(0, this.propList.length);
if (data) {
for (let key in data) {
if (!data.hasOwnProperty(key) || data[key] instanceof Function) {
continue;
}
needRefresh = true;
this[key] = data[key];
this.propList.push(key);
}
}
if (needRefresh) {
this.refresh();
}
return this;
}
public destroy(): void {
super.destroy();
console.log('destroying GeneralCollection....');
this.propList.forEach((prop: string) => {
delete this[prop];
});
this.propList.splice(0, this.propList.length);
}
}