vuejs / vue-touch

Hammer.js wrapper for Vue.js
MIT License
2.72k stars 391 forks source link

Vue 2.0 and vue-touch#next: no $event property?! #48

Closed rse closed 7 years ago

rse commented 8 years ago

I'm trying vue-touch#next under Vue 2.0 with...

v-touch:swipe="eventListItemSwiped({ itemId: item.id, direction: $event.direction })"
v-touch-options:swipe="{ direction: 'horizontal', threshold: 100 }"

...but it seems the special $event property does not exist for vue-touch events. I always just get a Vue warning: Property or method "$event" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option. (found in root instance).

I know I could do...

v-touch:swipe="eventListItemSwiped"
v-touch-options:swipe="{ direction: 'horizontal', threshold: 100 }"

...and receive the Hammer event as the single argument and then assemble the data structure in the callback as { itemId: $(ev.target).attr("data-id"), direction: event.direction }. But could you please add back the $event property for vue-touch? It would align vue-touch again to the regular Vue event handling where $event is available. Without this the Hammer events have to be handled differently than other types of events.

Or did I missed something and hence $event is not working for me?

rse commented 8 years ago

One more issue with the "next" variant of Vue-Touch: the old "v-touch-options" is no longer available. Just the global options. Can you re-add "v-touch-options" please, too?

LinusBorg commented 8 years ago

Disclaimer: I wrote the update for 2.0 but did not write the initial implementation

Are you sure $event was accessible in the 1.0 version? directives are not events, thefore there's never any $event available afaik.

About v-touch-options: That is unfortunately not as easy as it may sound. Due to the changes to custom directives (in this case: the deprecation of the priority option for custom directives) v-touch-options can't be implemented in the way that it was.

rse commented 8 years ago

No, I'm not sure that $event existed in the 1.0 version. I'm just trying to convert a Vue 2.0 + HammerJS app to Vue 2.0 + vue-touch#next and recognized that the current vue-touch#next does not look sophisticated enough. Independent whether $event is available or not, we have to make the HammerJS event object available to the callback, because only this way one can check things like "direction", etc. Passing it through to the callback via $event would be just the most obvious solution for Vue users as they are used to this from the similar "looking" v-on:xxx, too.

LinusBorg commented 8 years ago

we have to make the HammerJS event object available to the callback, because only this way one can check things like "direction", etc.

The callback function that you pass to v-touch will receive the Hammer Event Object as its (only) argument.

vue-touch="method(arg, $event)" will not work tough, because unlike v-on, a custom directive will evaluate and run this expression immediatly when the directive is rendered, not when Hammer triggers an event later. So at that point, there is no event to pass yet.

But if you want to curry the callback function, you can do something like this:

<a v-touch="curry(myCallback, 'a', 'b')"></a>
methods: {
  curry(fn, ...initialArgs) {
    const newFn = fn.bind(this, ...initialArgs)
    return (...newArgs) => newFn(...newArgs) 
   },
  myCallback(a, b, event) {

  }
}

curry utilities are available via many util libs, e.g. lodash has _.partial(), so you don't have to implement my basic curry() method from above in your component, you can rely on tested libs.

rse commented 8 years ago

Thanks for the hint. Could you perhaps do the following: just always append the event object to the argument list of the callback. So, v-touch="foo" invokes foo(<event>) and v-touch="foo(42, 'bar')" invokes foo(42, 'bar', <event>). Because it is nasty having to provide the curry helper function as a method all the time just to be able to get certain parameters and the event.

LinusBorg commented 8 years ago

Could you perhaps do the following: just always append the event object to the argument list of the callback. So, v-touch="foo" invokes foo(<event>) and v-touch="foo(42, 'bar')" invokes foo(42, 'bar', <event>).

This is not possible with a directive, as I already explained above when I talked about $event:

The expression is already executed when the code of the directive receives it, that means that the expression foo(42, 'bar') would have already called the foo() function with the arguments 42 and 'bar' and the directive would only receive its return value.

This was possible in 1.0 with the acceptStatement option (and a little hack to get $event in scope), but it was deprecated.

I will think about how we can find a better solution.

rse commented 8 years ago

Oh, finally got it! Then vue-touch#next is really stuck, of course. Instead Vue 2.0 would have to be first enhanced to support more flexible implementations of custom directives where the argument is evaluated at the time an event happens instead of at render-time...

rse commented 8 years ago

Please don't be bothered by me, but just one more crazy idea: if a custom Vue 2.0 directive never can have the power of v-on, we perhaps should change the implementation strategy: instead of implementing an own v-touch, how about implementing custom DOM events triggered by HammerJS events, i.e., how about using v-on:swipe="..." and find a way to emulate the custom DOM event swipe with the help of HammerJS? Do you see a chance this could be achieved? If it would be possible, it would have both the advantage of the full power of v-on and let the touch gestures no longer be special cases, too ;-)