Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditionally attach event listener and handler to Vue component

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?

like image 387
tx291 Avatar asked Oct 28 '25 03:10

tx291


1 Answers

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:

  1. The expression is wrapped in an anonymous function, which becomes the event handler.
  2. The result of the expression is either a method name (as opposed to a method call) or null. Evaluating a method name is effectively a no-op.

Solution 1: Change method name into method call

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()" />

Solution 2: Use dynamic event

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>
like image 88
tony19 Avatar answered Oct 30 '25 08:10

tony19



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!