Skip to content

Commit 9509ad5

Browse files
authored
refactor(cdk/drag-drop): Remove use of zone onStable (#28681)
* refactor(cdk/drag-drop): Remove use of zone onStable * test: fix tests
1 parent e10a07d commit 9509ad5

File tree

2 files changed

+31
-19
lines changed

2 files changed

+31
-19
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
ChangeDetectionStrategy,
1313
Component,
1414
ElementRef,
15+
ErrorHandler,
1516
Input,
1617
NgZone,
1718
Provider,
@@ -1166,12 +1167,18 @@ describe('CdkDrag', () => {
11661167
expect(Math.floor(elementRect.left)).toBe(50);
11671168
}));
11681169

1169-
it('should throw if drag item is attached to an ng-container', fakeAsync(() => {
1170-
expect(() => {
1171-
createComponent(DraggableOnNgContainer).detectChanges();
1172-
flush();
1173-
}).toThrowError(/^cdkDrag must be attached to an element node/);
1174-
}));
1170+
it('should throw if drag item is attached to an ng-container', () => {
1171+
const errorHandler = jasmine.createSpyObj(['handleError']);
1172+
createComponent(DraggableOnNgContainer, [
1173+
{
1174+
provide: ErrorHandler,
1175+
useValue: errorHandler,
1176+
},
1177+
]).detectChanges();
1178+
expect(errorHandler.handleError.calls.mostRecent().args[0].message).toMatch(
1179+
/^cdkDrag must be attached to an element node/,
1180+
);
1181+
});
11751182

11761183
it('should cancel drag if the mouse moves before the delay is elapsed', fakeAsync(() => {
11771184
// We can't use Jasmine's `clock` because Zone.js interferes with it.

src/cdk/drag-drop/directives/drag.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import {Directionality} from '@angular/cdk/bidi';
1010
import {DOCUMENT} from '@angular/common';
1111
import {
12-
AfterViewInit,
1312
Directive,
1413
ElementRef,
1514
EventEmitter,
@@ -27,6 +26,10 @@ import {
2726
Self,
2827
InjectionToken,
2928
booleanAttribute,
29+
afterNextRender,
30+
AfterViewInit,
31+
inject,
32+
Injector,
3033
} from '@angular/core';
3134
import {coerceElement, coerceNumberProperty} from '@angular/cdk/coercion';
3235
import {BehaviorSubject, Observable, Observer, Subject, merge} from 'rxjs';
@@ -207,6 +210,8 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
207210
},
208211
);
209212

213+
private _injector = inject(Injector);
214+
210215
constructor(
211216
/** Element that the draggable is attached to. */
212217
public element: ElementRef<HTMLElement>,
@@ -296,35 +301,35 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
296301
}
297302

298303
ngAfterViewInit() {
299-
// Normally this isn't in the zone, but it can cause major performance regressions for apps
300-
// using `zone-patch-rxjs` because it'll trigger a change detection when it unsubscribes.
301-
this._ngZone.runOutsideAngular(() => {
302-
// We need to wait for the zone to stabilize, in order for the reference
303-
// element to be in the proper place in the DOM. This is mostly relevant
304-
// for draggable elements inside portals since they get stamped out in
305-
// their original DOM position and then they get transferred to the portal.
306-
this._ngZone.onStable.pipe(take(1), takeUntil(this._destroyed)).subscribe(() => {
304+
// We need to wait until after render, in order for the reference
305+
// element to be in the proper place in the DOM. This is mostly relevant
306+
// for draggable elements inside portals since they get stamped out in
307+
// their original DOM position, and then they get transferred to the portal.
308+
afterNextRender(
309+
() => {
307310
this._updateRootElement();
308311
this._setupHandlesListener();
309312

310313
if (this.freeDragPosition) {
311314
this._dragRef.setFreeDragPosition(this.freeDragPosition);
312315
}
313-
});
314-
});
316+
},
317+
{injector: this._injector},
318+
);
315319
}
316320

317321
ngOnChanges(changes: SimpleChanges) {
318322
const rootSelectorChange = changes['rootElementSelector'];
319323
const positionChange = changes['freeDragPosition'];
320324

321325
// We don't have to react to the first change since it's being
322-
// handled in `ngAfterViewInit` where it needs to be deferred.
326+
// handled in the `afterNextRender` queued up in the constructor.
323327
if (rootSelectorChange && !rootSelectorChange.firstChange) {
324328
this._updateRootElement();
325329
}
326330

327-
// Skip the first change since it's being handled in `ngAfterViewInit`.
331+
// Skip the first change since it's being handled in the `afterNextRender` queued up in the
332+
// constructor.
328333
if (positionChange && !positionChange.firstChange && this.freeDragPosition) {
329334
this._dragRef.setFreeDragPosition(this.freeDragPosition);
330335
}

0 commit comments

Comments
 (0)