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

时间:2018-06-20 15:45:40

标签: javascript vue.js custom-component

我想在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: ''
  }),
})
 .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;
 }
<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>

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

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

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

2 个答案:

答案 0 :(得分:2)

Vue Guide said

  

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: ''
  }),
})
 .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;
 }
<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>

答案 1 :(得分:0)

在自定义组件上使用v-model时,您需要声明一个名为“值”的道具,当您需要该组件有机会发出“输入”事件时。

类似这样的东西:

q){i:0;while[i<>50;i+:1];:"loop exited at ",string i}`
"loop exited at 50"

然后您可以像这样使用(在注册组件之后):

<template>
  <form @submit.prevent="$emit('onSearch',val)" class="form-perfil">
    <div class="form-group col-md-12">
      <input v-model="val" @input="$emit('input',val)" 
      placeholder="filtrar resultados" class="form-control">
    </div>
  </form>
</template>
<script>
module.exports = {
  name: "CaixaFiltro",
  props: ["value"],
  data: _ => ({ val: "" }),
  created() {
    this.val = this.value
  }
}
</script>

您可以找到更多详细信息there