Form 表单
介绍
用于数据录入、校验,支持输入框、单选框、复选框、文件上传等类型,需要与 Field 输入框 组件搭配使用。
引入
ts
import { IBestForm, IBestFormRule, IBestField, IBestFormController } from "@ibestservices/ibest-ui";
代码演示
基础用法
TIP
通过 formId
属性绑定表单与表单项的关系, rules
属性可配置表单校验规则,绑定 controller
属性可获取表单实例。
• formId
controller
props
属性为必填项,未设置 formId
prop
的表单项将不触发表单验证。
点我查看代码
ts
import { IBestCellGroup, IBestButton } from "@ibestservices/ibest-ui"
@Entry
@Component
struct DemoPage {
@State name: string = ""
@State phone: string = ""
private formId: string = 'form'
private controller: IBestFormController = new IBestFormController()
build() {
Column(){
IBestForm({
formId: this.formId,
controller: this.controller
}){
IBestCellGroup({inset: true}) {
IBestField({
formId: this.formId,
prop: 'name',
value: $name,
label: "姓名",
placeholder: "请输入姓名",
rules:[
{ required: true, message: '请输入姓名' },
{ min: 3, max: 10, message: '姓名长度在3-10个字符之间' }
]
})
IBestField({
formId: this.formId,
prop: 'phone',
value: $phone,
label: "手机号",
placeholder: "请输入手机号",
hasBorder: false,
rules: [
{ required: true, message: '请输入手机号' }
]
})
IBestButton({
text: "验证姓名",
type: 'primary',
buttonSize: 'large',
onClickBtn: () => {
this.controller.validateField("name").then(res => {
if(res.valid){
IBestToast.show("验证成功")
} else {
IBestToast.show({
message: `${res.field.label}验证失败`
})
}
})
}
})
IBestButton({
text: "提交",
type: 'primary',
buttonSize: 'large',
onClickBtn: () => {
this.controller.validate().then(res => {
if(res.valid){
IBestToast.show("验证成功")
} else {
let labels: string[] = res.fields.map(item => item.label)
IBestToast.show({
message: `${labels.join(',')}验证失败`
})
}
})
}
})
.margin({ top: 20 })
}
}
}
}
}
校验规则
TIP
通过 rules
定义表单校验规则,所有可用字段见下方表格
点我查看代码
ts
import { IBestCellGroup, IBestButton, IBestToast } from "@ibestservices/ibest-ui"
@Entry
@Component
struct DemoPage {
@State value1: string = ""
@State value2: string = ""
@State value3: string = ""
@State value4: string = ""
private formId: string = 'form'
// 正则校验
private pattern = /\d{6}/
// 校验函数返回 true 表示校验通过,false 表示不通过
private validator = (val: string) => /1\d{10}/.test(val)
// 校验函数可以直接返回一段错误提示
private validatorMessage = (val: string) => `${val} 不合法,请重新输入`
// 校验函数可以返回 Promise,实现异步校验
private asyncValidator(val: string): Promise<boolean> {
return new Promise((resolve) => {
IBestToast.show({
type: "loading",
message: '校验中...'
})
setTimeout(() => {
IBestToast.hide()
resolve(val === '1234')
}, 1000)
})
}
private rules: IBestFormRule = {
"value1": [
{ pattern: this.pattern, message: "请输入正确的内容" }
],
"value2": [
{ validator: this.validator, message: "请输入正确内容" }
],
"value3": [
{ validator: this.validatorMessage }
],
"value4": [
{ validator: this.asyncValidator, message: "请输入正确内容" }
]
}
private controller: IBestFormController = new IBestFormController()
build() {
Column(){
IBestForm({
formId: this.formId,
rules: this.rules,
controller: this.controller
}){
IBestCellGroup({inset: true}) {
IBestField({
formId: this.formId,
prop: 'value1',
value: $value1,
label: "正则校验",
placeholder: "正则校验"
})
IBestField({
formId: this.formId,
prop: 'value2',
value: $value2,
label: "函数校验",
placeholder: "返回true/false"
})
IBestField({
formId: this.formId,
prop: 'value3',
value: $value3,
label: "函数校验",
placeholder: "返回验证信息"
})
IBestField({
formId: this.formId,
prop: 'value4',
value: $value4,
label: "异步校验",
placeholder: "异步函数校验",
hasBorder: false
})
IBestButton({
text: "提交",
type: 'primary',
buttonSize: 'large',
onClickBtn: () => {
this.controller.validate((valid, fields) => {
if (valid) {
console.log("验证成功")
}
})
}
})
.margin({ top: 20 })
}
}
}
}
}
自定义表单项类型
点我查看代码
ts
import {
IBestButton,
IBestCalendarDialog,
IBestCascader,
IBestCascaderOption,
IBestCellGroup,
IBestCheckbox,
IBestCheckboxGroup,
IBestRadio,
IBestRadioGroup,
IBestStepper,
IBestSwitch,
IBestToast,
IBestUploaderFile,
IBestUploader
} from "@ibestservices/ibest-ui"
@Entry
@Component
struct DemoPage {
@State value1: boolean = false
@State value2: boolean = false
@State value3: string[] = []
@State value4: string = ""
@State value5: number = 1
@State value6: string = ""
@State selectValue: string[] = []
@State value7: string = ''
@State visible: boolean = false
@State visible1: boolean = false
@State value8: IBestUploaderFile[] = []
private formId: string = 'form'
private rules: IBestFormRule = {
"value3": [
{ required: true, message: '请选择至少一项' }
],
"value4": [
{ required: true, message: "请选择" }
],
"value6": [
{ required: true, message: "请选择城市" }
],
"value7": [
{ required: true, message: "请选择日期" }
],
"value12": [
{ required: true, message: "请上传图片" }
]
}
private controller: IBestFormController = new IBestFormController()
@State options: IBestCascaderOption[] = [
{
text: "江苏省",
value: "320000",
children: [
{
text: "南京市",
value: "320100",
children: [
{
text: "秦淮区",
value: "320104"
},
{
text: "雨花台区",
value: "320114"
}
]
},
{
text: "苏州市",
value: "320500",
children: [
{
text: "姑苏区",
value: "320508"
},
{
text: "昆山市",
value: "320583"
}
]
}
]
},
{
text: "安徽省",
value: "340000",
children: [
{
text: "合肥市",
value: "340100",
children: [
{
text: "蜀山区",
value: "340104"
},
{
text: "合肥高新技术产业开发区",
value: "340171"
}
]
},
{
text: "黄山市",
value: "341000",
children: [
{
text: "屯溪区",
value: "341002"
},
{
text: "黄山区",
value: "341003"
}
]
}
]
}
]
@Builder switchContent() {
IBestSwitch({
value: $value1
})
}
@Builder checkboxContent() {
IBestCheckbox({
value: this.value2,
shape: "square",
name: "value2",
onChange: value => {
this.value2 = value
}
})
}
@Builder checkboxGroupContent() {
IBestCheckboxGroup({
group: "group1",
activeList: $value3,
placeDirection: Axis.Horizontal
}){
IBestCheckbox({
group: "group1",
shape: "square",
label: "复选框1",
name: "1"
})
IBestCheckbox({
group: "group1",
shape: "square",
label: "复选框2",
name: "2"
})
}
}
@Builder radioContent() {
IBestRadioGroup({
active: $value4,
group: "group1",
placeDirection: Axis.Horizontal
}){
IBestRadio({
group: "group1",
label: "单选框1",
name: "1"
})
IBestRadio({
group: "group1",
label: "单选框2",
name: "2"
})
}
}
@Builder stepperContent(){
IBestStepper({
value: $value5,
min: 1,
max: 99,
step: 1
})
}
@Builder uploadImg(){
IBestUploader({
fileList: $value12,
max: 2
})
}
build() {
Column(){
IBestForm({
formId: this.formId,
rules: this.rules,
controller: this.controller
}){
IBestCellGroup({inset: true}) {
IBestField({
formId: this.formId,
prop: 'value1',
value: $value1,
label: "开关",
customRightContent: (): void => this.switchContent()
})
IBestField({
formId: this.formId,
prop: 'value2',
value: $value2,
label: "复选框",
rules: this.value2 ? [{required: true, message: '请选择'}] : [],
customRightContent: (): void => this.checkboxContent()
})
IBestField({
formId: this.formId,
prop: 'value3',
value: $value3,
label: "复选框组",
customRightContent: (): void => this.checkboxGroupContent()
})
IBestField({
formId: this.formId3,
prop: 'value4',
value: $value4,
label: "单选框",
customRightContent: (): void => this.radioContent()
})
IBestField({
formId: this.formId,
prop: 'value5',
value: $value5,
label: "步进器",
customRightContent: (): void => this.stepperContent()
})
IBestField({
formId: this.formId,
prop: 'value6',
value: this.value6,
label: "选择城市",
placeholder: "请选择城市",
isLink: true,
onFieldClick: () => {
this.visible = true
}
})
IBestField({
formId: this.formId,
prop: 'value7',
value: this.value7,
label: "日历",
placeholder: "请选择日期",
isLink: true,
onFieldClick: () => {
this.visible1 = true
}
})
IBestField({
formId: this.formId,
prop: 'value8',
value: $value8,
label: "上传图片",
hasBorder: false,
customRightContent: (): void => this.uploadImg()
})
IBestButton({
text: "提交",
type: 'primary',
buttonSize: 'large',
onClickBtn: () => {
this.controller.validate((valid, fields) => {
if (valid) {
IBestToast.show("验证成功")
}
})
}
})
.margin({ top: 20 })
}
}
// 选择城市
IBestCascader({
visible: this.visible,
options: this.options,
value: $selectValue,
onConfirm: value => {
this.value6 = value.map(item => item.text).join(',')
}
})
// 日历
IBestCalendarDialog({
visible: $visible1,
onConfirm: value => {
this.value7 = value[0].dateStr
}
})
}
}
}
动态表单校验
TIP
• 将需要遍历的表单项封装为单个组件, 在其内部处理每项表单数据;
• 需注意, ForEach的key值需要设置为唯一值(事例中为生成的随机id);
• 组件内部需在aboutToAppear中初始化表单项的值, 否则动态删除时, 表单数据会丢失;
• 组价内部需监听每个变量的变化, 并将新值赋值给表单项(事例中为item);
点我查看代码
ts
import {
IBestButton,
IBestCellGroup,
IBestField,
IBestForm,
IBestFormController,
IBestFormRule,
IBestPicker,
IBestPickerOption,
IBestPopup,
IBestRadio,
IBestRadioGroup,
IBestToast
} from '@ibestservices/ibest-ui'
@Observed
class Subject {
id: string = ""
type: string = ""
name: string = ""
score: number = 0
constructor() {
this.id = Math.random().toString(36).substring(2, 9)
}
}
@Entry
@Component
struct DemoPage {
private formId: string = 'form'
@State sex: string = ""
@State info1: string = ""
@State info2: string = ""
@State list: Subject[] = []
private rules: IBestFormRule = {
"sex": [
{ required: true, message: "请选择是否" }
],
"info1": [
{ required: true, message: "请输入男生信息" }
],
"info2": [
{ required: true, message: "请输入女生信息" }
],
"type": [
{ required: true, message: "请选择学科类型" }
],
"score": [
{ required: true, message: "请输入分数" }
]
}
private controller: IBestFormController = new IBestFormController()
@Builder radioContent() {
IBestRadioGroup({
active: $sex,
group: "group",
placeDirection: Axis.Horizontal
}){
IBestRadio({
group: "group",
label: "男",
name: "1"
})
IBestRadio({
group: "group",
label: "女",
name: "2"
})
}
}
deleteContent(index: number){
this.list.splice(index, 1)
}
build() {
Column(){
IBestForm({
formId: this.formId,
rules: this.rules,
controller: this.controller
}){
IBestCellGroup({hasBorder: false}){
IBestField({
formId: this.formId,
prop: 'sex',
value: $sex,
label: "性别",
hasBorder: this.sex != '',
customRightContent: (): void => this.radioContent()
})
if(this.sex == '1'){
IBestField({
formId: this.formId,
prop: 'info1',
value: $info1,
label: "男生信息",
placeholder: "请输入男生信息",
hasBorder: false
})
}else if(this.sex == '2'){
IBestField({
formId: this.formId,
prop: 'info2',
value: $info2,
label: "女生信息",
placeholder: "请输入女生信息",
hasBorder: false
})
}
ForEach(this.list, (item: Subject, index) => {
subjectItem({
item: item,
index: index,
formId: this.formId,
delete: (): void => this.deleteContent(index)
})
}, (item: Subject) => item.id)
}
IBestButton({
text: "添加内容",
type: "primary",
buttonSize: 'large',
onClickBtn: () => {
this.list.push(new Subject())
}
})
IBestButton({
text: "提交",
type: 'primary',
buttonSize: 'large',
onClickBtn: () => {
this.controller.validate((valid) => {
if (valid) {
IBestToast.show("验证成功")
}
})
}
})
}
}
}
}
@Component
struct subjectItem{
@ObjectLink item: Subject
@Prop index: number
@Prop formId: string
@State @Watch("typeChange") type: string = ""
@State @Watch("nameChange") name: string = ""
@State visible: boolean = false
@State @Watch("scoreChange") score: string = ""
private options: IBestPickerOption[] = [
{ text: '语文', value: '1' },
{ text: '数学', value: '2' },
{ text: '英语', value: '3' },
{ text: '物理', value: '4' },
{ text: '化学', value: '5' },
{ text: '生物', value: '6' }
]
delete: (index: number) => void = () => {}
@Builder pickerBuilder(){
IBestPicker({
options: this.options,
title: "请选择学科",
visibleItemCount: 5,
value: $type,
onConfirm: (selectedValues: string[], selectTexts: string[]) => {
this.visible = false
this.name = selectTexts.join('-')
},
onCancel: () => {
this.visible = false
}
})
}
aboutToAppear(): void {
let item = this.item
if(item.type){
this.type = item.type
}
if(item.name){
this.name = item.name
}
if(item.score){
this.score = item.score.toString()
}
}
typeChange(){
this.item.type = this.type
}
nameChange(){
this.item.name = this.name
}
scoreChange(){
this.item.score = Number(this.score)
}
build() {
IBestCellGroup({title: `学科${this.index+1}`, hasBorder: false}){
Text("删除")
.fontColor("#969799")
.fontSize(14)
.position({right: 0, top: 17})
.onClick(() => {
this.delete(this.index)
})
IBestField({
formId: this.formId,
prop: `type.${this.index}`,
label: '学科',
value: this.name,
placeholder: "请选择学科",
isLink: true,
onFieldClick: () => {
this.visible = true
}
})
IBestField({
formId: this.formId,
prop: `score.${this.index}`,
label: "分数",
value: $score,
type: "number",
placeholder: "请输入分数",
hasBorder: false
})
IBestPopup({
visible: $visible,
popupAlign: "bottom",
contentBuilder: (): void => this.pickerBuilder()
})
}
}
}
API
@Props
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
formId | 表单id, 必传, 需保证全局唯一性 | string | '' |
rules | 表单验证信息 | Record<string, IBestFormRuleItem[]> | {} |
space | 表单子项间距 | number | string | 10 |
labelWidth | 表单项左侧文本区域宽度 | number | string | 80 |
labelPosition | 左侧文本位置, 可选值 left top | string | left |
labelAlign | 左侧文本对齐方式, 可选值 left center right | string | left |
colon | 是否在label后加冒号 | boolean | false |
requireAsteriskPosition | 星号的位置, 可选值 left right | string | left |
showMessage | 是否显示验证信息 | boolean | true |
disabled | 是否禁用 | boolean | false |
controller | 表单实例控制器 | IBestFormController | null |
插槽
插槽名 | 说明 | 类型 |
---|---|---|
defaultBuilder | 表单子项 | CustomBuilder |
IBestFormController API
方法名 | 说明 | 参数 | 返回值 |
---|---|---|---|
validate | 验证整个表单 | callback?: (valid: boolean, field: FieldValidateResult[]) => void | Promise<IBestFormValidateResult> |
validateField | 验证指定表单 | prop: string, callBack?: (valid: boolean, field?: FieldValidateResult) => void | Promise<IBestFieldValidateResult> |
resetValidation | 重置整个/指定表单验证信息 | prop?: string | string[] | void |
getFormValues | 获取表单所有表单项的值 | - | Record<string, IBestFieldValueType> |
IBestFormRuleItem 数据结构
参数 | 说明 | 类型 |
---|---|---|
required | 是否为必选字段,当值为空值时(空字符串、空数组、false、undefined、null ),校验不通过 | boolean |
message | 验证错误提示信息 | string |
pattern | 正则表达式 | RegExp |
validator | 自定义校验函数 | (value: string) => boolean | string | Promise<boolean | string> |
trigger | 验证触发时机, 默认都会触发 | 'blur' | 'change' |
min | 字符最小长度 | number |
max | 字符最大长度 | number |