9
Vue.directive('login-to-click', {
  bind (el) {
    const clickHandler = (event) => {
      event.preventDefault()
      event.stopImmediatePropagation()
      alert('click')
    }
    el.addEventListener('click', clickHandler, true)
  }
})

usage

<button @click="handleClick" v-login-to-click>CLICK</button>

handleClick is always triggered. How I can prevent that from directive? Tried with/without addEventListener "capture" flag without any luck.


For now I ended up with following solution:

Vue.prototype.$checkAuth = function (handler, ...args) {
  const isLoggedIn = store.getters['session/isLoggedIn']
  if (isLoggedIn) {
    return handler.apply(this, args)
  } else {
    router.push('/login')
  }
}

And then in component

<button @click="$checkAuth(handleClick)">CLICK</button>

4 Answers 4

8

From my understanding those are two different event handlers, you are only preventing the default event of the one bound in the directive, this has no influence on @click however, because you are not overwriting the click listener but adding a second one. If you want the default of your @click binding to be prevented you can use @click.prevent="handleClick".

I don't think there's any way to do it from the directive, since you explicitly add another listener by binding @click to the button.

Sign up to request clarification or add additional context in comments.

2 Comments

My use case is very simple: in my app I have many buttons (follow/like/add to watchlist/block etc) that require user to be logged in to click on them. I wanted to add v-login-to-click directive that would prevent any clicks on given element/component if user is not logged in (Instead I would redirect user to login page or show login modal).
Well like I said, you can't overwrite the clicklistener that you bind with @click from a directive. The only thing you can do is use click.prevent everywhere you use both the directive and @click or (beware: hacky) use the el reference in the directive and remove all other click listeners. I don't really think 2) is an option because it'll literally remove the handleClick in your case.
4

in my app I have many buttons (follow/like/add to watchlist/block etc) that require user to be logged in to click on them

As with many things in Vue 2, this is a bad use case for a directive, but a very good use case for a component.

Here is a button that is only clickable when the user is authorized.

console.clear()

const AuthenticatedButton = {
  props:["onAuth", "onNonAuth", "disable", "auth"],
  template: `
    <button @click="onClick" 
            :disabled="disableWhenNotAuthorized">
      <slot>{{this.auth}}</slot>
    </button>`,
  computed:{
    disableWhenNotAuthorized(){
      return this.disable && !this.auth
    }
  },
  methods:{
    onClick(){
      if (this.auth && this.onAuth) this.onAuth()
      if (!this.auth && this.onNonAuth) this.onNonAuth()      
    }
  }
}

new Vue({
  el:"#app",
  data:{
    loggedIn: false
  },
  methods:{
    onClick(){
      alert("User is authenticated")
    },
    notAuthorized(){
      alert("You are not authorized.")
    }
  },
  components:{
    "auth-btn": AuthenticatedButton
  }
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <h3>User is {{!loggedIn ? 'Not Authorized' : 'Authorized'}}</h3>
  <auth-btn :auth="loggedIn" 
            :on-auth="onClick"
            :on-non-auth="notAuthorized">
    Executes non auth handler when not authorized
  </auth-btn> <br>
  <auth-btn :auth="loggedIn" 
            :on-auth="onClick"
            :disable="true">
    Disabled when not authorized
  </auth-btn> <br><br>
  <button @click="loggedIn = true">Authenicate User</button>
</div>

With this button you can set an authorized handler and a non-authorized handler. Additionally, you can just disable the button if the user is not authorized.

In this component the authorized state is passed in through a property, but if you were using some form of state management (like Vuex) you could just as easily use that instead.

4 Comments

This is a good example, but suppose that one of the components is not a button, but a link or image. Then I would have to prepare sich wrapper for each of them.
@user606521 This literally took me about 10 minutes to write. Everything except the template could be a mixin if needed. Your concern is valid, but feels like premature optimization.
For now I ended up with another solution (please see my edits in question). I just want to keep code clean and as small as possible - I am working on a project with 100+ different pages. I am not sure if my solution is not little bit "hacky" but it works well.
@user606521 Aboslutely, go with what works best for you :) I primarily wanted to point out the main vector for re-use in Vue 2 is components. Another way to handle this might be higher order components which is essentially a component that wraps any other component returning a new one. In this case, an "authorized" HOC could potentially be applied to any other component. This is something much more common in React but is making headway in Vue.
1

https://v2.vuejs.org/v2/guide/events.html#Event-Modifiers

There are a couple modifiers. .prevent is what I was looking for

<a href="#" @click.prevent="sendMessage('just one click');">

Comments

-2

You can use:

event.target.preventDefault();

I am not sure why would want to do that though, since you are adding a click listener for a reason.

Perhaps you could clarify your question a bit more.

2 Comments

event.target.preventDefault is undefined.
So what does your event actually show when you log it?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.