Пользовательская директива VueJs 2 закрывается при нажатии снаружи не работает - PullRequest
0 голосов
/ 26 октября 2018

Я работаю над компонентом vuejs, в котором, если вы щелкнете за пределами поля со списком после того, как щелкнули по нему, он должен закрыться.

Моя проблема в том, что пользовательская директива не работает, программа компилируется, но в браузере я получаю эту ошибку:

[Vue warn]: Error in directive click-outside bind hook: "TypeError: Cannot set property 'event' of undefined"

Это код моего компонента:

<template>
  <div class="form-group" v-click-outside="hide">
  <label v-if="label" for="combobox" class="control-label" v-tack>{{ label }}:</label>
    <input id="combobox"
    class="form-control combo-box-control"
    v-on:keyup="filter(searchText,options)"
    v-model="searchText"
    :placeholder="placeholder"
    v-on:click="showAllOptions()" :disabled="isDisabled">
        <template v-if="showAutocomplete">
        <div class="combobox-list">
            <p class="combobox-options" :key="item.id" v-for="item in listFiltered" v-on:click="optionSelected(item)">{{item.text}}</p>
        </div>
        </template>
  </div>
</template>
<script>
export default {
  data () {
    return {
      listFiltered: [],
      searchText: '',
      showAutocomplete: false
    }
  },
  props: {
    name: { type: String, required: true },
    options: Array,
    label: String,
    isDisabled: { type: Boolean, default: false },
    selectedOption: Object,
    placeholder: String
  },
  methods: {
    filter (word, array) {
      if (word === undefined) {
        this.showAutocomplete = false
        this.listFiltered = []
        return
      }
      this.showAutocomplete = true
      this.listFiltered = array.filter(function (item) {
        return item.text.toLowerCase().includes(word.toLowerCase())
      })
    },
    optionSelected (item) {
      this.searchText = item.text
      this.showAutocomplete = false
      if (item !== undefined) {
        this.$emit('change', { name: this.name, item: item })
      }
    },
    showAllOptions () {
      this.listFiltered = this.options
      this.showAutocomplete = !this.showAutocomplete
    },
    hide () {
      this.showAutocomplete = false
    }
  }
}
</script>
<style>
.form-group{
    position:relative;
}
.form-group input{
    width:100%;
}

input.combo-box-control:active{
  border-style: solid;
  border-width: 1px 1px 0 1px;
  border-radius: 5px 5px 0px 0px;
  border-color: #96c8da;
}

.form-control.combo-box-control:focus{
  border-color: #96c8da;
}

.combobox-list{
    position:relative;
    height:154px;
    width:100%;
    background-color:white;
    overflow-y:auto;
    text-align:justify;
    z-index: 5;
    border-style: solid;
    border-color: #96c8da;
    border-width: 0 1px 1px 1px;
    border-radius: 0px 0px 5px 5px;
    top: -3px;
}

.combobox-options{
    padding:6px 0;
    margin:0;
}
.combobox-options:hover{
    background-color:#d9d9d9;
}
</style>

и main.js:

Vue.directive('click-outside', {
  bind: function (el, binding, vnode) {
    this.event = function (event) {
      if (!(el === event.target || el.contains(event.target))) {
        vnode.context[binding.expression](event)
      }
    }
    document.body.addEventListener('click', this.event)
  },
  unbind: function (el) {
    document.body.removeEventListener('click', this.event)
  }
})

проблема в строке this.event = function (event) {, потому что ошибка говорит Cannot set property 'event' of undefined, так что это не определено. Не уверен, как это определить, внутри директивы.

Я использую этот пример, чтобы заставить его работать с моим пользовательским компонентом: https://jsfiddle.net/Linusborg/yzm8t8jq/ Я что-то пропустил? Обновление: Оказывается, код из примера взят из vuejs 1.x, хотя я нашел похожий пример в vuejs 2.1 https://jsfiddle.net/y0rpfecd/,, все еще получаю ту же ошибку.

1 Ответ

0 голосов
/ 17 января 2019

Возможно, вы нашли ответ на него. Надеюсь, кто-нибудь найдет это полезным.

Я недавно проверил директиву click-outside в моем стороннем проекте. Просто замените this.event на window.event в директивном коде. И это прекрасно работает !!

directive.js

import Vue from 'vue';

Vue.directive('click-outside', {
    bind: function (el, binding, vnode) {
        window.event = function (event) {
            if (!(el == event.target || el.contains(event.target))) {
                vnode.context[binding.expression](event);
            }
        };
        document.body.addEventListener('click', window.event)
    },
    unbind: function (el) {
        document.body.removeEventListener('click', window.event)
    },
});

Для использования в компоненте: Просто импортируйте директиву.

<template>
  <div class="form-group" v-click-outside="hide">
  <label v-if="label" for="combobox" class="control-label" v-tack>{{ label }}:</label>
    <input id="combobox"
    class="form-control combo-box-control"
    v-on:keyup="filter(searchText,options)"
    v-model="searchText"
    :placeholder="placeholder"
    v-on:click="showAllOptions()" :disabled="isDisabled">
        <template v-if="showAutocomplete">
        <div class="combobox-list">
            <p class="combobox-options" :key="item.id" v-for="item in listFiltered" v-on:click="optionSelected(item)">{{item.text}}</p>
        </div>
        </template>
  </div>
</template>
<script>
import clickOutside from '../directive';
export default {
  data () {
    return {
      listFiltered: [],
      searchText: '',
      showAutocomplete: false
    }
  },
  props: {
    name: { type: String, required: true },
    options: Array,
    label: String,
    isDisabled: { type: Boolean, default: false },
    selectedOption: Object,
    placeholder: String
  },
  methods: {
    filter (word, array) {
      if (word === undefined) {
        this.showAutocomplete = false
        this.listFiltered = []
        return
      }
      this.showAutocomplete = true
      this.listFiltered = array.filter(function (item) {
        return item.text.toLowerCase().includes(word.toLowerCase())
      })
    },
    optionSelected (item) {
      this.searchText = item.text
      this.showAutocomplete = false
      if (item !== undefined) {
        this.$emit('change', { name: this.name, item: item })
      }
    },
    showAllOptions () {
      this.listFiltered = this.options
      this.showAutocomplete = !this.showAutocomplete
    },
    hide () {
      this.showAutocomplete = false
    }
  }
}
</script>
<style>
.form-group{
    position:relative;
}
.form-group input{
    width:100%;
}

input.combo-box-control:active{
  border-style: solid;
  border-width: 1px 1px 0 1px;
  border-radius: 5px 5px 0px 0px;
  border-color: #96c8da;
}

.form-control.combo-box-control:focus{
  border-color: #96c8da;
}

.combobox-list{
    position:relative;
    height:154px;
    width:100%;
    background-color:white;
    overflow-y:auto;
    text-align:justify;
    z-index: 5;
    border-style: solid;
    border-color: #96c8da;
    border-width: 0 1px 1px 1px;
    border-radius: 0px 0px 5px 5px;
    top: -3px;
}

.combobox-options{
    padding:6px 0;
    margin:0;
}
.combobox-options:hover{
    background-color:#d9d9d9;
}
</style>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...