Skip to content

Commit 9e34a0f

Browse files
committed
fix(cdk/drag-drop): error if preview dimensions are accessed too early (#24498)
Fixes a null pointer exception that can happen if the page is scrolled before the user has scrolled the minimum distance. The problem was that we were expecting for the dimensions to be defined by the time the user has had the chance to scroll, but in some cases that can be circumvented. These changes move the measurement to a getter method so that they're guaranteed to be available when they are requested. Fixes #24497. (cherry picked from commit 37be099)
1 parent 046022f commit 9e34a0f

File tree

1 file changed

+15
-13
lines changed

1 file changed

+15
-13
lines changed

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

+15-13
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ export class DragRef<T = any> {
240240
/** Whether the native dragging interactions have been enabled on the root element. */
241241
private _nativeInteractionsEnabled = true;
242242

243-
/** Cached dimensions of the preview element. */
243+
/** Cached dimensions of the preview element. Should be read via `_getPreviewRect`. */
244244
private _previewRect?: ClientRect;
245245

246246
/** Cached dimensions of the boundary element. */
@@ -680,15 +680,6 @@ export class DragRef<T = any> {
680680
return;
681681
}
682682

683-
// We only need the preview dimensions if we have a boundary element.
684-
if (this._boundaryElement) {
685-
// Cache the preview element rect if we haven't cached it already or if
686-
// we cached it too early before the element dimensions were computed.
687-
if (!this._previewRect || (!this._previewRect.width && !this._previewRect.height)) {
688-
this._previewRect = (this._preview || this._rootElement).getBoundingClientRect();
689-
}
690-
}
691-
692683
// We prevent the default action down here so that we know that dragging has started. This is
693684
// important for touch devices where doing this too early can unnecessarily block scrolling,
694685
// if there's a dragging delay.
@@ -1238,11 +1229,11 @@ export class DragRef<T = any> {
12381229
if (this._boundaryRect) {
12391230
const {x: pickupX, y: pickupY} = this._pickupPositionInElement;
12401231
const boundaryRect = this._boundaryRect;
1241-
const previewRect = this._previewRect!;
1232+
const {width: previewWidth, height: previewHeight} = this._getPreviewRect();
12421233
const minY = boundaryRect.top + pickupY;
1243-
const maxY = boundaryRect.bottom - (previewRect.height - pickupY);
1234+
const maxY = boundaryRect.bottom - (previewHeight - pickupY);
12441235
const minX = boundaryRect.left + pickupX;
1245-
const maxX = boundaryRect.right - (previewRect.width - pickupX);
1236+
const maxX = boundaryRect.right - (previewWidth - pickupX);
12461237

12471238
x = clamp(x, minX, maxX);
12481239
y = clamp(y, minY, maxY);
@@ -1510,6 +1501,17 @@ export class DragRef<T = any> {
15101501

15111502
return coerceElement(previewContainer);
15121503
}
1504+
1505+
/** Lazily resolves and returns the dimensions of the preview. */
1506+
private _getPreviewRect(): ClientRect {
1507+
// Cache the preview element rect if we haven't cached it already or if
1508+
// we cached it too early before the element dimensions were computed.
1509+
if (!this._previewRect || (!this._previewRect.width && !this._previewRect.height)) {
1510+
this._previewRect = (this._preview || this._rootElement).getBoundingClientRect();
1511+
}
1512+
1513+
return this._previewRect;
1514+
}
15131515
}
15141516

15151517
/**

0 commit comments

Comments
 (0)