JQueryFormBuilder 自定义控件
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
定义控件
按照文档继承Control.js 并且注册
//`import control from '../control'`
import control from '../control'
//define a new class extending `control` (or any other child control class)
export default class controlImg extends control {
// define a static get definition class if you wish to have a custom icon
static get definition() {
return {
// mi18n custom mappings (defaults to camelCase type)
mi18n: {
lecImg: 'lecImg'
},
}
}
//define a configure method if required - used to specify external javascript & css files and perform any additional configuration
//define a `build` method - this is always required
build() {
let { name } = this.config
name = this.config.multiple ? `${name}[]` : name
const inputConfig = Object.assign({}, this.config, { name })
inputConfig.src = "http://172.20.176.91/fileserver/yundeetest91/682091851116708864/202009/lecsm/lecsm_index_report_form/images/Z2pJtfK3bNdYflVQOK/u_3287874995_3313728965_fm_199_app_68_f_JPEG.jpg?v=1.0"
inputConfig.width = 30
inputConfig.height = 30
inputConfig.class = 'lec_img'
this.dom = this.markup('img', null, inputConfig)
return this.dom
}
onRender() {
// Set userData if available
if (this.config.userData) {
$(this.dom).val(this.config.userData[0])
}
}
}
// register this control for the following types & text subtypes
control.register(['lecImg'], controlImg)
在index.js中配置
在多语言文件中配置
jqFormBuilder默认是英语,所以我们先在en-US.lang中配置
lecImg是注册的控件名称
src是控件的属性
配置控件渲染以及控件的属性编辑框
经过第一步,可以在界面上的控件列表中看到该控件,但是没法在编辑器中渲染出来,如下图
在form-builder.js中找到defaultFieldAttrs方法,
const defaultFieldAttrs = type => {
//defaultAttrs表示每个控件都有的默认属性
const defaultAttrs = ['required', 'label', 'description', 'placeholder', 'className', 'name', 'access', 'value']
//noValFields 表示 没有 value 属性的控件,
//由于我添加的是一个图片控件,所以lecImg 要放进去
const noValFields = ['header', 'paragraph', 'file', 'autocomplete', 'lecImg'].concat(d.optionFields)
const valueField = !noValFields.includes(type)
// typeAttrsMap 控件类型 与 其属性的map集合,后续创建编辑框时,会通过类型来获取这里我们定义的属性
// 所以,如果有什么自定义的属性需要编辑的可以在这个map中添加
const typeAttrsMap = {
autocomplete: defaultAttrs.concat(['options', 'requireValidOption']),
button: ['label', 'subtype', 'style', 'className', 'name', 'value', 'access'],
checkbox: [
'required',
'label',
'description',
'toggle',
'inline',
'className',
'name',
'access',
'other',
'options',
],
text: defaultAttrs.concat(['subtype', 'maxlength']),
date: defaultAttrs,
file: defaultAttrs.concat(['subtype', 'multiple']),
header: ['label', 'subtype', 'className', 'access'],
hidden: ['name', 'value', 'access'],
paragraph: ['label', 'subtype', 'className', 'access'],
number: defaultAttrs.concat(['min', 'max', 'step']),
select: defaultAttrs.concat(['multiple', 'options']),
textarea: defaultAttrs.concat(['subtype', 'maxlength', 'rows']),
//此处可以加上属性框
lecImg: defaultAttrs.concat(['src', 'width', 'height'])
}
if (type in controls.registeredSubtypes && !(type in typeAttrsMap)) {
typeAttrsMap[type] = defaultAttrs.concat(['subtype'])
}
typeAttrsMap['checkbox-group'] = typeAttrsMap.checkbox
typeAttrsMap['radio-group'] = typeAttrsMap.checkbox
const typeAttrs = typeAttrsMap[type]
if (type === 'radio-group') {
removeFromArray('toggle', typeAttrs)
}
// Help Text / Description Field
if (['header', 'paragraph', 'button'].includes(type)) {
removeFromArray('description', typeAttrs)
}
if (!valueField) {
removeFromArray('value', typeAttrs)
}
return typeAttrs || defaultAttrs
}
在form-builder.js中找到advFields方法,该方法会根据属性生成一个编辑框
所以我们要在这个方法中,为刚刚我们自定义的属性,添加默认的编辑框
找到advFieldMap变量,在其末尾添加
src: () => textAttribute('src', values),
width: () => numberAttribute('width', values),
height: () => numberAttribute('height', values),
/**
* Build the editable properties for the field
* @param {object} values configuration object for advanced fields
* @return {String} markup for advanced fields
* 此处构建该控件的属性编辑框
*/
const advFields = values => {
const { type } = values
const advFields = []
const fieldAttrs = defaultFieldAttrs(type)
const advFieldMap = {
required: () => requiredField(values),
toggle: () => boolAttribute('toggle', values, { first: mi18n.get('toggle') }),
inline: () => {
const labels = {
first: mi18n.get('inline'),
second: mi18n.get('inlineDesc', type.replace('-group', '')),
}
return boolAttribute('inline', values, labels)
},
label: () => textAttribute('label', values),
description: () => textAttribute('description', values),
subtype: () => selectAttribute('subtype', values, subtypes[type]),
style: () => btnStyles(values.style),
placeholder: () => textAttribute('placeholder', values),
rows: () => numberAttribute('rows', values),
className: isHidden => textAttribute('className', values, isHidden),
name: isHidden => textAttribute('name', values, isHidden),
value: () => textAttribute('value', values),
maxlength: () => numberAttribute('maxlength', values),
access: () => {
const rolesDisplay = values.role ? 'style="display:block"' : ''
const availableRoles = [`<div class="available-roles" ${rolesDisplay}>`]
for (key in opts.roles) {
if (opts.roles.hasOwnProperty(key)) {
const roleId = `fld-${data.lastID}-roles-${key}`
const cbAttrs = {
type: 'checkbox',
name: 'roles[]',
value: key,
id: roleId,
className: 'roles-field',
}
if (roles.includes(key)) {
cbAttrs.checked = 'checked'
}
availableRoles.push(`<label for="${roleId}">`)
availableRoles.push(h.input(cbAttrs).outerHTML)
availableRoles.push(` ${opts.roles[key]}</label>`)
}
}
availableRoles.push('</div>')
const accessLabels = {
first: mi18n.get('roles'),
second: mi18n.get('limitRole'),
content: availableRoles.join(''),
}
return boolAttribute('access', values, accessLabels)
},
other: () =>
boolAttribute('other', values, {
first: mi18n.get('enableOther'),
second: mi18n.get('enableOtherMsg'),
}),
options: () => fieldOptions(values),
requireValidOption: () =>
boolAttribute('requireValidOption', values, {
first: ' ',
second: mi18n.get('requireValidOption'),
}),
multiple: () => {
const typeLabels = {
default: {
first: 'Multiple',
second: 'set multiple attribute',
},
file: {
first: mi18n.get('multipleFiles'),
second: mi18n.get('allowMultipleFiles'),
},
select: {
first: ' ',
second: mi18n.get('selectionsMessage'),
},
}
return boolAttribute('multiple', values, typeLabels[type] || typeLabels.default)
},
//定义默认属性后。需要在这里添加对应属性的映射方法,这样才能生成对应的编辑框
// 由于src 是字符串类型的数据,所以依葫芦画瓢,粘贴一个textAttribute
// width和height同理
src: () => textAttribute('src', values),
width: () => numberAttribute('width', values),
height: () => numberAttribute('height', values),
}
let key
const roles = values.role !== undefined ? values.role.split(',') : []
const numAttrs = ['min', 'max', 'step']
numAttrs.forEach(numAttr => {
advFieldMap[numAttr] = () => numberAttribute(numAttr, values)
})
const noDisable = ['name', 'className']
Object.keys(fieldAttrs).forEach(index => {
const attr = fieldAttrs[index]
const useDefaultAttr = [true]
const isDisabled = opts.disabledAttrs.includes(attr)
if (opts.typeUserDisabledAttrs[type]) {
const typeDisabledAttrs = opts.typeUserDisabledAttrs[type]
useDefaultAttr.push(!typeDisabledAttrs.includes(attr))
}
if (opts.typeUserAttrs[type]) {
const userAttrs = Object.keys(opts.typeUserAttrs[type])
useDefaultAttr.push(!userAttrs.includes(attr))
}
if (isDisabled && !noDisable.includes(attr)) {
useDefaultAttr.push(false)
}
if (useDefaultAttr.every(Boolean)) {
advFields.push(advFieldMap[attr](isDisabled))
}
})
// Append custom attributes as defined in typeUserAttrs option
if (opts.typeUserAttrs[type]) {
const customAttr = processTypeUserAttrs(opts.typeUserAttrs[type], values)
advFields.push(customAttr)
}
return advFields.join('')
}
至此,你的自定义控件可以在界面上进行操作了