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