I have a Vue component that is used several places in my app. In some of these cases, I need to handle a click event with a specific function, like:
<div @click="checkNav" />
However, I would only like to attach this handler when needed, so it doesn't fire unnecessarily when it's not needed.
I've tried passing a prop to the component and attaching the handler conditionally, like so:
<div @click="isCheckNeeded ? checkNav : null" />
and then in props, I've specified:
isCheckNeeded {
type: Boolean,
required: false,
default: false,
}
However, my checkNav function never fires, and I've double checked that isCheckNeeded is true in Vue devtools.
Is this kind of conditional check not possible, or not recommended? Is there a better way to conditionally attach event listeners/handlers?
It might help to see how your template is being compiled to understand the cause of the problem...
When v-on receives a method name, vue-template-compiler compiles it into a method lookup, where the resolved method becomes the event handler [1]. For instance, your template <div @click="checkNav" /> is compiled into this render function:
with (this) {
return _c("div", { on: { click: checkNav } })
}
On the other hand with an inline event handler, <div @click="isCheckNeeded ? checkNav : null" /> is compiled into this:
with (this) {
return _c("div", {
on: {
click: function ($event) {
isCheckNeeded ? checkNav : null
},
},
})
}
Notice a couple things here:
null. Evaluating a method name is effectively a no-op.This is probably the simplest solution, but it has the disadvantage of the handler always being invoked upon click (although a falsy isCheckNeeded would cause an early return).
<!-- BEFORE: -->
<!--
<div @click="isCheckNeeded ? checkNav : null" />
-->
<!-- AFTER: -->
<div @click="isCheckNeeded ? checkNav() : null" />
<!-- OR: -->
<div @click="isCheckNeeded && checkNav()" />
This is slightly more complex, but it has the advantage of registering the event handler only when necessary. The event handler is automatically unregistered when isCheckNeeded is falsy.
<div @[clickEvent]="checkNav" />
...
<script>
export default {
computed: {
clickEvent() {
return this.isCheckNeeded ? 'click' : null
}
},
}
</script>
Vue.component('my-component', {
template: `<div @[clickEvent]="checkNav"><slot/></div>`,
props: {
isCheckNeeded: Boolean
},
computed: {
clickEvent() {
return this.isCheckNeeded ? 'click' : null
}
},
methods: {
checkNav() {
console.log('checkNav')
}
}
})
new Vue({
el: '#app',
data() {
return {
isCheckNeeded: false
}
}
})
.click-area {
border: solid 1px;
padding: 2rem;
margin: 1rem;
}
<script src="https://unpkg.com/[email protected]"></script>
<div id="app">
<button @click="isCheckNeeded = !isCheckNeeded">Toggle click handler</button>
<pre>isCheckNeeded={{isCheckNeeded}}</pre>
<my-component :is-check-needed="isCheckNeeded">
<div class="click-area">
<span v-if="isCheckNeeded">Click me!</span>
<span v-else>Clicking ignored</span>
</div>
</my-component>
</div>
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