Vue.js 带有 v-model 的自定义选择组件

ine*_*ble 5 javascript custom-component vue.js

我想在 Vue.js 中创建一个自定义选择组件。由于我需要特定的选项样式,因此我需要创建由 div 等组成的“选择”,其外观和行为就像真正的 html 选择。

目前我有这样的事情:

Vue.component('child', {
  template: `<div class="component-container"  @click="showOptions = !showOptions">
        <div class="component__select">
            <span class="component__select--name">Select Fruit</span>
            
            <span class="c-arrow-down" v-if="!showOptions"></span>
            <span class="c-arrow-up" v-if="showOptions"></span>
        </div>
        <ul class="component__select-options" v-if="showOptions" >
            <li class="select--option" v-for="option in options">
                <label> <input type="checkbox" :value="option"/> {{option.name}}</label>
            </li>
        </ul>
    </div>`,

  methods: {
    selectOption(option) {
      this.$emit('option', option)
    }
  },
  data: () => ({
  showOptions: false,
  }),
  props: ['options']
});

var vm = new Vue({
  el: '#app',
  data: () => ({
	options: [
  {id: 0, name: 'Apple'},
  {id: 1, name: 'Banana'},
  {id: 2, name: 'Orange'},
  {id: 2, name: 'Strawberry'},
  ],
  selectedFruit: ''
  }),
})
Run Code Online (Sandbox Code Playgroud)
 .component__select {
   height: 38px;
   background-color: #F5F7FA;
   border: 1px solid #dddddd;
   line-height: 38px;
   display: grid;
   max-width: 500px;
   grid-template-columns: 10fr 1fr;
 }

 .component__select--name {
   font-size: 0.8rem;
   padding: 0 0 0 25px;
   cursor: pointer;
 }

 .c-arrow-down {
   justify-self: end;
 }

 .component__select-options {
   max-height: 180px;
   border: 1px solid #dddddd;
   border-top: none;
   overflow: auto;
   position: absolute;
   z-index: 1500;
   max-width: 500px;
   width: 500px;
   margin: 0;
   padding: 0;
 }

 .select--option {
   height: 35px;
   display: grid;
   align-content: center;
   padding: 0 0 0 25px;
   background-color: #f5f5fa;
   border-bottom: 1px solid #dddddd;
 }

 .select--option:last-child {
   border-bottom: none;
 }

 .select--option:nth-child(2n) {
   background-color: #ffffff;
 }
 
 .select--option input{
   display: none;
 }

 .single-option {
   height: 55px;
   background-color: #2595ec;
   font-size: 0.8rem;
   border: 1px solid red;
 }

 .cust-sel {
   width: 200px;
   height: 38px;
   background-color: #f5f5fa;
   border: 1px solid #dddddd;
 }

 .cust-sel:focus {
   outline-width: 0;
 }
Run Code Online (Sandbox Code Playgroud)
<html>
<head>
  <title>An example</title>
</head>
<body>
  <div id="app">
    <span> This is parent component</span>
    <p>I want to have data from select here:  "{{selectedFruit}}"</p>
    <child :options="options" v-model="selectedFruit"></child>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

但我现在的问题是如何使用子组件上的 v-model 将数据从子组件返回到父组件。

(我知道我可以从子组件中发出数据并执行以下操作:
<custom-select :options="someOptions" @selected="setSelectedOption"/>
但我需要它可重用,并且编写越来越多的方法来从父组件中的每个选择中检索数据并不完全是我认为的工作方式。)

我还需要返回整个对象,而不仅仅是 ID。(这就是我有的原因:value="option")有什么想法吗?

Sph*_*inx 5

正如 Vue 指南所说

v-model 本质上是用于更新用户输入事件数据的语法糖,并且特别关注一些边缘情况。

语法糖将类似于:

该指令=v-model将绑定值,然后监听input事件以进行更改,例如v-bind:value="val" v-on:input="val = $event.target.value"

因此,对于您的用例,您需要创建一个 prop=value,然后使用 event=input 发出所选选项。

就像下面的演示(绑定/发出整个选项对象):

Vue.config.productionTip = false
Vue.component('child', {
  template: `<div class="component-container"  @click="showOptions = !showOptions">
        <div class="component__select">
            <span class="component__select--name">{{value ? value.name : 'Select Fruit'}}</span>
            
            <span class="c-arrow-down" v-if="!showOptions"></span>
            <span class="c-arrow-up" v-if="showOptions"></span>
        </div>
        <ul class="component__select-options" v-if="showOptions" >
            <li class="select--option" v-for="option in options" @click="selectOption(option)">
                <label> <input type="checkbox" :value="option"/> {{option.name}}</label>
            </li>
        </ul>
    </div>`,

  methods: {
    selectOption(option) {
      this.$emit('input', option)
    }
  },
  data: () => ({
    showOptions: false
  }),
  props: ['options', 'value']
});

var vm = new Vue({
  el: '#app',
  data: () => ({
    options: [
  {id: 0, name: 'Apple'},
  {id: 1, name: 'Banana'},
  {id: 2, name: 'Orange'},
  {id: 2, name: 'Strawberry'},
  ],
  selectedFruit: ''
  }),
})
Run Code Online (Sandbox Code Playgroud)
 .component__select {
   height: 38px;
   background-color: #F5F7FA;
   border: 1px solid #dddddd;
   line-height: 38px;
   display: grid;
   max-width: 500px;
   grid-template-columns: 10fr 1fr;
 }

 .component__select--name {
   font-size: 0.8rem;
   padding: 0 0 0 25px;
   cursor: pointer;
 }

 .c-arrow-down {
   justify-self: end;
 }

 .component__select-options {
   max-height: 180px;
   border: 1px solid #dddddd;
   border-top: none;
   overflow: auto;
   position: absolute;
   z-index: 1500;
   max-width: 500px;
   width: 500px;
   margin: 0;
   padding: 0;
 }

 .select--option {
   height: 35px;
   display: grid;
   align-content: center;
   padding: 0 0 0 25px;
   background-color: #f5f5fa;
   border-bottom: 1px solid #dddddd;
 }

 .select--option:last-child {
   border-bottom: none;
 }

 .select--option:nth-child(2n) {
   background-color: #ffffff;
 }
 
 .select--option input{
   display: none;
 }

 .single-option {
   height: 55px;
   background-color: #2595ec;
   font-size: 0.8rem;
   border: 1px solid red;
 }

 .cust-sel {
   width: 200px;
   height: 38px;
   background-color: #f5f5fa;
   border: 1px solid #dddddd;
 }

 .cust-sel:focus {
   outline-width: 0;
 }
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<div id="app">
  <span> This is parent component</span>
  <p>I want to have data from select here:  "{{selectedFruit}}"</p>
  <child :options="options" v-model="selectedFruit"></child>
</div>
Run Code Online (Sandbox Code Playgroud)