自制ant.design表格组件教程

前言

在实际项目中我们时常有可能需要Ip地址输入框,键值对输入框这样的复杂输入组件。但是ant design
官方并没有给出这样的组件,文档中也没有提到应该如何写出这样的组件。

我在研究了一番实现了该功能,在这里与大家分享一下,希望可以方便到后来人。

源码分享

对于不需要教程的同学可以直接查看源码:

教程-编写一个IP输入组件

第一步:完全受控组件与ant design表格组件联动

首先我们需要建立一个完全受控的输入组件,并且该组件可以嵌套进ant design的表格组件当中并正常
运行。

{getFieldDecorator('ip')(
  <IpInput />
)&#125;

IpInput组件代码:

import React, &#123;Component&#125; from 'react';
import &#123;Input&#125; from "antd";

export default class IpInput extends Component &#123;
  constructor(props) &#123;
    super(props);

    this.state = &#123;
      ip: '',
    &#125;
  &#125;


  hdlInputChange = (e) => &#123;
    this.setState(&#123;ip: e.target.value&#125;)
    this.props.onChange(&#123;target: &#123;value: e.target.value&#125;&#125;)
  &#125;

  render() &#123;
    return (
      <div>
        <Input value=&#123;this.state.ip&#125; onChange=&#123;this.hdlInputChange&#125; />
      </div>
    );
  &#125;
&#125;

在CodeSandbox中浏览

第二步:在IpInput组件中建立多个Input,并联动

重点是输出值的拆分与聚合

hdlInputChange = (e, block) => &#123;
    let ipAdressArr = [this.state.ablock, this.state.bblock, this.state.cblock, this.state.dblock]
    let value = e.target.value
    this.setState(&#123;[`$&#123;block&#125;block`]: value&#125;)
    ipAdressArr[blockDict[block]] = value
    this.props.onChange(&#123;target: &#123;value: ipAdressArr.join('.')&#125;&#125;)
  &#125;

详细代码请看:CodeSandbox

到这一步一个基本的ip Input输入组件已经成型,但是难点部分还未开始。

第三步:校验规则处理

如果现在我们就开始正常使用该组件,4个输入框的报错样式必定是同时出现的。一个是红框的话,其他
三个也必定是红框状态。这是ant design的程序设计,FormItem的报错样式由外层容器通过CSS控制。

上面这段话不容易理解的话自己试一下就明白了。

也就是说我们需要单独控制4个Input框的校验状态,已经有数字的应为正确状态,没有数字的校验时显示为
红色。且这个校验行为要能够被ant designForm组件触发。

首先我们建立一个类似ant design原生的Form.Item组件用来包裹每个Input并负责校验逻辑。

class FromItemValidatorWarpper extends Component &#123;
  constructor(props) &#123;
    super(props)

    this.state = &#123;
      validateStatus: ''
    &#125;
  &#125;

  componentDidMount () &#123;&#125;

  componentDidUpdate (prevProps, prevState) &#123;
    if(this.props.valiating && prevProps.validating !== this.props.valiating)&#123;
      // form 触发的 validating

      let validateStatus = ''
      if(!this.getChildProp("value"))&#123;
        validateStatus = 'error'
      &#125;
      if(this.state.validateStatus !== validateStatus)&#123;
        this.setState(&#123;validateStatus&#125;)
      &#125;

    &#125;
  &#125;

  getChildProp(prop) &#123;
    const child = this.props.children
    return child && child.props && child.props[prop];
  &#125;

  render() &#123;
    return (
      <Form.Item
        validateStatus=&#123;this.state.validateStatus&#125;
      >
        &#123;this.props.children&#125;
      </Form.Item>
    )
  &#125;
&#125;

然后用该组件包裹IpInput组件

<FromItemValidatorWarpper valiating=&#123;this.state.valiating&#125;>
  <Input value=&#123;this.state.dblock&#125; onChange=&#123;(e)=>&#123;this.hdlInputChange(e, 'd')&#125;&#125; />
</FromItemValidatorWarpper>

IpInput组件中建立valiating属性,并使其和外层ant design Form组件的validator联动。

  componentDidUpdate(prevProps) &#123;
    let dataField = this.props["data-__field"]

    if(dataField.errors && !prevProps["data-__field"].errors)&#123;
      // valitor 函数触发报错
      this.setState(&#123;valiating: true&#125;)
    &#125;
    if(!dataField.errors && prevProps["data-__field"].errors)&#123;
      // valitor 函数触发清除报错
      this.setState(&#123;valiating: false&#125;)
    &#125;
  &#125;

最后在使用IpInput的组件位置添加一个正则校验。

  &#123;getFieldDecorator('ip', &#123;
    rules: [
      &#123;required: true, message: 'Please input your ip!'&#125;,
      &#123;
        pattern: /((?:(?:25[0-5]|2[0-4]\d|((1\d&#123;2&#125;)|([1-9]?\d)))\.)&#123;3&#125;(?:25[0-5]|2[0-4]\d|((1\d&#123;2&#125;)|([1-9]?\d))))/,
        message: 'Please finish your ip!'
      &#125;
    ],
  &#125;)(
    <IpInput/>
  )&#125;

请在CodeSandbox中查看效果

OK,我们现在只差最后一步了,即在外层使用组件时使用setFieldsValue初始化组件值的功能。

第四步:setFieldsValue方法支持

componentDidUpdate方法中加入一段代码判断组件上级value是否和内部value相同,不同且没有进行
初始化时进行初始化。

if(dataField.value !== [this.state.ablock, this.state.bblock, this.state.cblock, this.state.dblock].join('.') && !this.initialized)&#123;
  this.initialized = true
  let ipAdressArr = dataField.value.split('.')
  this.setState(&#123;
    ablock: ipAdressArr[0],
    bblock: ipAdressArr[1],
    cblock: ipAdressArr[2],
    dblock: ipAdressArr[3],
  &#125;)
&#125;

请在CodeSandbox中查看效果

结尾

以上,一个IP输入组件基本完成了。细节包括样式还有很多可以优化的地方,我在这里就不继续了。maybe
以后再来更新。

我把最后的完整代码放在github上,有任何建议可
以给我提交issue或下面评论回复,谢谢。

作者

Micheal

发布于

2019-10-11

更新于

2023-04-12

许可协议

评论