JqueryFormBuidler 自定义控件-详细版

本文介绍如何在JqueryFormBuilder中自定义图片控件,包括创建控件、配置属性、更新预览及获取数据的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JqueryFormBuidler 自定义控件

一、JqueryFormBuilder文件树

源码文件目录

├─demo
│  │  index.html
│  │  
│  ├─js
│  │      actionButtons.js
│  │      demo.js
│  │      
│  └─sass
│          demo.scss
│          
├─fonts
│      config.json
│      formbuilder-icons.woff
│      
├─js
│  │  config.js
│  │  control.js
│  │  controls.js
│  │  data.js
│  │  dom.js
│  │  events.js
│  │  form-builder.js
│  │  form-render.js
│  │  helpers.js
│  │  layout.js
│  │  README.md
│  │  test1.js
│  │  testData.txt
│  │  utils.js
│  │  
│  ├─control
│  │      autocomplete.js
│  │      button.js
│  │      controlFileList.js
│  │      custom.js
│  │      file.fineuploader.js
│  │      hidden.js
│  │      index.js
│  │      myImage.js
│  │      paragraph.js
│  │      README.md
│  │      select.js
│  │      text.js
│  │      textarea.js
│  │      textarea.quill.js
│  │      textarea.tinymce.js
│  │      
│  └─control_plugins
│          README.md
│          starRating.js
│          textarea.trumbowyg.js
│          
└─sass
    │  form-builder.scss
    │  form-render.scss
    │  _controls.scss
    │  _kc-toggle.scss
    │  _stage.scss
    │  
    └─base
            _animation.scss
            _bs.scss
            _font.scss
            _mixins.scss
            _variables.scss

如果需要实现自定义控件,需要了解以下文件

  • js/form-builder.js 设计器的源码,以及绑定的事件,基本都在该文件中
  • js/helpers.js 在用户编辑设计器上的控件的属性后会调用updatePreview方法更新预览的视图,所以可以在该方法中加入我们自己的业务逻辑生成想要的规则
  • js/utils.js 主要是markup方法,该方法可以帮我们生成页面上的html元素,源码中大量用到了该方法
  • 多语言文件,根据自己的语言环境进行设置。在demo/assets/lang文件夹下 。在设置自定义控件后,该自定义控件的名称,属性,都是自己定义的,如果不在多语言文件中提前定义,则在预览视图就看不到自定义控件的名称,属性名

二、自定义控件类型

jqueryFormBuilder中的所有控件都是继承自control类。所以我们在创建的自定义控件的时候也需要继承该类

以自定义控件 图片控件 为例

文件 js/control/controlImg.js

首先根据官方文档

  • 引入 control 文件
  • 定义一个继承自control的类,也可以继承control的子类
  • 定义一个静态的definition方法(这一步不是必须的)。如果你希望你的控件有一个自定义图标以及多语言映射,那么是必须的
  • 非必要步骤,定义一个configure方法,该方法可以指定外部的js以及css,如果你有另外的配置,同样可以在该方法中进行配置
  • 必要步骤,定义一个build方法,在该方法中可以使用this.config获取默认配置属性,以及自定义的配置属性。该方法需要返回一个Dom元素。可以使用utils文件中的markup方法
  • 非必要步骤,定义一个onRender方法,在Dom元素从build方法插入至界面时被调用,会接收一个event参数。
  • 调用control.register方法注册新的控件类型

参考代码

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
    configure() {

    }
    //define a `build` method - this is always required
    build() {
        const { values, value, placeholder, type, inline, other, toggle, ...data } = this.config
        name = this.config.multiple ? `${name}[]` : name
        const inputConfig = Object.assign({}, this.config, { name })

        if(typeof(inputConfig.src) == 'undefined' || inputConfig.src == ''){
            //设置一个默认的图片地址
            inputConfig.src = "https://gitee.com/justsawa/mypicture/raw/master/image/20201208195552.png"
        }
        
        if(inputConfig.width == ''){
            //设置默认的图片宽高
            inputConfig.width = 100
        }
        if(inputConfig.height == ''){
            //设置默认的图片宽高
            inputConfig.height = 100
        }
        //设置自定义样式
        inputConfig.class = 'my_img'
        //生成
        const htmlTpl = this.markup('img', null, inputConfig)
        return htmlTpl
    }

    onRender() {
    }
}
// register this control for the following types & text subtypes
control.register(['controlImg'], controlImg)

js/control/index.js文件中引入,这样才能在右边的控件列表显示出来

三、默认图片控件-事件触发,预览视图修改

在第二节中,参考官方文档,创建了一个自定义控件,但是,这样只是定义好了自定义控件的类型,在实际开发中,我们可能需要自定义的属性,事件去实现我们自己业务需求。

3.1 配置多语言文件

因为我们这个自定义控件的类型是新建的,并没有在多语言文件中进行设置,那么他在设计界面的控件列表中就显示不了label。在form-builder.js文件中的appendNewField方法中可以看到,label的取值是从多语言文件中取值的,

如果没有则显示不了名称,导致界面显示有问题,如图

JQueryFormBuilder的多语言文件默认使用的是英语。demo/assets/lang/en-US.lang

加入后

3.2 设置属性的编辑框

因为我们加了自定义的属性,src、height、width三个属性,但是这个时候点击编辑框是看不到这三个属性的编辑栏的。

因为JQueryFormBuilder在的设计器类,在向面板添加控件时,会根据控件类型(就是控件注册时写的名称)去寻找这个控件需要的属性然后根据设置为其分配指定的编辑框,最后返回对应的Dom数据。

代码路径 : js/form-builder.js

重点方法介绍

  • prepFieldVars 该方法为定义好的控件返回一个标准的设计器数据结构
  • appendNewField
    • 该方法为编辑器添加一个新的控件。
    • 控件会包含2部分
      • 一部分是预览部分,class名:prev-holder
      • 另一部分是属性编辑部分,class名:frm-holder
    • 参数 :
      • values 就是当前的控件,包含了当前的控件的一切属性以及数据
  • advFields 该方法为控件构建属性编辑框
    • 参数 :
      • values 就是当前的控件,包含了当前的控件的一切属性以及数据
  • defaultFieldAttrs 这个方法中,传入控件类型参数,返回对应的属性

在代码中可以看到,appendNewField 方法会返回一个Dom元素,该元素中包含了预览部分和编辑部分。

我们需要在编辑部分加入自定义的属性,让用户在设计表单时可以对其控制

defaultFieldAttrs 方法中为我们的控件类型,配置属性,因为图片类型是不带value属性的,所以在该方法中需要加上一个过滤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HYkKvOX-1607672640301)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201209112104786.png)]

然后advFields 方法就能找到这个属性。可以看到该方法中定义了一个advFieldMap的变量。该map的key就是属性名称,而value则是一个一个的箭头函数,返回对应的编辑框。所以,我们要在这个变量中,加入自己的代码才能生成对应的编辑框。

由于src,width,height三个属性都是一般的编辑框。我们可以使用源码中原本定义好的函数

image-20201209112711766

这个时候,再去页面上查看,编辑框就能显示出来了

image-20201209112822742

3.3 修改预览视图

至此,虽然实现了属性的编辑框,但是我们还没有实现编辑后,就能在预览视图中看到。

要实现这个效果,需要知道JQueryFormBuilder的预览视图的更新过程。

  1. 用户点击关闭按钮,或者edit按钮切换视图时,会触发事件。
  2. 事件中,会先获取当前元素的控件元素,其实也是一个Dom节点,包含了预览部分和编辑部分,然后将该元素作为参数传入Helper.js中的toggleEdit方法中
  3. 在toggleEdit方法中会调用Helper.js中的updatePreview方法。这个方法会更新预览视图,传入的参数也是当前的控件所在Dom元素
  4. 最后可以看到在该方法中调用了this.layout.build方法,这个方法调用的就是我们这个控件定义的时候的build方法。所以updatePreview只是更新了该控件的一些属性,真正的构建视图还是在build方法做的

我们需要在updatePreview中加入自己的业务逻辑,修改业务需要的属性值。

参考代码:

    if(fieldType.match('controlImg')){
      previewData.src = $(".fld-src", field).val();
      previewData.height = $(".fld-height", field).val();
      previewData.width = $(".fld-width", field).val();
    }

完整代码:

  updatePreview($field) {
    const _this = this
    const d = this.d
    const fieldClass = $field.attr('class')
    const field = $field[0]
    if (fieldClass.includes('input-control')) {
      return
    }

    console.log($field)
    const fieldType = $field.attr('type')
    const $prevHolder = $('.prev-holder', field)
    let previewData = Object.assign({}, _this.getAttrVals(field, previewData), { type: fieldType })

    if (fieldType.match(d.optionFieldsRegEx)) {
      previewData.values = []
      previewData.multiple = $('[name="multiple"]', field).is(':checked')

      $('.sortable-options li', field).each(function(i, $option) {
        const option = {
          selected: $('.option-selected', $option).is(':checked'),
          value: $('.option-value', $option).val(),
          label: $('.option-label', $option).val(),
        }
        previewData.values.push(option)
      })
    }

    if(fieldType.match('fileList')){
      previewData.values = []
      $('.myfile li', field).each(function(i, $option) {
          const file = {
             label: $(this).children(":first").text(),
             value: $(this).children(":first").attr("href")
          }
          previewData.values.push(file)
      })
    }
	
    //如果控件类型是controlImg 则修改其属性
    if(fieldType.match('controlImg')){
      previewData.src = $(".fld-src", field).val();
      previewData.height = $(".fld-height", field).val();
      previewData.width = $(".fld-width", field).val();
    }

    previewData = trimObj(previewData, true)

    previewData.className = _this.classNames(field, previewData)

    $field.data('fieldData', previewData)

    // determine the control class for this type, and then process it through the layout engine
    const custom = controlCustom.lookup(previewData.type)
    const controlClass = custom ? custom.class : control.getClass(previewData.type, previewData.subtype)
    const preview = this.layout.build(controlClass, previewData)

    empty($prevHolder[0])
    $prevHolder[0].appendChild(preview)
    preview.dispatchEvent(events.fieldRendered)
  }

现在对我们的图片属性编辑框进行编辑,就可以实现实时更新预览了

image-20201210110109132

image-20201210110031064

可以看到图片的宽高已经变成了200 * 200

四、默认图片控件-getData数据获取(自定义属性获取)

到前面几步实现了自定义控件的实时编辑更新,但是获取数据时,却没有办法获取到我们想要的数据

如图:

image-20201210110448552

可以看到并没有我们定义的属性返回,但是编辑之后,其实是有属性的。

如果想要在没有编辑的时候,或者在获取数据时有自己的业务逻辑,可以修改Helper.js中的prepData方法

参考代码

          //为controlImg增加指定属性
          if(fieldData.type == "controlImg"){
            fieldData.src = $(".fld-src", field).val();
            fieldData.height = $(".fld-height", field).val();
            fieldData.width = $(".fld-width", field).val();
          }

这个代码要放在

fieldData = trimObj(fieldData)

这个代码后面,因为trimObj方法中会对空值的属性进行剔除,

完整代码

  prepData(form) {
    const formData = []
    const d = this.d
    const _this = this

    if (form.childNodes.length !== 0) {
      // build data object
      forEach(form.childNodes, function(index, field) {
        const $field = $(field)

        if (!$field.hasClass('disabled-field')) {
          let fieldData = _this.getTypes($field)
          const $roleInputs = $('.roles-field:checked', field)
          const roleVals = $roleInputs.map(index => $roleInputs[index].value).get()

          fieldData = Object.assign({}, fieldData, _this.getAttrVals(field))

          if (fieldData.subtype) {
            if (fieldData.subtype === 'quill') {
              const id = `${fieldData.name}-preview`
              if (window.fbEditors.quill[id]) {
                const instance = window.fbEditors.quill[id].instance
                const data = instance.getContents()
                fieldData.value = window.JSON.stringify(data.ops)
              }
            } else if (fieldData.subtype === 'tinymce' && window.tinymce) {
              const id = `${fieldData.name}-preview`
              if (window.tinymce.editors[id]) {
                const editor = window.tinymce.editors[id]
                fieldData.value = editor.getContent()
              }
            }
          }

          if (roleVals.length) {
            fieldData.role = roleVals.join(',')
          }

          fieldData.className = fieldData.className || fieldData.class

          if (fieldData.className) {
            const match = /(?:^|\s)btn-(.*?)(?:\s|$)/g.exec(fieldData.className)
            if (match) {
              fieldData.style = match[1]
            }
          }

          fieldData = trimObj(fieldData)

          const multipleField = fieldData.type && fieldData.type.match(d.optionFieldsRegEx)

          if (multipleField) {
            fieldData.values = _this.fieldOptionData($field)
          }
          if(fieldData.type == "fileList"){
            fieldData.values = _this.fieldFileListData($field)
          }
          //为controlImg增加指定属性
          if(fieldData.type == "controlImg"){
            fieldData.src = $(".fld-src", field).val();
            fieldData.height = $(".fld-height", field).val();
            fieldData.width = $(".fld-width", field).val();
          }
          formData.push(fieldData)
        }
      })
    }

    return formData
  }

截图

image-20201210113028645

效果展示

image-20201210113102498

image-20201210113323347

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值