从零实现Vue的组件库(十)- Select 实现

栏目: 编程语言 · 发布时间: 5年前

内容简介:当选项过多时,使用下拉菜单展示并选择内容。代码实例地址:Select 实例

当选项过多时,使用下拉菜单展示并选择内容。

Select 组件主要特点在于:
  • 数据双向绑定,下拉列表变动时,选中项如何回显;
  • 单选、多选的区分,以及对应处理。

1. 实例

从零实现Vue的组件库(十)- Select 实现

代码

<fat-select v-model="inputValue">
    <fat-option
        v-for="item in options"
        :key="item.value"
        :label="item.label"
        :value="item.value"
    >{{ item.label }}</fat-option>
</fat-select>
复制代码

实例地址:Select 实例

代码地址: Github UI-Library

2. 原理

Select组件的基本结构如下

从零实现Vue的组件库(十)- Select 实现

主要可分为两个部分:

  • 显示框:用来展示已经选中项,包含取消按钮;
  • 下拉框:包含已选中的高亮项,禁用项,默认选择选项等,具备点击选中,再次点击取消的操作;
  • 确保每个下拉项唯一,即使存在相同 label 的情况。

fat-select 显示框:

<template>
    <div
      :class="['select-wrapper', { 'is-disabled': disabled }]"
      tabindex="0"
      @click.stop="isOpen = !disabled && !isOpen"
      @blur="handleBlur"
    >
      <div class="select-top-part">
        <template v-if="!selectItems.length">
          <span class="placeholder">{{ placeholder }}</span>
        </template>

        <template v-else>
          <div>{{ selectItems[0].label }}</div>
        </template>
      </div>
      <!-- 下拉框 -->
      <div class="select-bottom-part" v-show="isOpen">
        <slot></slot>
      </div>
    </div>
</template>

<script>
export default {
  props: {
    placeholder: { type: String, default: "请选择" },
    optionKey: { type: String, default: "value" },
    value: { type: [String, Object, Number, Array] }
  },
  model: {
    prop: "value",
    event: "input"
  },
  data() {
    return {
      isOpen: false,

      selectValue: [],

      selectItems: []
    };
  },
  provide() {
    return {
      fatSelect: this
    };
  },
  watch: {
    value: {
      handler(value) {
        const { multiple } = this;
        const init = value ? value : multiple ? [] : "";
        this.selectValue = multiple ? [...init] : init;
      },
      immediate: true
    },
    selectValue: {
      handler(value) {
        this.selectItems = [];
      }
    }
  },
  methods: {
    handleDelete(item) {
      const { value } = item;
      this.selectValue = this.selectValue.filter(item => item !== value);
      this.$emit("input", this.selectValue);
      this.$emit("change", this.selectValue);
    },
    handleBlur(event) {
      this.isOpen = false;
      this.$emit('blur', event);
    }
    ...
  }
};
</script>
复制代码

利用 tabIndex 属性使得最外层的 div 能够触发 blur 事件,如果失焦就收起下拉框。

<div
    :class="['select-wrapper', { 'is-disabled': disabled }]"
    tabindex="0"
    @click.stop="isOpen = !disabled && !isOpen"
    @blur="handleBlur"
>
    ...
    <!-- 下拉框 -->
    <div class="select-bottom-part" v-show="isOpen">
        <slot></slot>
    </div>
</div>

handleBlur(event) {
    this.isOpen = false;
    this.$emit('blur', event);
}
复制代码

组件实现数据双向绑定,当 v-model 对应的值变动时, Select 组件的值也会发生改变,但是显示框内所呈现的是选中项的 label 属性,所以将选中值 selectValue 和选中项 selectItems 进行区分。

同时配置 v-model 相关属性,同时监测 watch 相关 value 具体如下

model: {
    prop: "value",
    event: "input"
},
watch: {
    value: {
        handler(value) {
            const { multiple } = this;
            const init = value ? value : multiple ? [] : "";
            this.selectValue = multiple ? [...init] : init;
        },
        immediate: true
    }
}
复制代码

同时利用 provide 向其所有下拉框注入一个依赖,用于访问 selectValueselectItemspropdata

provide() {
    return {
        fatSelect: this
    };
}
复制代码

默认 optionKey: { type: String, default: "value" } 作为下拉项的唯一标识,默认值为 value ,也可自定义。

fat-option 下拉框:

利用插槽将下拉框插入 Select 组件中,其具体定义如下

<template>
  <div
    :class="['select-option-wrapper', { 'is-selected': isSelect }, { 'is-disabled': disabled }]"
    @click.stop="handleClick"
  >
    <slot></slot>
  </div>
</template>
<script>
export default {
  props: {
    value: { type: [Object, String, Number], required: true },
    label: { type: String },
    disabled: { type: Boolean, defa: false }
  },
  inject: ["fatSelect"],
  computed: {
    isSelect() {
      const {
        fatSelect: { optionKey, selectItems }
      } = this;
      const key = this[optionKey] || this.$attrs[optionKey];

      return selectItems.find(item => item.key === key);
    }
  },
  watch: {
    ["fatSelect.selectValue"]: {
      handler(newValue) {
        const {
          value,
          label,
          fatSelect: { optionKey, multiple, selectValue }
        } = this;
        const key = this[optionKey] || this.$attrs[optionKey];

        if (
          newValue === value ||
          (Array.isArray(newValue) && newValue.find(item => item === value))
        ) {
          if (!multiple) {
            this.fatSelect.selectItems = [
              {
                key,
                label,
                value
              }
            ];
          } else {
            this.fatSelect.selectItems.push({
              key,
              label,
              value
            });
          }
        }
      },
      immediate: true
    }
  },
  methods: {
    ...
  }
};
</script>
复制代码

利用 inject: ["fatSelect"] 将上述 provideSelect 组件注入到当前选项中,

通过 this.fatSelect 来访问父组件的 selectItems 来判断,当前选项是否为选中项。

isSelect() {
    const {
        fatSelect: { optionKey, selectItems }
    } = this;
    const key = this[optionKey] || this.$attrs[optionKey];

    return selectItems.find(item => item.key === key);
}
复制代码

同时watch fatSelect.selectValue 也就是选中值,之前说过该组件实现数据的双向绑定,当 Select 组件 v-model 绑定的值变动时,需要同步到下拉项。

["fatSelect.selectValue"]: {
    handler(newValue) {
        const {
          value,
          label,
          fatSelect: { optionKey, multiple, selectValue }
        } = this;
        const key = this[optionKey] || this.$attrs[optionKey];

        if (
          newValue === value ||
          (Array.isArray(newValue) && newValue.find(item => item === value))
        ) {
          if (!multiple) {
            this.fatSelect.selectItems = [
              {
                key,
                label,
                value
              }
            ];
        } else {
            this.fatSelect.selectItems.push({
              key,
              label,
              value
            });
          }
        }
    },
    immediate: true
}
复制代码

如果对应的 fatSelect.selectValue 变动时,要判断当前选项的 optionKey 是否在 selectValue 中,如果存在,就将

this.fatSelect.selectItems = [
    {
        key,
        label,
        value
    }
];
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Nature of Code

The Nature of Code

Daniel Shiffman / The Nature of Code / 2012-12-13 / GBP 19.95

How can we capture the unpredictable evolutionary and emergent properties of nature in software? How can understanding the mathematical principles behind our physical world help us to create digital w......一起来看看 《The Nature of Code》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具