Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programatic usage via exported functions or/and mixin #220

Closed
renatodeleao opened this issue Aug 21, 2020 · 3 comments · Fixed by #221
Closed

Programatic usage via exported functions or/and mixin #220

renatodeleao opened this issue Aug 21, 2020 · 3 comments · Fixed by #221

Comments

@renatodeleao
Copy link
Collaborator

renatodeleao commented Aug 21, 2020

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 ☮️

@renatodeleao
Copy link
Collaborator Author

Note: I just realised that we can already use this without any changes to the package:

import vClickOutside from 'v-click-outside';
const { bind, unbind } = vClickOutside.directive

So this issue becomes more of question now, to check if there's no downside or if it makes sense to document in readme ✌️

@ndelvalle
Copy link
Owner

Hi @renatodeleao thanks for taking the time and filling this issue and for the great read (Analogies included). I was not aware of this approach you mentioned, if you think it can be useful for other people and it can be used this way then I'm in for adding it to the readme 🙌🏻 PR is most than welcome!

@renatodeleao
Copy link
Collaborator Author

@ndelvalle my pleasure!
Here it is #221

Thanks for your work on this once again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants