【Taro开发】-封装Form表单组件和表单检验(七)

Taro小程序开发

系列文章的所有文章的目录

【Taro开发】-初始化项目(一)

【Taro开发】-路由传参及页面事件调用(二)

【Taro开发】-taro-ui(三)

【Taro开发】-带token网络请求封装(四)

【Taro开发】-自定义导航栏NavBar(五)

【Taro开发】-formData图片上传组件(六)

【Taro开发】-封装Form表单组件和表单检验(七)

【Taro开发】-tabs标签页及子组件的下拉刷新(八)

【Taro开发】-简易的checkBoxGroup组件(九)

【Taro开发】-页面生成二维码及保存到本地(十)

【Taro开发】-宣传海报,实现canvas实现圆角画布/图片拼接二维码并保存(十一)

【Taro开发】-分享给好友/朋友圈(十二)

【Taro开发】-小程序自动打包上传并生成预览二维码(十三)

【Taro开发】-全局自定义导航栏适配消息通知框位置及其他问题(十四)



前言

基于Taro的微信小程序开发,主要组件库为Taro-ui。
formItem组件需满足前后缀,是否必填,校验提示。
form组件需能在submit时返回是否全部通过校验。
由于taro-ui组件库无相关组件/不满足需求,因此进行封装重写。


表单校验实现效果
提示:以下是本篇文章正文内容,下面案例可供参考

1、异步校验机制

//utils/util
import Schema from 'async-validator';

export const onValidateField = (value, rules) => {
  const validator = new Schema(rules);
  return new Promise((resolve) => {
    validator.validate(value, (errors, fields) => {
      if (errors) {
        resolve(errors);
      } else {
        const errMsg = Object.keys(rules).map((key) => {
          return {
            field: key,
            message: undefined,
          };
        });
        resolve(errMsg);
      }
    });
  });
};

2、 FormItem组件

/* eslint-disable no-undef */
import React, { Component } from "react";
import { View, Text, Switch, Form, Button } from "@tarojs/components";
import Taro, { getCurrentInstance } from "@tarojs/taro";
import { AtIcon } from "taro-ui";
import "./index.scss";
import { onValidateField } from "@/utils/util";

class FormItem extends Component {
  constructor() {
    super();
    this.state = {
      errMsg: []
    };
  }
  check = async (value, rule) => {
    const errors = await onValidateField(value, rule);
    this.setState({ errMsg: errors });
  };
  renderChildren(props) {
    //遍历所有子组件
    return React.Children.map(props.children, child => {
      if (!props.children?.length)
        return React.cloneElement(child, {
          //把父组件的props.name赋值给每个子组件
          name: props.name,
          onChange: val => {
            if (val !== child?.props.value) {
              let checkName = props.name;
              props.rules &&
                this.check({ [checkName]: val }, { [checkName]: props.rules });
              child.props.onChange && child.props.onChange(val);
            }
          }
        });
      else return child;
    });
  }
  checkRules(rules) {
    // 检查为必填项
    if (Array.isArray(rules)) {
      for (let i = 0; i < rules.length; i++) {
        if (rules[i].required) {
          return true;
        }
      }
    } else {
      return false;
    }
  }
  render() {
    const { errMsg } = this.state;
    const {
      prefix,
      suffix,
      title,
      enter,
      enterCallBack,
      hasLine,
      rules
    } = this.props;
    return (
      <View className="trace-rowAlignCenter form">
        {prefix}
        <Text className={this.checkRules(rules) ? "label necessary" : "label"}>
          {title}
        </Text>
        <View
          className={
            hasLine ? "trace-rowAlignCenter hasline" : "trace-rowAlignCenter"
          }
          style={{ flex: 1 }}
          onClick={() => enterCallBack && enterCallBack()}
        >
          <View style={{ flex: 1 }}>{this.renderChildren(this.props)}</View>
          {suffix ||
            (enter ? (
              <AtIcon size={24)} value="chevron-right" color="#999" />
            ) : (
              <AtIcon
                className="perch"
                size={24}
                value="chevron-right"
                color="#999"
              />
            ))}
        </View>
        {errMsg?.length > 0 && errMsg[0].message && (
          <Text className="formTip">{errMsg[0].message}</Text>
        )}
      </View>
    );
  }
}
export default FormItem;

@import "@/styles/variables.scss";

.form {
  position: relative;
  display: flex;
  align-items: center;
  padding-left: 32px;
  height: 112px;
  background-color: $color-white;

  .at-icon {
    // 前缀图标
    margin-right: 24px;
  }

  .necessary {
    // 是否显示红*
    &:after {
      content: "*";
      color: $color-red;
    }
  }

  .label {
    // 标题名称
    font-size: 32px;
    color: $color-black;
  }

  .formTip {
    // 提示红字
    color: $color-red;
    position: absolute;
    right: 66px;
    bottom: 5px;
  }

  .hasline {
    border-bottom: 1px solid #f0f0f0;
  }

  .trace-rowAlignCenter {
    height: 100%;

    .at-input {
      background-color: $color-white;

      &:after {
        display: none;
      }

      .at-input__input {
        // placeholder
        font-size: 32px;
        text-align: right;
        color: $color-text-b3;
        font-weight: 400;
        padding-right: 8px;
      }
    }

    .at-icon {
      // 后缀图标
      margin-right: 14px;

      &:before {
        color: $color-text-9;
      }
    }

    .perch {
      // 占位后缀图标
      opacity: 0;
    }
  }
}

3、Form组件

/* eslint-disable no-undef */
import { Component, createRef } from "react";
import { View, Text, Switch, Form, Button } from "@tarojs/components";

class FormCom extends Component {
  el = createRef();
  constructor() {
    super();
  }

  isVerifyPass(element) {
    let res = true;
    if (element) {
      element?.map(formItem => {
        if (formItem) {
          formItem?.childNodes.map(child => {
            if (child?.props?.class === "formTip") {
              res = false;
            }
          });
        }
      });
    }
    return res;
  }

  formSubmit = e => {
    const { onSubmit } = this.props;
    onSubmit && onSubmit(e, this.isVerifyPass(this.el.current?.childNodes));
  };

  render() {
    return (
      <Form ref={this.el} {...this.props} onSubmit={this.formSubmit}>
        {this.props.children}
      </Form>
    );
  }
}
export default FormCom;

4、校验规则

//utils/rules.js
export function rulePhone(rule, value) {//手机号码
  if (!value) return true
  if (/^1[3-9]\d{9}$/.test(value)) return true
  return false;
}
...

5、使用

  formSubmit = (e, isPass) => {
    console.log("是否通过:", e, isPass);
  };

//render
		<FormCom
          onSubmit={(e, isPass) => this.formSubmit(e, isPass)}
        >
          <FormItem
            name="add"
            title="手机号码"
            prefix={<AtIcon size={10} value="add" />}
            // suffix={<AtIcon size={20} value="chevron-right" />}
            enter
            rules={[
              { required: true, message: "请输入手机号" },
              {
                validator: (rule, value) => rulePhone(rule, value),
                message: "请输入正确的手机号"
              }
            ]}
          >
            <AtInput
              value={this.state.form.phone}
              onChange={val => {
                this.setState({
                  form: { ...this.state.form, phone: val }
                });
              }}
              placeholder="我是默认提示"
            />
          </FormItem>
          <AtButton
            className="trace-btn-default"
            type="primary"
            circle
            formType="submit"
          >
            提交
          </AtButton>
        </FormCom>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-雾里-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值