mand-mobile icon indicating copy to clipboard operation
mand-mobile copied to clipboard

form 表单校验

Open wuazhu opened this issue 5 years ago • 9 comments

Mand Mobile Version

版本 2.2.0

OS Version & Browser Version

MacOS Chrome

Node Version, Package Management Tool

node 8.11.3

  1. 按照官方案例做一个 input 的表单校验, 把 md-input-item 提出一个组件, 当表单初始化的时候需要去给 input 赋值, 这个时候按官方案例并无地方去赋值, 组件内部接收一个 value 做 v-model 用, 通过 value 传值进去也不行.

  2. 表单中有各类项, 比如 picker, 如何结合表单做一个校验, 或者说如何给 md-field-item 绑定一个校验器. 是否可以有案例支持.

  3. 是否考虑支持像 cube-ui 类似的 field 组件, 可以提供数据 model 生成表单

wuazhu avatar May 20 '19 16:05 wuazhu

  1. v-model就是用于给表单赋值的,等同于value,只不过内置了双向绑定
<md-input-item v-model="inputVal"></md-input-item>
  1. mand-mobile交互设计中,field-item仅用于接受和展示输入值,且输入一般来源于各种选择器,所以选择器仅提供可选值,所以没有在field-item做类似校验的功能,校验功能都在作为输出方的选择器上。

  2. 考虑

xxyan0205 avatar May 21 '19 02:05 xxyan0205

可能我没有说明白第一个点, 我意思是这类表单该如何校验, 我现在做法如下, 是给每一个需要校验的都用一个md-input-item来绑定一个值, 点击的时候去打开去校验这个值, 总觉得这种做法是很傻.. 但是苦于没有其他方式来实现, 官网也没有具体示例, 想问问你们是如何做表单这块的

<md-input-item
        v-model="genderValues"
        name="gender"
        v-validate="'required'"
        data-vv-validate-on="change"
        solid
        readonly
        :disabled="true"
        placeholder="请选择性别"
        arrow
        :error="errors.first('gender')"
        @click.native="showGenderPikcer = !showGenderPikcer">
        <label slot="left"
          class="md-field-item-title">性别<span class="text-danger">*</span>
        </label>
      </md-input-item>

<md-picker
      ref="genderPickerRef"
      v-model="showGenderPikcer"
      :data="genderData"
      :default-index="genderIndex"
      @confirm="selectGender"
      title="选择性别"
    ></md-picker>

wuazhu avatar May 22 '19 02:05 wuazhu

可以参考 https://didi.github.io/mand-mobile/#/zh-CN/docs/components/form/input-item?anchor=%E8%A1%A8%E5%8D%95%E6%A0%A1%E9%AA%8C

xxyan0205 avatar May 22 '19 03:05 xxyan0205

可以参考 https://didi.github.io/mand-mobile/#/zh-CN/docs/components/form/input-item?anchor=%E8%A1%A8%E5%8D%95%E6%A0%A1%E9%AA%8C

这个已经看过了, 并且这个还有点问题, 比如里面提到 md-input-item 的 blur 有问题需要把这个组件在重新写一个组件里面去 emit blur 方法, 这个在空表单使用的时候没问题, 但是没法给input-validate赋值, 组件内部使用的是 v-model, 并接收一个value 字段, 讲道理那input-validate就可以使用 v-model="xx" 传入一个字段, 或者: value="xx" 传入一个字段, xx赋值的时候的时候 这个 input-validate 并没有 展示出对应的文字效果. 还有个问题, 类似表单项里 picker 如何进行绑定值校验, 能否提供个例子呢...

<input-validate
        type="phone"
        title="Phone"
        name="phone"
        placeholder="Phone Validate On Blur"
        v-validate="'phone'"
        data-vv-value-path="innerValue"
        data-vv-validate-on="blur"
        :error="errors.first('phone')"
      >

wuazhu avatar May 22 '19 03:05 wuazhu

目前在统一校验这块没有太好的解决方式,之前的确是需要分散在各个地方。如issue里提到的,我们考虑在Field上增加配置校验规则,统一对内部的field-item和input-item进行校验。

xxyan0205 avatar May 22 '19 04:05 xxyan0205

<template>
  <div class="selector-item" @click="clickHandler">
    <md-input-item
      ref="mdInput"
      class="inner-input"
      is-highlight
      :value="innerTxt"
      :placeholder="placeholder"
      :error="error"
      readonly>
      <md-icon name="arrow_down" class="icon-arrow" svg slot="right"></md-icon>
    </md-input-item>
  </div>
</template>

<script lang="tsx">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { Component as instance } from 'vue'
import { Icon, InputItem } from 'mand-mobile'
import '@/lib/apiComponents'

interface IOption {
  value: string | number | boolean,
  text: string | number,
  [k: string]: string | number | boolean,
}

@Component({
  components: {
    [Icon.name]: Icon,
    [InputItem.name]: InputItem,
  },
})
export default class Selector extends Vue {
  @Prop({
    type: Array,
    default: () => [],
  }) options!: IOption[]

  @Prop({
    type: String,
    default: 'Picker',
  }) type!: string
  @Prop({
    type: [String, Number, Array],
    default: '',
  }) value!: string | number | []
  @Prop({ type: [String, Number], default: '' }) title!: string | number
  @Prop({ type: [String, Number], default: '' }) placeholder!: string | number
  @Prop({ type: [String, Number], default: '' }) error!: string | number
  @Prop({ type: Boolean, default: false }) autoRemove!: boolean

  private innerValue: string | number | [] = ''
  private picker!: any

  @Watch('value', { immediate: true })
  valueChange(val: string | number | []) {
    this.innerValue = val
  }
  @Watch('options')
  optionsChange(val: IOption[]) {
    if (this.type !== 'date' && this.picker && val.length > 0) {
      this.picker.$updateProps({
        data: val,
      })
      // 重置滚动区域
      this.$nextTick(() => {
        this.picker.$children[0].$children[1].reflowScroller()
      })
    }
  }

  get innerTxt() {
    if (this.type === 'date') return this.value
    return this.type !== 'date' && this.options.filter(item => item.value === this.innerValue)[0] &&
      this.options.filter(item => item.value === this.innerValue)[0].text
  }

  clickHandler() {
    (this.$refs.mdInput as any).blur()
    this.showPicker()
    this.$emit('click')
  }

  selectHandle(option: IOption) {
    this.$emit('input', option.value)
  }

  hideHandle() {
    this.picker.$updateProps({
      value: false,
    })
    this.$emit('hide')
  }

  confirmHandler(json: any) {
    this.$emit('input', `${json[0].value}-${json[1].value}`)
    this.hideHandle()
  }

  showPicker() {
    if (!this.picker && this.type !== 'date') {
      this.picker = this.$createMdSelector({
        $props: {
          value: true,
          data: this.options,
          title: this.title,
          'default-value': this.value,
          'max-height': '40vh',
          'min-height': '10vh',
        },
        $events: {
          choose: this.selectHandle,
          hide: this.hideHandle,
        },
      })
    }
    if (!this.picker && this.type === 'date') {
      this.picker = this.$createMdDatePicker({
        $props: {
          value: true,
          type: 'custom',
          title: 'Bekerja sejak',
          'today-text': '&(today)',
          'custom-types': ['yyyy', 'MM'],
          'unit-text': ['', '', ''],
          'default-date': new Date(),
          'max-date': new Date(),
          'cancel-text': 'Batal',
          'ok-text': 'Lanjut',
        },
        $events: {
          confirm: this.confirmHandler,
          hide: this.hideHandle,
          change: (json: any) => console.log(json),
        },
      })
    }
    if (this.picker) {
      this.picker.$updateProps({
        value: true,
      })
    }
    if (this.autoRemove) {
      this.picker.$on('hide', (json: any) => {
        this.picker.remove()
        this.picker = null
      })
    }
  }
}
</script>

<style lang="stylus" scoped>
.selector-item
  width 100%
  position relative
  display flex
  flex-direction column
  justify-content space-between
  align-items center
  .inner-input
    width 100%
  .icon-arrow
    width 48px
    height 48px
</style>

封装一下 selector 和 datepicker 即可,将 picker 调用改为 api 式即可

zouhangwithsweet avatar May 22 '19 05:05 zouhangwithsweet

请问所有的 picker 或者 modal 都有 create 单例模式么. 具体在文档哪里有说明呢.. 能否提供个链接, 一直没发现..

wuazhu avatar May 24 '19 07:05 wuazhu

奥,是我自己封装的,你用 Vue-create-api 包装下就好

zouhangwithsweet avatar May 29 '19 06:05 zouhangwithsweet

我是用vee-validate二次封装

<validation-provider
                       tag="div"
                       :rules="rules"
                       :name="name ? name : '该项'"
                       v-slot="{ errors }"
                       ref="validateRef">
    <md-input-item ref="inputRef"
                   v-model="innerValue"
                   :name="name"
                   :error="error || errors[0]"
                   :readonly="isReadOnly"
                   :disabled="isDisabled"
                   v-bind="$attrs"
                   v-on="$listeners"
                   @change.native="onInputChange"
                  >
      <template slot="right">
        <slot name="right"></slot>
      </template>
    </md-input-item>
  </validation-provider>

rules则是校验规则

rules="required|phone"

RunningTree avatar May 12 '20 03:05 RunningTree