Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional event binding - vuejs

Tags:

events

vue.js

In my scenario I have mouseover and mouseout events that I want to bind to conditionally (e.g only if user is on a device that has a mouse).

I realize I can have the condition in the event handler itself but that would still be allocating the memory for the event handlers which is unnecessary.

Is there a way to make the event binding itself conditional?

(to be clear, what I'd like is to be able to short-circuit the event subscription so the underlying addEventListener operation never happens if the condition is false)

like image 334
asi Avatar asked Dec 31 '17 12:12

asi


People also ask

How do you use if condition in Vue?

The directive v-if is used to conditionally render a block. The block will only be rendered if the directive's expression returns a truthy value.

How do you use V-if and V else?

To conditionally render something in Vue, you should use v-if and v-else directives. Simply pass an expression to the v-if directive, and the block will render if the expression is true. You can also use v-else , which will render if the preceeding v-if expression evaluates to a falsy value.

Does V-if destroy component?

v-if is “real” conditional rendering because it ensures that event listeners and child components inside the conditional block are properly destroyed and re-created during toggles.

What is the difference between V-if and V-show?

The key difference is that v-if conditionally renders elements and v-show conditionally displayselements. This means that v-if will actually destroy and recreate elements when the conditional is toggled. Meanwhile, v-show will always keep the element in the DOM and will only toggle its display by changing its CSS.


2 Answers

Update (February 2021)

Several solutions seem to be accepted on Github, whereas my original answer is not. I'm rounding them up here for convenience:

Solution 1a (see Engin Yapici's answer below):

v-on="enableClick ? { click: clickHandler } : {}" 

Solution 1b (see Heals Legodi's answer below):

v-on="enableClick ? { click: () => clickHandler(params) } : {}" 

Solution 2a (see rvy's answer below and this working demo)

@[eventName]="clickHandler" 

Solution 2b (from coyotte508's comment; 2a without the computed property):

@[isClickable&&`click`]="clickHandler" 

Solution 3 (mentioned here; seems to be compatible with event modifiers):

@click="enableClick && clickHandler" 

Original answer

This works as of Vue 2.6:

<div   @mouseover="enableMouseover ? mouseoverHandler : null"   @click="enableClick ? clickHandler : null"   ... > 

While an event resolves to null, the binding will be removed.

https://github.com/vuejs/vue/issues/7349#issuecomment-458405684

Comments on original answer

It seems to be something I came up with accidentally by misunderstanding that Github thread. But I know from my own testing that it definitely worked back when I posted it. And people continued to upvote it right up to when I made this edit, implying the solution still had some merit. So give it a try if you like.

But after a downvote and a couple of negative comments, I felt the need to improve my answer to save future readers potential headaches. Note that pretty much all the answers to this question refer to the same Github thread, so if you've got time, you might want to go right to the source to learn more. There are many options discussed there, and I suspect more will continue to be posted over time.

like image 60
MarredCheese Avatar answered Sep 20 '22 09:09

MarredCheese


Following this discussion it appears the best way to achieve this is to bind v-on to a specification object containing the events you are interested in subscribing to and place your conditionals there like so:

<div v-on="{ mouseover: condition ? handler : null, click: ... }">

Some notes:

  • Passing null for a handler means the underlying addEventLisetener will not happen - which is what we want
  • This means grouping all the event subscriptions into one v-on attribute rather then splitting it into separate and explicit bindings (<div @mouseover='...' @click='...'/>)

  • If this is a long living component and the underlying data changes frequently (leading to rebinding) you should be paying attention to the disposal of the subscriptions (i.e the corresponding removeEventListener) as subscriptions made in one bind pass will not be disposed of on subsequent ones. Evaluate as per your use case...

like image 45
asi Avatar answered Sep 18 '22 09:09

asi