I'm working on a vuejs component in which if you click outside of the combobox after you clicked on it, it's supposed to close.
My problem it's that the custom directive it's no working, the program compiles but in the browser i get this error:
[Vue warn]: Error in directive click-outside bind hook: "TypeError: Cannot set property 'event' of undefined"
This is the code of my component:
<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>
and the 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)
}
})
the problem is in the line this.event = function (event) { because the error says Cannot set property 'event' of undefined, so this is undefined.
Not sure how to define this, inside the directive.
I'm using this example to make it work with my custom component: https://jsfiddle.net/Linusborg/yzm8t8jq/ Did i missed something? Update: Turns out the code from the example is from a vuejs 1.x, although i found a similar example in vuejs 2.1 https://jsfiddle.net/y0rpfecd/, still get the same error.
You may have found answer to it. Hope somebody find it useful.
I recently tested click-outside directive in my side project. Just replace this.event with window.event in directive code. And it works perfect!!
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)
},
});
To use in component: Just import directive.
<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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With