jsdk-offical
Version:
JSDK is the most comprehensive TypeScript framework, like JDK.
425 lines (363 loc) • 17.2 kB
text/typescript
/**
* @project JSDK
* @license MIT
* @website https://github.com/fengboyue/jsdk
*
* @version 2.0.0
* @author Frank.Feng
*/
/// <reference path="Widget.ts"/>
/// <reference path="../model/AjaxProxy.ts"/>
module JS {
export namespace fx {
let J = Jsons;
export type FormWidgetEvents = WidgetEvents |
'changed' | 'validating' | 'validated' | 'loading' | 'loadsuccess' | 'loadfailure' | 'loaderror' | 'dataupdating' | 'dataupdated';
/**
* [newVal, oldVal]
*/
export type FormWidgetEventHanler_Changed<T> = EventHandler2<T, any, any>;
/**
* [rst, val, fieldName]
*/
export type FormWidgetEventHanler_Validating<T> = EventHandler3<T, ValidateResult, any, string>;
/**
* [rst, val, fieldName]
*/
export type FormWidgetEventHanler_Validated<T> = EventHandler3<T, ValidateResult, any, string>;
/**
* [req]
*/
export type FormWidgetEventHanler_Loading<T> = EventHandler1<T, HttpRequest>;
/**
* [rst]
*/
export type FormWidgetEventHanler_Loadsuccess<T> = EventHandler1<T, ResultSet<any>>;
/**
* [rst]
*/
export type FormWidgetEventHanler_Loadfailure<T> = EventHandler1<T, ResultSet<any>>;
/**
* [error]
*/
export type FormWidgetEventHanler_Loaderror<T> = EventHandler1<T, HttpResponse | Error>;
/**
* [newData, oldData]
*/
export type FormWidgetEventHanler_Dataupdating<T> = EventHandler2<T, any, any>;
/**
* [newData, oldData]
*/
export type FormWidgetEventHanler_Dataupdated<T> = EventHandler2<T, any, any>;
export interface FormWidgetListeners<T> extends WidgetListeners<T> {
changed?: FormWidgetEventHanler_Changed<T>
validating?: FormWidgetEventHanler_Validating<T>
validated?: FormWidgetEventHanler_Validated<T>
loading?: FormWidgetEventHanler_Loading<T>
loadsuccess?: FormWidgetEventHanler_Loadsuccess<T>
loadfailure?: FormWidgetEventHanler_Loadfailure<T>
loaderror?: FormWidgetEventHanler_Loaderror<T>
dataupdating?: FormWidgetEventHanler_Dataupdating<T>
dataupdated?: FormWidgetEventHanler_Dataupdated<T>
}
export class FormWidgetConfig<T extends FormWidget> extends WidgetConfig<T> {
disabled?: boolean = false;
dataModel?: Klass<ListModel> = ListModel;
valueModel?: Klass<Model> | Model = Model;
validators?: Array<ValidatorSetting> = [];
autoValidate?: boolean = false;
validateMode?: 'tip' | { mode: 'tip', place?: LRTB } | { showError: (this: T, errorMsg: string) => void, hideError: (this: T) => void } | any = 'tip';
readonly?: boolean = false;
title?: string;
titlePlace?: 'left' | 'top' = 'left';
titleTextPlace?: LOC9 = 'rm';
titleCls?: string;
titleStyle?: string;
titleWidth?: string | number;
bodyCls?: string;
bodyStyle?: string;
data?: any = null;
dataQuery?: string | HttpRequest;
iniValue?: any = null;
listeners?: FormWidgetListeners<T>;
}
export abstract class FormWidget extends Widget implements IValueWidget, IDataWidget {
constructor(cfg: FormWidgetConfig<any>) {
super(cfg);
}
public iniValue(): any
public iniValue(v: any, render?: boolean): this
public iniValue(v?: any, render?: boolean): any {
let cfg = <FormWidgetConfig<any>>this._config;
if (arguments.length == 0) return cfg.iniValue;
cfg.iniValue = v;
if(render) this.value(v, true);
return this
}
public readonly(): boolean
public readonly(is: boolean): this
public readonly(is?: boolean): any {
if (arguments.length == 0) return (<FormWidgetConfig<any>>this._config).readonly;
this._mainEl.prop('readonly', is);
(<FormWidgetConfig<any>>this._config).readonly = is;
return this;
}
protected _onBeforeInit() {
this._initDataModel();
this._initValueModel();
}
protected _onAfterInit() {
let cfg = <FormWidgetConfig<any>>this._config;
if (cfg.dataQuery) this.load(cfg.dataQuery, true);
cfg.disabled ? this.disable() : this.enable();
}
public disable() {
this._mainEl.prop('disabled', true);
(<FormWidgetConfig<any>>this._config).disabled = true;
return this
}
public enable() {
this._mainEl.prop('disabled', false);
(<FormWidgetConfig<any>>this._config).disabled = false;
return this
}
public isEnabled(): boolean {
return !(<FormWidgetConfig<any>>this._config).disabled
}
public title(text: string): this;
public title(): string;
public title(text?: string): any {
let cfg = <FormWidgetConfig<any>>this._config;
if (arguments.length == 0) return cfg.title;
this.widgetEl.find('div[jsfx-role="title"]>span').html(text);
cfg.title = text;
return this
}
protected abstract _bodyFragment(): string;
protected _hAlign(): string {
let al = (<FormWidgetConfig<any>>this._config).titleTextPlace || 'lm';
return { 'l': 'left', 'r': 'right', 'c': 'center' }[al.substr(0, 1)]
}
protected _vAlign(): string {
let al = (<FormWidgetConfig<any>>this._config).titleTextPlace || 'lm';
return { 't': 'top', 'b': 'bottom', 'm': 'middle' }[al.substr(1, 1)]
}
protected _mainEl: JQuery<HTMLElement>;/** 组件内部主DOM对象 */
protected _render() {
let cfg = (<FormWidgetConfig<any>>this._config), titleAttrs = cfg.tip ? ` title=${cfg.tip}` : '';
if (cfg.title) {
let tValign = this._vAlign(), tHalign = this._hAlign(), p0 = tHalign == 'right' && cfg.titlePlace == 'top' ? 'p-0' : '',
cls = `${p0} font-${cfg.sizeMode || 'md'} items-${tValign} items-${tHalign} ${cfg.colorMode ? 'text-' + cfg.colorMode : ''} ${cfg.titleCls || ''}"`;
let style = Types.isDefined(cfg.titleWidth) ? `width:${CssTool.normValue(cfg.titleWidth, '100%')};` : '';
if (cfg.titleStyle) style += cfg.titleStyle;
titleAttrs += ` class="${cls}"`;
if (style) titleAttrs += ` style="${style}"`;
}
let html =
`<div jsfx-role="title"${titleAttrs}>${cfg.title ? '<span>' + cfg.title + '</span>' : ''}</div>
<div jsfx-role="body" class="font-${cfg.sizeMode || 'md'} items-middle ${cfg.bodyCls || ''}" style="flex:1;${cfg.bodyStyle || ''}">
${this._bodyFragment()}
</div>`;
this.widgetEl.html(html);
this._mainEl = this.widgetEl.find('[jsfx-role=main]');
}
protected _onBeforeRender() {
let cfg = (<FormWidgetConfig<any>>this._config),
w = CssTool.normValue(cfg.width, '100%'),
d = cfg.titlePlace == 'left' ? 'flex' : 'grid',
css = {
'display': (w == 'auto' ? 'inline-' : '') + d,
'width': w
}
this.widgetEl.css(css);
}
protected _iniValue() {
let cfg = <FormWidgetConfig<any>>this._config;
this.value(cfg.iniValue, true);
}
protected _onAfterRender() {
this.on('validated', (e: Event, rst: ValidateResult, val: any, name: string) => {
window.setTimeout(() => {//第一次时需要延时执行,等待DOM的样式先生效
rst.hasError() ? this._showError(rst.getErrors(name)[0].message) : this._hideError()
}, 100)
})
this._iniValue();
}
protected _showError(msg: string) {
let cfg = <FormWidgetConfig<any>>this._config,
mode = cfg.validateMode,
fn = (mode == 'tip' || (mode && mode['mode'] == 'tip')) ? this._showTipError : mode['showError'];
if (fn) fn.apply(this, [msg])
}
protected _hideError() {
let cfg = <FormWidgetConfig<any>>this._config,
mode = cfg.validateMode,
fn = (mode == 'tip' || (mode && mode['mode'] == 'tip')) ? this._hideTipError : mode['hideError'];
if (fn) fn.call(this)
}
private _getTipEl(place: LRTB) {//大多数情况下取body
let cfg = <FormWidgetConfig<any>>this._config;
return this.widgetEl.find(cfg.titlePlace == 'left' && place == 'left' ? '[jsfx-role=title]>span': '[jsfx-role=body]')
}
protected _showTipError(msg: string) {
if (!msg) return;
let div = this.widgetEl.find('.error .tooltip-inner');
if (div.length == 1) {//tooltip存在则直接赋值
div.html(msg)
} else {
let cfg = <FormWidgetConfig<any>>this._config,
mode = cfg.validateMode,
place = mode && mode['place'] ? mode['place'] : 'right',
el = this._getTipEl(place);
el.tooltip({
placement: place,
offset: '0, 2px',
fallbackPlacement: <any>'clockwise',
container: el[0],
trigger: 'manual',
html: false,
title: msg,
template: '<div class="tooltip error" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>'
}).tooltip('show')
}
}
protected _hideTipError() {
let cfg = <FormWidgetConfig<any>>this._config,
mode = cfg.validateMode,
place = mode && mode['place'] ? mode['place'] : 'right',
el = this._getTipEl(place);
if(el.tooltip) el.tooltip('dispose')
}
protected _validate(name: string, val: any, rst: ValidateResult): string | boolean {
let field = new ModelField({
name: name,
validators: (<FormWidgetConfig<any>>this._config).validators
});
return field.validate(val, rst)
}
public validate(): string | boolean {
if (Check.isEmpty((<FormWidgetConfig<any>>this._config).validators)) return true;
let name = this.name(),
rst = new ValidateResult(),
val = J.clone(this.value());
this._fire('validating', [rst, val, name]);
let vdt = this._validate(name, val, rst);
this._fire('validated', [rst, val, name]);
return vdt;
}
//////////////////////////////////////////////////////////////////////////////
protected _dataModel: ListModel;
public dataModel<M>(): M {
return <any>this._dataModel
}
protected _initDataModel() {
let me = this, cfg = <FormWidgetConfig<any>>this._config;
this._dataModel = Class.newInstance(cfg.dataModel);
(<FormWidgetEvents[]>['loading', 'loadsuccess', 'loadfailure', 'loaderror', 'dataupdating', 'dataupdated']).forEach(e => {
this._dataModel.on(e, function () {
if (e == 'dataupdated') me.data(this.getData(), true);
me._fire<FormWidgetEvents>(e, Arrays.slice(arguments, 1));
})
})
}
/**
* Read/Write data.
* @param data 数据
* @param silent 是否事件静默
*/
public data(): any
public data(data: any, silent?: boolean): this
public data(data?: any, silent?: boolean): any {
let cfg = <FormWidgetConfig<any>>this._config;
if (arguments.length == 0) return cfg.data;
let newData = J.clone(data),
oldData = J.clone(cfg.data);
if (!silent) this._fire('dataupdating', [newData, oldData]);
cfg.data = data;
if (this._dataModel) this._dataModel.setData(data, true);
this._renderData();
this._renderValue();
if (!silent) this._fire('dataupdated', [newData, oldData]);
return this;
}
protected _renderData() { }
/**
* Clear value.
*/
public clear(silent?: boolean) {
return this.value(null, silent)
}
/**
* Load data from server.
*/
public load(quy: string | HttpRequest, silent?: boolean): Promise<ResultSet<any>> {
let cfg = <FormWidgetConfig<any>>this._config;
cfg.dataQuery = <HttpRequest>J.union(Http.toRequest(cfg.dataQuery), Http.toRequest(quy));
return this._dataModel.load(cfg.dataQuery, silent);
}
public reload() {
if (this._dataModel) this._dataModel.reload();
return this
}
protected _equalValues(newVal: any, oldVal: any): boolean {
return oldVal == newVal
}
public value(): any
public value(val: any, silent?: boolean): this
public value(val?: any, silent?: boolean): any {
let cfg = <FormWidgetConfig<any>>this._config, oldVal = this._valueModel.get(this.name());
if (arguments.length == 0) return oldVal;
this._setValue(val, silent);
this._renderValue();
return this
}
protected _setValue(val, silent?: boolean) {
this._hideError();
this._valueModel.set(this.name(), val, silent || this._equalValues(val, this.value()));
if ((<FormWidgetConfig<any>>this._config).autoValidate) this.validate();
}
protected _renderValue(): void {
let v: string = this.value() || '';
if (this._mainEl.val() !== v) this._mainEl.val(v);
}
/**
* Restore to ini value.
*/
public reset() {
return this.value((<FormWidgetConfig<any>>this._config).iniValue)
}
protected _valueModel: Model;
public valueModel(): Model {
return this._valueModel
}
protected _initValueModel() {
let cfg = <FormWidgetConfig<any>>this._config, vModel = cfg.valueModel;
if (!vModel) {
this._valueModel = new Model();
} else if (Types.subklassOf(<any>vModel, Model)) {
this._valueModel = Class.newInstance(<Klass<Model>>vModel);
} else {
this._valueModel = <Model>vModel;
}
this._valueModel.addField({
name: this.name(),
validators: cfg.validators
});
let me = this;
this._valueModel.on('dataupdated', function (e, newData) {
let fName = me.name();
if (newData && newData.hasOwnProperty(fName)) {
me.value(newData[fName]);
}
});
this._valueModel.on('fieldchanged', (e, newVal, oldVal) => {
this._fire<FormWidgetEvents>('changed', [newVal, oldVal])
});
}
//////////////////////////////////////////////////////////////////////////////
}
}
}
import FormWidgetConfig = JS.fx.FormWidgetConfig;
import FormWidget = JS.fx.FormWidget;
import FormWidgetEvents = JS.fx.FormWidgetEvents