JQueryFormBuilder 自定义控件教程

348 阅读3分钟

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('')
  }

至此,你的自定义控件可以在界面上进行操作了
在这里插入图片描述


在这里插入图片描述