Skip to content

Programatic usage via exported functions or/and mixin  #220

@renatodeleao

Description

@renatodeleao

Hey @ndelvalle, first many things for the work on this package 🙏
I've came across a different usecase for v-click-outside and would like to make a suggestion.

Context

So i'm a big fan of renderless components and their moto: all the functionality layer; no presentational conditions. Till vue3 with the composition api came out, this was IMO, the best way of creating truly flexible plugins.
One of the caveats of these components (among other irrelevant things for now) is that we can't pass a directive up, via slot-scope, meaning that we can't "transparently" apply v-click-outside, via slotScope bindings, to scoped slot element.

<some-renderless-component #default="slotScope">
   <div v-bind="slotScope.props" v-on="slotScope.listeners">
   A truly transparent renderless slot element. As a consumer i don't need to know what particular
   bindings/listeners are being attached, those are implementation details from this perspective.
  </div>
</some-renderless-component>

To workaround this, is usually suggested that consumers import the directive at the parent and apply the directive themselves, to slot element and using slotScope.someClickOutsideCallback as argument to mutate the renderless component inner state. I mean that works great, but isn't that also the equivalent to say to a McDonalds customer:

"here's your big mac! But please don't forget to add your burger in the middle before eating, since it's served on that separate side box".

That would be weird right? (actually proud of this analogy).

Possible solution

To workaround this, on this particular renderless dropdown that i'm building, i'm shamelessly implementing a copy cat function of your clickedInside detection script and manually toggling document events. Here's the pseudo-code gist of it.

// some renderless dropdown
methods: {
  open() {
     this.getRefElementForClickOutside()
     this.addClickOutsideListeners() // with your setTimeout great finding (i'm a great at the copy business)
  }

  close() {
    this.removeClickOutsideListeners()
  }

  beforeDestroy() {
   this.removeClickOutsideListeners()
  }
}

This works, but then it strike me, why copy if you can use the original source? so i've build this into your package:

And now i can programatically use it instead of the regular declarative way:

import { bind, unbind } from 'v-click-outside'
// ...
methods: {
 open() {
    this._el = this.getRefElementForClickOutside()
    bind(this._el, { value: this.onClickOutside })
 }

 close() {
    unbind(this._el)
 }

 beforeDestroy() {
   unbind(this._el)
 }
}

I've also created a mixin that serializes the vue directive’s bindingValue object format by assigning the argument to the value key, but i know mixins are a controversial subject, even if namespaced, so it's completely optional. We can also bake this serialization into the exported bind function by wrapping the original into into another function with the same mechanism.

Conclusion

I have tested this implementation on my dropdown component, with your examples and in the sandbox above mentioned. Since you're in the click outside business for way longer than me, i want to ask you if i'm being too naive with something or if you think this is valuable feature for v-click-outside users, for whom i would happily create the PR.

Thanks for your time ☮️

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions