Vue 自定义组件

下面有一个例子。

<template>
  <el-input
    :value="value"
    @click.native="select"
    readonly>
    <i class="el-icon-circle-close" slot="suffix" @mousedown.prevent @click.stop="clear"/>
  </el-input>
</template>

<script>
export default {
  name: 'ElDialogSelect',
  // 自定义 v-model
  model: {
    prop: 'value',
    event: 'clear'
  },
  props: {
    value: [String, Number]
  },
  methods: {
    // 自定义事件
    clear(){
      this.$emit('clear', '')
    },
    select(){
      this.$emit('select', this.value)
    }
  }
}
</script>

自定义事件

该组件定义了 select 事件和 clear 事件:

methods: {
  clear(){
    // 使用 $emit 方法触发 clear 事件
    this.$emit('clear', '')
  },
  select(){
    // 使用 $emit 方法触发 select 事件
    this.$emit('select', this.value)
  },
  ...
}

其中 clear 事件传了一个空字符串作为参数,select 事件传递 value 变量作为参数。

点击 el-input 会调用 methods 中的 select 方法,进而触发 select 事件,点击 el-input 后方的 clear 图标会调用 methods 中的 clear 方法,进而触发 clear 事件:

<el-input
    ...
    @click.native="select"
    ...>
    <i
    ...
    slot="suffix"
    ...
    @click.stop="clear"/>
</el-input>

父组件可传递事件处理函数给该组件:

<template>
  <el-dialog-select @select="handleSelect" @clear="handleClear">
</template>
<script>
export default{
  ...
  methods: {
    handleSelect(value){
      console.log(value)
    },
    handleClear(value){
      console.log(value)
    }
  }
}
</script>

点击 el-input,handleSelect 可收到this.$emit('select', this.value)传来的 value 值,点击 clear 图标,handleClear 可收到this.$emit('clear', '')传来的空字符串。

自定义 v-model

先定义 model 和 prop:

model: {
  prop: 'value',
  event: 'clear'
},
props: {
  value: [String, Number]
},

然后将用来绑定数据的 prop 传给 el-input:

<el-input
  :value="value"
  ...
></el-input>

这样,父元素就可以使用 v-model 绑定值:

<template>
  <el-dialog-select v-model="selectStr" @clear="handleClear">
</template>
<script>
export default{
  ...
  data(){
    return {
        selectStr: ''
    }
  },
  methods: {
    ...
    handleClear(value){
      this.selectStr = ''
    }
  }
}
</script>

当父元素的 selectStr 发生改变时,el-input 的 value 属性会对应改变(值从父组件传给子组件的 value 属性,再从子组件的 value 属性传给 el-input 的 value 属性)。

当点击 clear 图标的时候,子组件会传递空字符串给父组件中的 handleClear 方法,如果在 handleClear 中添加this.selectStr = ''语句,则可以实现点击 clear 图标时清空 selectStr,进而清空 el-input 的 value 属性。

model 选项是什么

Vue 中用 v-model 来做数据绑定:

<input v-model="searchText" />

背后编译器会把它展开为如下形式:

<input
  :value="searchText"
  @input="searchText = $event.target.value"
/>

而当使用在一个组件上时,v-model 会被展开为如下的形式:

<CustomInput
  :modelValue="searchText"
  @update:modelValue="newValue => searchText = newValue"
/>

对于上面的例子,model: { prop: 'value', event: 'clear' }的作用是让:

<el-dialog-select v-model="selectStr">

展开为:

<el-dialog-select :value="selectStr" @clear="newValue => selectStr = newValue">

扩展

基于上面的例子,可以写这样一个组件:

<template>
  <!-- 选择组件 -->
  <el-tooltip :disabled="disabledTooltip" effect="dark" :content="value" placement="top">
    <el-input
      :value="value"
      :placeholder="placeholder"
      class="el-select"
      ref="elInput"
      @click.native="select"
      @mouseenter.native="hovering = true"
      @mouseleave.native="hovering = false"
      readonly
    >
      <i v-if="showClear"
        class="el-input__icon el-icon-circle-close el-input__clear"
        slot="suffix"
        @mousedown.prevent
        @click.stop="clear"/>
      <i v-else class="el-select__caret el-input__icon el-icon-arrow-down"
        slot="suffix"/>
    </el-input>
  </el-tooltip>
</template>

<script>
export default {
  name: 'ElDialogSelect',
  model: {
    prop: 'value',
    event: 'clear'
  },
  props: {
    value: [String, Number],
    showOverflowTooltip: {
      type: Boolean,
      default: true
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    clearable: {
      type: Boolean,
      default: true
    }
  },
  data(){
    return {
      hovering: false,
      disabledTooltip: true
    }
  },
  computed: {
    showClear(){
      return this.clearable && this.value && this.hovering
    }
  },
  watch: {
    value(){
      this.$nextTick(()=>this.disabledTooltip = this.setDisabledTooltip())
    }
  },
  methods: {
    clear(){
      this.$emit('clear', '')
    },
    select(){
      this.$emit('select', this.value)
    },
    // 设置 tooltip 显隐
    setDisabledTooltip(){
      if(!this.showOverflowTooltip) return true
      const input = this.$refs?.elInput?.$refs?.input
      if(input && input.offsetWidth < input.scrollWidth) return false
      return true
    }
  }
}
</script>

<style lang="less" scoped>
.el-select{
  /deep/ input{
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}
</style>

一个使用的例子是点击组件打开弹窗,点击组件 clear 图标清除所选数据:

<template>
  <div>
    <el-form
      :model="form">
      <el-form-item label="选择数据:">
        <el-dialog-select v-model="form.selectedNames" 
          @select="selectData" @clear="clearSelect"/>
      </el-form-item>
    </el-form>
    ...
  </div>
</template>

<script>
export default{
  ...
  data(){
    return {
      form: {
        selectedNames: ''
      }
    }
  },
  methods: {
    selectData(){
      // 打开弹窗
    },
    clearSelect(){
      // 清除选择数据
    }
  }
}
</script>

参考:自定义事件 — Vue.js组件 v-model | Vue.jsVue 的 model 选项 - 掘金组件事件 | Vue.jsAPI — Vue.js组件事件 | Vue.js组件 v-model | Vue.js

posted @ 2023-08-09 22:32  Higurashi-kagome  阅读(58)  评论(0编辑  收藏  举报