Skip to content

bug(drag-drop): Root element pointer down listeners causing excessive change detection #18726

Closed
@Achilles1515

Description

@Achilles1515

Reproduction

https://stackblitz.com/edit/angular-kwisjn?file=src/app/cdk-drag-drop-overview-example.html

The StackBlitz demo above is a slightly modified version of the "Basic Drag&Drop" official example with a change detection counter added and a second draggable box.

Simply clicking (and not even dragging) an element containing the cdkDrag directive causes app-wide change detection to occur. This behavior is exemplified by clicking the second box and witnessing the change detection counter increase.

The first draggable box in the demo has had its root element listeners (mousedown and touchstart) modified to run outsize Angular zone.

drag-ref.ts code:

/**
   * Sets an alternate drag root element. The root element is the element that will be moved as
   * the user is dragging. Passing an alternate root element is useful when trying to enable
   * dragging on an element that you might not have access to.
   */
  withRootElement(rootElement: ElementRef<HTMLElement> | HTMLElement): this {
    const element = coerceElement(rootElement);

    if (element !== this._rootElement) {
      if (this._rootElement) {
        this._removeRootElementListeners(this._rootElement);
      }

      element.addEventListener('mousedown', this._pointerDown, activeEventListenerOptions);
      element.addEventListener('touchstart', this._pointerDown, passiveEventListenerOptions);
      this._initialTransform = undefined;
      this._rootElement = element;
    }

    return this;
  }

Is there any reason these event listeners can't be instantiated outside the Angular zone? Like so:

  withRootElement(rootElement: ElementRef<HTMLElement> | HTMLElement): this {
    const element = coerceElement(rootElement);

    if (element !== this._rootElement) {
      if (this._rootElement) {
        this._removeRootElementListeners(this._rootElement);
      }

      this._ngZone.runOutsideAngular(() => {
        element.addEventListener('mousedown', this._pointerDown, activeEventListenerOptions);
        element.addEventListener('touchstart', this._pointerDown, passiveEventListenerOptions);
      });
      this._initialTransform = undefined;
      this._rootElement = element;
    }

    return this;
  }

Expected Behavior

No change detection occurs when simply clicking a draggable item.

Actual Behavior

Change detection occurs when simply clicking a draggable item.

Environment

  • Angular:
  • CDK/Material: 9.1.1

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions