@zenweb/form
Version:
Zenweb Form module
250 lines (249 loc) • 7.6 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var _a;
import { inject, init } from '@zenweb/inject';
import { MessageCodeResolver } from '@zenweb/messagecode';
import { RequiredError, ValidateError } from 'typecasts';
import { FieldFail } from './field.js';
// const objectSpliter = '$';
function layoutExists(layout, name) {
for (const i of layout) {
if (i === name)
return true;
if (Array.isArray(i))
return layoutExists(i, name);
}
return false;
}
export class Form {
messageCodeResolver;
/**
* 初始化完成的字段
*/
_fields;
/**
* 布局结果
*/
_layout;
/**
* 字段结果
*/
_fieldsResult;
/**
* 表单数据
*/
_data;
/**
* 表单校验错误信息
*/
errors = {};
/**
* 是否有校验错误
*/
get hasErrors() {
return Object.keys(this.errors).length > 0;
}
/**
* 已格式化错误消息
*/
_errorMessages;
/**
* 重置表单
* - 清除已处理数据
* - 清除已处理字段
* - 清除已处理布局
* - 清除错误消息
*/
reset() {
delete this._fieldsResult;
delete this._layout;
delete this._data;
this.errors = {};
delete this._errorMessages;
}
/**
* 取得表单提交结果
*/
get data() {
return this._data;
}
/**
* 设置表单提交结果或初始值
*/
set data(data) {
if (data) {
/*
for (const [name, opt] of Object.entries(this.plainFields)) {
const kpath = name.split(objectSpliter);
const value = propertyAt(data, kpath);
if (typeof value !== 'undefined') {
propertyAt(this._data, kpath, typeCast(value, opt.cast));
}
}
*/
this._data = Object.assign({}, this._data, data);
}
else {
delete this._data;
}
}
/**
* 输出字段
*/
get fields() {
if (!this._fieldsResult) {
this._fieldsResult = {};
for (const [name, field] of Object.entries(this._fields)) {
this._fieldsResult[name] = field.output(this._data, name);
}
}
return this._fieldsResult;
}
/**
* 表单布局,如果不设置或者缺少字段,则自动按顺序追加到结尾
*/
get layout() {
if (!this._layout) {
this._layout = [];
for (const name of Object.keys(this._fields)) {
if (!layoutExists(this._layout, name)) {
this._layout.push(name);
}
}
}
return this._layout;
}
/**
* 输出表单给前端
*/
toJSON() {
return {
fields: this.fields,
layout: this.layout,
};
}
/**
* 验证输入数据
* @param input 输入数据
* @returns 是否有错误
*/
async validate(input) {
this.reset();
for (const [name, field] of Object.entries(this._fields)) {
// 忽略只读字段
if (field.option.readonly)
continue;
try {
// 尝试获取输入数据,先key匹配,如果没有尝试key列表匹配
let _inputData;
if (input && typeof input === 'object') {
if (name in input)
_inputData = input[name];
else if (`${name}[]` in input)
_inputData = input[`${name}[]`];
}
let value = field.clean(_inputData);
if (value !== undefined) {
// 字段数据清理
// 查找对象方法组合为 clean_{fieldname}() 的函数
const cleanField = this[`clean_${name}`];
if (cleanField && typeof cleanField === 'function') {
value = await cleanField.call(this, value);
}
if (!this._data)
this._data = {};
// @ts-ignore
this._data[name] = value;
// propertyAt(this._data, name.split(objectSpliter), value);
}
}
catch (e) {
this.errors[name] = e;
}
}
if (!this.hasErrors && this.clean) {
await this.clean();
}
return !this.hasErrors;
}
/**
* 检查输入数据,如果有误直接抛出异常
*/
async assert(input) {
if (!await this.validate(input)) {
throw new FieldFail('form.fail', undefined, {
errors: this.errorMessages,
});
}
}
/**
* 验证失败 - 抛出异常
*/
fail(code, params) {
throw new FieldFail(code, params);
}
/**
* 错误消息
* - 当发生错误时 key 对应字段名称
*/
get errorMessages() {
if (!this._errorMessages) {
const messages = {};
Object.entries(this.errors).map(([field, e]) => {
if (e instanceof RequiredError) {
messages[field] = this.messageCodeResolver.format(`form.required.${field}`, {});
}
else if (e instanceof ValidateError) {
let code = e.validate;
if (e.validate === 'cast')
code += `.${typeof e.target === 'function' ? e.target.name || '-' : e.target}`;
messages[field] = this.messageCodeResolver.format(`form.validate.${code}.${field}`, e);
}
else if (e instanceof FieldFail) {
messages[field] = this.messageCodeResolver.format(`${e.mcode}.${field}`, e.extra);
}
else {
messages[field] = e.message;
}
});
this._errorMessages = messages;
}
return this._errorMessages;
}
}
__decorate([
inject,
__metadata("design:type", MessageCodeResolver)
], Form.prototype, "messageCodeResolver", void 0);
export class FormBase extends Form {
get data() {
return this._data;
}
set data(data) {
if (data) {
this._data = Object.assign({}, this._data, data);
}
else {
delete this._data;
}
}
/**
* 初始化
*/
async [_a = Symbol()]() {
this._fields = await this.setup();
}
}
__decorate([
init,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], FormBase.prototype, _a, null);