When there are too many options, use the drop-down menu to display and select the content.

  • Two-way data binding: How to display the selected item when the drop-down list changes;
  • Single choice, multi – choice distinction, and corresponding processing.

Example 1.

code

<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>
Copy the code

Instance address: Select instance

Github UI-Library

Principle 2.

The basic structure of the Select component is as follows

It can be divided into two parts:

  • Display box: used to show the selected items, including the cancel button;
  • Drop-down box: contains selected highlights, disabled items, default selection options, etc., with click selected, click again to cancel the operation.

Fat-select display box:

<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>
      <! -- Drop down box -->
      <div class="select-bottom-part" v-show="isOpen">
        <slot></slot>
      </div>
    </div>
</template>

<script>
export default {
  props: {
    placeholder: { type: String.default: "Please select" },
    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>
Copy the code

Use the tabIndex attribute to make the outermost div trigger the blur event and collapse the dropdown if out of focus.

<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> </div> handleBlur(event) {this.isopen = false; this.$emit('blur', event); }Copy the code

The component implements bidirectional data binding. When the value corresponding to the V-Model changes, the value of the Select component also changes. However, the label attribute of the selected item is displayed in the display box, so the selected value selectValue is distinguished from the selected item selectItems.

At the same time, v-model attributes are configured and watch values are monitored as follows

model: {
    prop: "value".event: "input"
},
watch: {
    value: {
        handler(value) {
            const { multiple } = this;
            const init = value ? value : multiple ? [] : "";
            this.selectValue = multiple ? [...init] : init;
        },
        immediate: true}}Copy the code

It also uses provide to inject a dependency into all of its drop-down boxes to access prop and data such as selectValue and selectItems.

provide() {
    return {
        fatSelect: this
    };
}
Copy the code

The default optionKey: {type: String, default: “value”} is the unique identifier of the drop-down item. The default value is value and can be customized.

Fat-option drop-down box:

Insert the drop-down box into the Select component using a slot, as defined below

<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>
Copy the code

Inject the Select component provided above into the current option with inject: [“fatSelect”]

FatSelect accesses the parent component’s selectItems to determine if the current option is selected.

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

    return selectItems.find(item= > item.key === key);
}
Copy the code

In addition, watch FatSelect. selectValue is also the selected value. As mentioned before, this component implements bidirectional binding of data. When the value of v-model binding of Select component changes, it needs to synchronize to the drop-down item.

["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
}
Copy the code

If fatSelectValue changes, check whether the current option’s optionKey is in the selectValue. If it is, it will

this.fatSelect.selectItems = [
    {
        key,
        label,
        value
    }
];
Copy the code

Conclusion 3.

Some details of the Select component are ignored, such as the logic of multiple choices and single choices. The design logic of the component is highlighted, as well as the implementation of data binding, and some problems encountered in the actual business are summarized.

Original statement: This article is an original article, please indicate the source.