自定义字段组件如何处理vue组件内的表单校验

介绍

本示例以字符串字段为业务场景,将输入框用element-plus的组件实现了一遍,vue组件内在onMounted生命周期内将ElForm表单实例通过ts组件内提供到propssetFormInstance方法设置到了ts组件的属性formInstance上,这样就可以在ts组件校验方法validator()触发的时候直接调用表单组件实例formInstance的校验方法validate()

适用场景
  1. 当前字段存储了动态表单的配置json,vue组件内自行实现了一套表单渲染逻辑,需要在vue组件和ts组件内同时触发校验
参考文档
示例代码

ts组件

import {
  BaseFieldWidget,
  FormStringFieldSingleWidget,
  isValidatorSuccess,
  ModelFieldType,
  SPI,
  ValidatorInfo,
  ViewType,
  Widget
} from '@kunlun/dependencies';
import { FormInstance } from 'element-plus';
import MyFormStringField from './MyFormStringField.vue';

@SPI.ClassFactory(
  BaseFieldWidget.Token({
    viewType: [ViewType.Form, ViewType.Search],
    ttype: ModelFieldType.String,
    widget: 'Input',
    model: 'resource.k2.Model0000000109',
    name: 'code',
  })
)
export class MyFormStringFieldWidget extends FormStringFieldSingleWidget {

  public initialize(props) {
    super.initialize(props);
    this.setComponent(MyFormStringField);
    return this;
  }

  /**
   * ElementPlus的表单vue组件实例
   * @private
   */
  private formInstance?: FormInstance;

  @Widget.Method()
  private setFormInstance(formInstance: FormInstance | undefined) {
    this.formInstance = formInstance;
  }

  /**
   * 字段校验方法
   */
  public async validator(): Promise<ValidatorInfo> {
    const validRes = await this.formInstance?.validate((valid, fields) => {});
    console.log('validRes', validRes)
    if (!validRes) {
      return this.validatorError('校验失败');
    }

    const res = await super.validator();
    if (!isValidatorSuccess(res)) {
      return res;
    }
    if (this.value == null) {
      return this.validatorSuccess();
    }
    return this.validateLength(this.value);
  }

}

vue组件

<template>
  <ElForm ref="formInstance" :model="model" :rules="rules">
    <ElFormItem label="编码" prop="code">
      <ElInput v-model="model.code" @input="onValueChange"></ElInput>
    </ElFormItem>
  </ElForm>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, onMounted, watch } from 'vue';
import { ElForm, ElFormItem, ElInput, FormInstance } from 'element-plus';

export default defineComponent({
  name: 'MyFormStringField',
  components: {
    ElForm,
    ElFormItem,
    ElInput
  },
  props: ['value', 'setFormInstance', 'onChange'],
  setup(props) {
    const formInstance = ref<FormInstance>();
    const model = ref({ code: '' });

    const rules = reactive({
      code: [
        { required: true, message: '必填', trigger: 'blur' },
      ]
    });

    function onValueChange() {
      props.onChange?.(model.value.code);
    }

    onMounted(() => {
      props.setFormInstance?.(formInstance.value);
    });

    watch(props.value, () => {
      model.value.code = props.value;
    });

    return {
      formInstance,
      model,
      rules,
      onValueChange
    }
  }
});
</script>

页面校验效果查看
自定义字段组件如何处理vue组件内的表单校验

Oinone社区 作者:nation原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/17094.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
nation的头像nation数式员工
上一篇 2024年9月6日 am11:38
下一篇 2024年9月6日 pm8:36

相关推荐

  • 多对多的表格 点击添加按钮打开一个表单弹窗

    多对多的表格 点击添加按钮打开一个表单弹窗 默认情况下,多对多的表格上方的添加按钮点击后,打开的是个表格 ,如果您期望点击添加按钮打开的是个表单页面,那么可以按照下方的操作来 1: 先从界面设计器拖一个多对多的字段进来 2: 将该字段切换成表格,并拖入一些字段到表格上 3: 选中添加按钮,将其隐藏 4: 从组件区域的动作分组中拖一个跳转动作,并且进行如下的配置 5: 属性填写好后进行保存,然后在设计弹窗 6: 拖入对应的字段到弹窗中, 当弹窗界面设计完成后,再把保存的按钮拖入进来 这样多对多的添加弹窗就变成了表单

    前端 2023年11月1日
    2.8K00
  • 前端表格复制

    我们可能会遇到表格复制的需求,也就是表格填写的时候,不是增加一行数据,而是增加一个表格。以下是代码实现和原理分析。 代码实现 在 boot 工程的 main.ts 中加入以下代码 import { registerDesignerFieldWidgetCreator, selectorDesignerFieldWidgetCreator } from '@oinone/kunlun-ui-designer-dependencies'; // 注册无代码组件,将表头分组的无代码组件,注册成字段表格组件 registerDesignerFieldWidgetCreator( { widget: 'DynamicCreateTable' }, selectorDesignerFieldWidgetCreator({ widget: TABLE_WIDGET })! ); DynamicCreateTableWidget 动态添加表格 ts 组件 import { FormO2MTableFieldWidget, Widget, DslDefinition, RuntimeView, SubmitValue, BaseFieldWidget, ModelFieldType, SPI, ViewType, ActiveRecord, uniqueKeyGenerator } from '@oinone/kunlun-dependencies'; import { MyMetadataViewWidget } from './MyMetadataViewWidget'; import DynamicCreateTable from './DynamicCreateTable.vue'; @SPI.ClassFactory( BaseFieldWidget.Token({ viewType: ViewType.Form, ttype: ModelFieldType.OneToMany, widget: 'DynamicCreateTable' }) ) export class DynamicCreateTableWidget extends FormO2MTableFieldWidget { public myMetadataViewWidget: MyMetadataViewWidget[] = []; @Widget.Reactive() public myMetadataViewWidgetLength = 0; @Widget.Reactive() public myMetadataViewWidgetKeys: string[] = []; protected props: Record<string, unknown> = {}; public initialize(props) { super.initialize(props); this.props = props; this.setComponent(DynamicCreateTable); return this; } // region 创建动态表格 @Widget.Method() public async createTableWidget(record: ActiveRecord) { const index = this.myMetadataViewWidget.length; const handle = uniqueKeyGenerator(); const slotKey = `MyMetadataViewWidget_${handle}`; const widget = this.createWidget( new MyMetadataViewWidget(handle), slotKey, // 插槽名称 { subIndex: index, metadataHandle: this.metadataHandle, rootHandle: this.rootHandle, automatic: true, internal: true, inline: true } ); this.initDynamicSubview(this.props, widget); widget.setData(record); this.myMetadataViewWidgetLength++; this.myMetadataViewWidgetKeys.push(slotKey); this.myMetadataViewWidget.push(widget); } protected initDynamicSubview(props: Record<string, unknown>, widget: MyMetadataViewWidget) { const { currentViewDsl } = this; let viewDsl = currentViewDsl; if (!viewDsl) { viewDsl = this.getViewDsl(props)…

    2025年7月21日
    73900
  • 【界面设计器】自定义字段组件实战——千分位输入框

    阅读之前 此文章为实战教程,已假定你熟悉了【界面设计器】较为完整的【自定义组件】相关内容。 如果在阅读过程中出现的部分概念无法理解,请自行学习相关内容。【前端】文章目录 业务背景 用户在输入【金额】字段时,实时展示千分位格式。 业务分析 从需求来看,我们需要实现一个【千分位】组件,并且该组件允许在【表单】视图中使用。 扩展实现:该组件虽然仅要求在【表单】中使用,但也可以在【搜索】中使用完全相同的实现,因此这里我们在注册时会增加【搜索】视图,将【千分位】组件应用在【搜索】中。对于【表格】、【详情】和【画廊】来说,该组件是没有【输入】行为的展示组件,在这里我们不进行演示。 准备工作 此处你应该已经在某个业务模型下,可以完整执行当前模型的全部【增删改查】操作。 业务模型定义 (以下仅展示本文章用到的模型字段,忽略其他无关字段。) 名称 API名称 业务类型 是否多值 长度(单值长度) 编码 code 文本 否 128 名称 name 文本 否 128 金额 money 金额 否 – 创建组件、元件 准备工作完成后,我们需要根据【业务背景】确定【组件】以及【元件】相关信息,并在【界面设计器】中进行创建。 以下操作过程将省略详细步骤,仅展示可能需要确认的关键页面。 创建千分位组件 创建千分位元件 启动SDK工程进行组件基本功能开发 (npm相关操作请自行查看SDK工程中内置的README.MD) 关键点详解 数据交互类型的字段组件(以下简称展示组件)与仅展示类型的字段组件(以下简称交互组件)有一些差别。 通常情况下,在展示组件中仅需使用value属性即可展示相关内容。在交互组件中除了value用于展示外,还需使用change、focus以及blur处理用户输入时的基本交互逻辑。 数据交互方法主要功能说明: change方法:当值发生变更时调用,根据字段相关配置将值回填至表单中。 focus方法:当组件获取焦点时调用,记录当前值,并在调用blur方法时进行处理。 blur方法:当前组件失去焦点时调用,根据focus方法记录的值,进行失焦触发逻辑的执行。 这里的三个数据交互方法仅仅是对用户行为的抽象,而并非限定其使用场景。 通常来说,这三个方法的调用顺序为:focus –> change –> blur。 如在日期时间组件中,面板打开时调用了focus方法,面板值发生变更时,调用了change方法,面板关闭时调用了blur方法。 如在地图组件中,选中地图上的某个点时仅会调用change方法,用户交互上并不能体现出focus和blur的行为。因此对于这个组件而言,只会有change方法被执行。 代码实现参考 PS:oio-input-number样式必须升级到4.6.x以上的最新版本才支持 Thousandth.vue <template> <a-input-number class="oio-input-number" :value="inputValue" :formatter="formatter" :parser="parser" @update:value="change" @focus="focus" @blur="blur" /> </template> <script lang="ts"> import { InputNumber as AInputNumber } from 'ant-design-vue'; import { computed, defineComponent } from 'vue'; export default defineComponent({ name: 'Thousandth', components: { AInputNumber }, props: { value: { type: [String, Number] }, change: { type: Function }, focus: { type: Function }, blur: { type: Function } }, setup(props) { const inputValue = computed(() => { return props.value; }); const formatter = (value) => { return `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ','); }; const parser = (value) => { return value.replace(/\$\s?|(,*)/g, ''); }; return { inputValue, formatter, parser }; } }); </script> FormMoneyThousandthFieldWidget.ts import { FormFieldWidget, ModelFieldType, SPI, ViewType } from '@kunlun/dependencies'; import Thousandth from './Thousandth.vue'; @SPI.ClassFactory( FormFieldWidget.Token({ viewType: [ViewType.Form, ViewType.Search], ttype: ModelFieldType.Currency, widget:…

    2024年4月19日
    1.4K00
  • 移动端端默认布局模板

    默认布局 表格视图(TABLE) <view type="TABLE"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field"> <xslot name="searchFields" slotSupport="field" /> </element> </view> <pack widget="group" class="oio-m-default-view-element" style="height: 100%;flex: 1;overflow-y: hidden;" wrapperStyle="height: 100%;box-sizing:border-box;"> <pack widget="row" style="height: 100%;"> <pack widget="col" mode="full" style="min-height: 234px;height: 100%;"> <element widget="table" slot="table" slotSupport="field" checkbox="false"> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" /> </element> </pack> </pack> </pack> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 表单视图(FORM) <view type="FORM"> <element widget="form" slot="form"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 详情视图(DETAIL) <view type="DETAIL"> <element widget="detail" slot="detail"> <xslot name="fields" slotSupport="pack,field" /> </element> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> </view> 画廊视图(GALLERY) 默认内嵌布局(inline=true) 内嵌表格视图(TABLE) 内嵌表单视图(FORM) 内嵌详情视图(DETAIL) <view type="DETAIL"> <element widget="detail" slot="detail"> <xslot name="fields" slotSupport="pack,field" /> </element> </view>`

    2024年12月11日
    2.6K00
  • 表单页如何在服务端动作点击后让整个表单都处于loading状态

    介绍 在业务场景中,有时候由于提交的数据很多,导致服务端动作耗时较长,为了保证这个过程中表单内的字段不再能被编辑,我们可以通过自定义能力将整个表单区域处于loading状态 自定义动作组件代码 import { ActionType, ActionWidget, BaseElementViewWidget, BaseView, ClickResult, ServerActionWidget, SPI, Widget } from '@kunlun/dependencies'; @SPI.ClassFactory(ActionWidget.Token({ actionType: ActionType.Server })) class LoadingServerActionWidget extends ServerActionWidget { protected async clickAction(): Promise<ClickResult> { const baseView = Widget.select(this.rootHandle) as unknown as BaseView; if (!baseView) { return super.clickAction(); } const baseViewWidget = baseView.getChildrenInstance().find((a) => a instanceof BaseElementViewWidget) as unknown as BaseElementViewWidget; if (!baseViewWidget) { return super.clickAction(); } return new Promise((resolve, reject) => { try { baseViewWidget.load(async () => { const res = await super.clickAction(); resolve(res); }); } catch (e) { reject(false); } }); } } 本案例知识点 BaseElementWidget提供了load方法将继承了该class的元素渲染的区域做整体loading交互,等入参的函数处理完成后恢复正常状态,其实所有继承了ActionWidget的组件也提供了这个能力让按钮在执行函数中的时候处于loading状态, 每个组件都有一个全局唯一的handle值,所在根视图的rootHandle,组件可以用this.rootHandle通过Widget.Select方法查找到所在的根视图组件,从视图的实例化子元素里可以查找到具体的业务类型视图组件,如详情页的DetailWidget、表单页的FormWidget、表格页的TableWidget,拿到这些实例后就可以操作里面的属性和方法了

    2024年5月29日
    1.5K00

Leave a Reply

登录后才能评论