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

@HostListener('touchmove') should behave like @HostListener('mousemove') #10834

Closed
dharders opened this issue Aug 16, 2016 · 8 comments
Closed
Labels
area: core Issues related to the framework runtime core: event listeners feature: insufficient votes Label to add when the not a sufficient number of votes or comments from unique authors feature: votes required Feature request which is currently still in the voting phase feature Issue that requests a new feature freq1: low P4 A relatively minor issue that is not relevant to core functions
Milestone

Comments

@dharders
Copy link

dharders commented Aug 16, 2016

I'm submitting a ...

[x] feature request

Current behavior

This feature request is inspired by #9864 and #8035.

For the most part @HostListener('touchmove') does behave like @HostListener('mousemove') in regards to event propagation to the decorated callback function. However, when a touchmove is underway and the original DOM element that initiated the touchstart is removed by Angular (eg NgFor update) then the touchmove events stop firing. This is a bad experience for mobile users.

This issue arises from browser implementation of the Touch Spec. TouchEvents do actually continue to fire after DOM removal BUT they are only targeted at the node/element that the original touchstart occurred on and don't bubble (unlike MouseEvents, which is confusing to the user/developer!).

So, we cant perform a simple @HostListener('touchmove', ['$event']) and expect it to work like @HostListener('mousemove', ['$event']) as DOM removal changes the touch event propagation (and since the host event listener is attached to the outer component element the touchmove event never reaches our host listener). The solution is to dynamically add event listeners to the event.target element of the touchstart event as they occur. Then perform cleanup on touchend or touchcancel (or ngOnDestroy()).

Since Angular is a Framework, it should take care of these browser quirks for us. Or at the very least document it. My company lost countless hours trying to get to the bottom of this quirk and I'd like to save others the same.

Our current workaround/hack:

@HostListener('touchstart', ['$event'])
@HostListener('mousedown', ['$event'])
  onStart(event) {
    if (event.touches) {                      // only for touch
      this.removePreviousTouchListeners();    // avoid mem leaks      
      this.touchmoveListenFunc = this.renderer.listen(event.target, 'touchmove', (e) => { this.onMove(e); });
      this.touchendListenFunc = this.renderer.listen(event.target, 'touchend', (e) => { this.removePreviousTouchListeners(); this.onEnd(e); });
      this.touchcancelListenFunc = this.renderer.listen(event.target, 'touchcancel', (e) => { this.removePreviousTouchListeners(); this.onEnd(e); });
    }
   ...
}

 @HostListener('mousemove', ['$event'])
  // @HostListener('touchmove', ['$event'])    // don't declare this, as it is added dynamically
  onMove(event) {
    ...   // do stuff with event
  }

@HostListener('mouseup', ['$event'])
  // @HostListener('touchend', ['$event'])     // don't use these as they are added dynamically
  // @HostListener('touchcancel', ['$event']) // don't use these as they are added dynamically
  onEnd(event) {
    ...  // do stuff
  }

 ngOnDestroy() {
   this.removePreviousTouchListeners();
 }

 removePreviousTouchListeners() {
    if (this.touchmoveListenFunc !== null)
      this.touchmoveListenFunc();             // remove previous listener
    if (this.touchendListenFunc !== null)
      this.touchendListenFunc();              // remove previous listener
    if (this.touchcancelListenFunc !== null)
      this.touchcancelListenFunc();           // remove previous listener

    this.touchmoveListenFunc = null;
    this.touchendListenFunc = null;
    this.touchcancelListenFunc = null;
  }

Don't forget to inject Renderer in the constructor (import from @angular/core)

Expected/desired behavior

@HostListener('touchmove') should behave like @HostListener('mousemove') without requiring the above hacks. Angular Framework should be responsible for ensuring the touchevents reach the host listener (as MouseEvents do)

Reproduction of the problem

Without Workaround http://plnkr.co/edit/QR6WDzv6NxOmn6LXTngG?p=preview
With Workaround http://plnkr.co/edit/EvmY1a?p=preview

What is the expected behavior?

Instructions are shown in the plunker. In the workaround, touch dragging works as expected

What is the motivation / use case for changing the behavior?

Easier mental model for developers. Consistency between Touch and Mouse events. Framework hide browser quirks.

Use case: Booking system, drag appointment to new day, NgFor updates (and removes DOM node) see plunker.

Please tell us about your environment:

  • Angular version: 2.0.0-rc.5
  • Browser: All
  • Language: All

Further reading:

@mhevery mhevery added area: core Issues related to the framework runtime and removed comp: core labels Sep 7, 2016
@vicb vicb added the feature Issue that requests a new feature label Oct 6, 2016
@DzmitryShylovich
Copy link
Contributor

@dharders can u pls update plunkers?

@DzmitryShylovich
Copy link
Contributor

@pkozlowski-opensource no feedback. can be closed

@dharders
Copy link
Author

Updated plunkers to latest.

Without workaround: http://plnkr.co/edit/pf3izYg3WvqVHvk70gu0?p=preview
With workaround: http://plnkr.co/edit/AewqJipE6yshLfRx6rc7?p=preview

Note: You will need a touchscreen to view the behaviour

cc: @DzmitryShylovich

@Tyler-V
Copy link

Tyler-V commented Mar 22, 2017

@dharders, just spent the better part of yesterday going through the same headache!

I am using a virtual scroll component to display a large dataset with the minimum page size required and updating the visible list of DOM elements on scroll.

Same issue with me, *ngFor fires to update the list and BOOM my touchmove/touchend events never fire, works completely fine with mouse events. Very frustrating, thanks for the workaround will try it out now.

Edit: Worked like a charm, would like to see angular address this however.

@ngbot ngbot bot added this to the Backlog milestone Jan 23, 2018
@SamaraSsr
Copy link

I had the same issue, when i removed TouchEvent interface from below code, it is working fine now
@HostListener('document:touchmove', ['$event']) onTouchMove($event: TouchEvent): void { if (this.isPanning && this.panningEnabled) { const clientX = $event.changedTouches[0].clientX; const clientY = $event.changedTouches[0].clientY; const movementX = clientX - this._touchLastX; const movementY = clientY - this._touchLastY; this._touchLastX = clientX; this._touchLastY = clientY; this.pan(movementX, movementY); } }

@jelbourn jelbourn added P4 A relatively minor issue that is not relevant to core functions and removed severity2: inconvenient labels Oct 1, 2020
@angular-robot
Copy link
Contributor

angular-robot bot commented Jun 4, 2021

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

@angular-robot angular-robot bot added the feature: votes required Feature request which is currently still in the voting phase label Jun 4, 2021
@angular-robot
Copy link
Contributor

angular-robot bot commented Jun 24, 2021

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.

@angular-robot angular-robot bot added the feature: insufficient votes Label to add when the not a sufficient number of votes or comments from unique authors label Jun 24, 2021
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Apr 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: core Issues related to the framework runtime core: event listeners feature: insufficient votes Label to add when the not a sufficient number of votes or comments from unique authors feature: votes required Feature request which is currently still in the voting phase feature Issue that requests a new feature freq1: low P4 A relatively minor issue that is not relevant to core functions
Projects
None yet
Development

No branches or pull requests

10 participants