preface

In 20 years, my front-end stack moved from Angular to Vue due to work changes. They are the most popular front-end frameworks at the moment and require a quick change in component design thinking at the beginning.

It has been more than half a year now. My front-end technology stack is mainly as follows:

Front-end framework: Vue 2

UI framework: Element UI

Development language: TypeScript

Use these techniques, and continue to develop, encapsulate some of the components of the project, never get bored.

demand

During project development, there is a scenario where a form component is linked: two Select components are composed, and the options of the second Select are dynamically loaded based on the results of the first Select.

design

To design a linkage component, the first thing that comes to mind is to combine two Selectors and bind them together through an event mechanism to achieve linkage.

Linkage components will eventually be used in Form forms, and the basic functionality of Form components should be taken into account when designing their functionality, such as disabled, Clearable, placeholder, and V-Model bidirectional bindings.

implementation

export interface Option {
  value: string | number;
  label: string;
}
export interface SelectProvider {
  getFirstOptions(): Promise<Option[]>;
  getOptionsByName(name: string.type? :string) :Promise<Option[]>;
  getOptionsById(id: number | string.type? :string) :Promise<Option[]>;
  getDefaultOptions(type? :string) :Promise<Option[]>;
}
export class DefaultSelectProvider {
  emptyOptions: Option[] = [];
  
  getFirstOptions(): Promise<Option[]> {
    return Promise.resolve(this.emptyOptions);
  }
  
  getOptionsByName(name: string.type? :string) :Promise<Option[]> {
    return Promise.resolve(this.emptyOptions);
  }
  
  getOptionsById(id: number | string.type? :string) :Promise<Option[]>  {
    return Promise.resolve(this.emptyOptions); 
  }
  
  getDefaultOptions(type? :string) :Promise<Option[]> {
      return Promise.resolve(this.emptyOptions); }}Copy the code
<template>
  <div>
    <el-select v-model="firstValue"
                :disabled="disabled"
                :clearable="clearable" placeholder="Please select" @change="handleFirstChange">
      <el-option v-for="item in firstOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
    </el-select>
    <el-select
      v-model="secondValue"
      :disabled="disabled"
      :clearable="clearable"
      remote
      filterable
      placeholder="Please select"
      style="margin-left: 8px"
      @change="handleSecondChange"
      :remote-method="handleSecondRemoteMethod"
    >
      <el-option v-for="item in secondOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
    </el-select>
  </div>
</template>
<script lang="ts">
// import ...
@Component
export default class TwoSelect extends Vue { @Prop() value: any; @Prop() first! : string; @Prop() second! : string; @Prop() clearable! : boolean; @Prop() disabled! : boolean; @Prop({type: Object.default: newDefaultSelectProvider() }) selectProvider! : SelectProvider; firstOptions: Option[] = []; secondOptions: Option[] = []; @Watch('value', { immediate: true.deep: true })
  onValueChanged(val) {
    if (val) {
      this.firstValue = val[this.firstField];
      this.secondValue = val[this.secondField];
    } else {
      this.firstValue = ' ';
      this.secondValue = ' ';
    }
  }
  firstField = this.first || 'first';
  secondField = this.second || 'second';
  firstValue = ' ';
  secondValue = ' ';
  created() {
    this.selectProvider.getFirstOptions().then(res= >  this.firstOptions = res);
    this.initSecondOptions();
  }
  initSecondOptions() {
    if (!this.secondOptions || this.secondOptions.length === 0) {
      this.selectProvider.getOptionsByValue(this.secondValue, this.firstValue).then(res= > (this.secondOptions = res)); }}handleFirstChange(val) {
    this.secondValue = ' ';
    this.$emit('firstChange', val);
    const ms: any = null;
    this.selectProvider.getDefaultOptions(val).then(res= > {
      this.secondOptions = res;
    });
  }
  emitChange() {
    const value = {};
    value[this.firstField] = this.firstValue;
    value[this.secondField] = this.secondValue;
    this.$emit('change', value);
  }
  handleSecondChange(val) {
    this.emitChange();
  }
  clearValue() {
    this.firstValue = ' ';
    this.secondValue = ' ';
  }
  handleSecondRemoteMethod(query) {
    // TODO: optimize with throttle-debounce
    if(query ! = =' ') {
      this.selectProvider.getOptionsByLabel(query, this.firstValue).then(res= > this.secondOptions = res); }}}</script>
Copy the code