Skip to content

Proposal: declaring rate-limited event handlers (debounce, throttle) #1773

Closed

Description

@jelbourn

It's very common to need to rate-limit DOM events that fire too quickly, such as scroll or mousemove for performance reasons. In the ES5 world, it is very easy to create this kind of handler. For example, if you were using underscore:

<widget (movemove)="checkHoverPosition($event)">
Widget.prototype.checkHoverPosition = _.debounce(function(event) {
  // ...
}, 300);

However, in ES6 / TypeScript, the class syntax does not have a way to define a method based on the output of some function like this. You can still use the ES5 syntax, but that totally kills the hip vibe. Not to mention that this doesn't work for Dart at all.

We could solve this by introducing rate-limiters (or any of kind of similar function) for event handlers into Angular itself with a syntax something like:

<widget (movemove)="checkHoverPosition($event) | debounce:300">
<widget (movemove)="checkHoverPosition($event) | throttle:300">

These rate-limiters are needed by the material components, so it's only a question of whether Angular will support them directly or if the components have to roll our own rate limiting.

Activity

  1. mhevery commented on May 13, 2015

    @mhevery
    Contributor

    @vsavkin Do you have an opinion?

    I think this should not be the concern of angular but rather of some third party plugin, as there could be lots of different ways you may want to limit this, and I would rather not get into this business. Also | is already used for pipe syntax so it can't be reused here.

  2. vsavkin commented on May 13, 2015

    @vsavkin
    Contributor

    Since there are few events that need to be handled this way (those are not really events, but rather continuous behaviors), I would prefer not to bake this mechanism into the framework unless we have to. Imo it should be done on a lower-level.

  3. matanlurey commented on May 14, 2015

    @matanlurey
    Contributor

    Jeremy, couldn't you create a function at runtime in ES6, e.g.?

    @View({
      template: '<widget (movemove)="onMouseMove($event)">'
    })
    class Widget {
      onMouseMove: Function;
    
      constructor() {
        this.onMouseMove = _.debounce(this.checkHoverPosition, 300);
      }
    
      checkHoverPosition(event) {
        ...
      }
    }
  4. jelbourn commented on May 14, 2015

    @jelbourn
    ContributorAuthor

    The method has to be on the prototype, otherwise you're creating a copy of it for every component instance.

  5. PatrickJS commented on May 14, 2015

    @PatrickJS
    Contributor

    this is why decorators are awesome

    @Component({ selector: 'widget' })
    @View({
      template: '<div (movemove)="onMouseMove($event)"></div>'
    })
    class Widget {
      onMouseMove: Function;
    
      @debounce(300)
      onMouseMove(event: Event) {
        //...
      }
    }
  6. tbosch commented on May 14, 2015

    @tbosch
    Contributor

    The only problem are zones. We don't want to trigger dirty checking when
    the denouncing decides to ignore the event.

    doing denouncing inside of an event handler in a directive or component
    will always trigger change detection, as our event handlers run inside the
    Angular zone.

    So I think we need to think about this a little more...

    On Thu, May 14, 2015 at 7:27 AM PatrickJS notifications@github.com wrote:

    this is why decorators are awesome

    @view({
    template: '<widget (movemove)="onMouseMove($event)">'
    })class Widget {
    onMouseMove: Function

    ;

    constructor() {
    this.onMouseMove = this.checkHoverPosition;
    }

    @debounce(300)
    checkHoverPosition(event) {
    //...
    }
    }

    —
    Reply to this email directly or view it on GitHub
    #1773 (comment).

  7. jelbourn commented on May 14, 2015

    @jelbourn
    ContributorAuthor

    @tbosch that's a very good point

    @vsavkin this kind of rate limiting is very common. Looking at the current material design components, some uses:

    • Text entry into the autocomplete
    • Text entry into md-input (for auto-resizing a textarea element)
    • Window resize events for several components (such as drawing ticks for a slider)
    • Scrolling events for things (headers, etc) that are "stickied"
    • For parent components doing something based on the addition or removal of a child (such as a re-layout)
  8. vicb commented on May 14, 2015

    @vicb
    Contributor

    If the implementation has not changed the event handler actually runs in the outer zone.

    In the outer zone, we test if the event target is our actual element. Only when they match, we execute code in the angular zone which triggers the CD.

    May be we can generalize the target = actual test with some more generic predicate support that would also work w/ debounce ?

  9. 33 remaining items

  10. baio commented on Jul 2, 2016

    @baio

    Hey guys any progress on this ?

  11. PatrickJS commented on Jul 2, 2016

    @PatrickJS
    Contributor

    @baio if you want this I can create a module for angular decorators that run outside of zone.js

  12. baio commented on Jul 2, 2016

    @baio

    @gdi2290 Hi Patrick, thanks but I basically want to try implement it by myself, if I will have troubles I would ask. If I use ChangeDetection.OnPush and Observables, I shouldn't worry about zoned change detections, am I correct ?

  13. baio commented on Jul 3, 2016

    @baio
  14. added
    area: coreIssues related to the framework runtime
    and removed
    area: coreIssues related to the framework runtime
    on Sep 7, 2016
  15. stalniy commented on Sep 26, 2016

    @stalniy

    The most expected implementation would be to use EVENT_MANAGER_PLUGINS

  16. markcooper commented on Nov 14, 2016

    @markcooper

    Please do this! This is a feature that is needed quite often

  17. andrevcf commented on Jan 26, 2017

    @andrevcf

    +1

  18. mhevery commented on Jan 28, 2017

    @mhevery
    Contributor

    Closing in favor of #13248, which will support this and many other use cases.

  19. angular-automatic-lock-bot commented on Sep 10, 2019

    @angular-automatic-lock-bot

    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.

  20. locked and limited conversation to collaborators on Sep 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions