Skip to content

Commit ec77513

Browse files
crisbetojelbourn
authored andcommitted
fix(drag-drop): disabled state not synced on data binding changes (#17330)
Usually we only sync up the drop list ref with the directive right before dragging starts, however if the disabled state is set, it can propagate to the child directives and prevent the user from ever starting a search again. These changes make it so whenever the disable state is set it'll get sync immediately. Also changes a misleading method name. Fixes #17325.
1 parent 6c4122c commit ec77513

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3409,6 +3409,36 @@ describe('CdkDrag', () => {
34093409
'Expected outer list to not be dragging.');
34103410
}));
34113411

3412+
it('should be able to re-enable a disabled drop list', fakeAsync(() => {
3413+
const fixture = createComponent(DraggableInDropZone);
3414+
fixture.detectChanges();
3415+
const dragItems = fixture.componentInstance.dragItems;
3416+
const tryDrag = () => {
3417+
const firstItem = dragItems.first;
3418+
const thirdItemRect = dragItems.toArray()[2].element.nativeElement.getBoundingClientRect();
3419+
dragElementViaMouse(fixture, firstItem.element.nativeElement,
3420+
thirdItemRect.left + 1, thirdItemRect.top + 1);
3421+
flush();
3422+
fixture.detectChanges();
3423+
};
3424+
3425+
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
3426+
.toEqual(['Zero', 'One', 'Two', 'Three']);
3427+
3428+
fixture.componentInstance.dropInstance.disabled = true;
3429+
fixture.detectChanges();
3430+
tryDrag();
3431+
3432+
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
3433+
.toEqual(['Zero', 'One', 'Two', 'Three']);
3434+
3435+
fixture.componentInstance.dropInstance.disabled = false;
3436+
fixture.detectChanges();
3437+
tryDrag();
3438+
3439+
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
3440+
.toEqual(['One', 'Two', 'Zero', 'Three']);
3441+
}));
34123442
});
34133443

34143444
describe('in a connected drop container', () => {

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
100100
return this._disabled || (!!this._group && this._group.disabled);
101101
}
102102
set disabled(value: boolean) {
103-
this._disabled = coerceBooleanProperty(value);
103+
// Usually we sync the directive and ref state right before dragging starts, in order to have
104+
// a single point of failure and to avoid having to use setters for everything. `disabled` is
105+
// a special case, because it can prevent the `beforeStarted` event from firing, which can lock
106+
// the user in a disabled state, so we also need to sync it as it's being set.
107+
this._dropListRef.disabled = this._disabled = coerceBooleanProperty(value);
104108
}
105109
private _disabled = false;
106110

@@ -151,7 +155,7 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
151155
return this.enterPredicate(drag.data, drop.data);
152156
};
153157

154-
this._syncInputs(this._dropListRef);
158+
this._setupInputSyncSubscription(this._dropListRef);
155159
this._handleEvents(this._dropListRef);
156160
CdkDropList._dropLists.push(this);
157161

@@ -249,7 +253,7 @@ export class CdkDropList<T = any> implements AfterContentInit, OnDestroy {
249253
}
250254

251255
/** Syncs the inputs of the CdkDropList with the options of the underlying DropListRef. */
252-
private _syncInputs(ref: DropListRef<CdkDropList>) {
256+
private _setupInputSyncSubscription(ref: DropListRef<CdkDropList>) {
253257
if (this._dir) {
254258
this._dir.change
255259
.pipe(startWith(this._dir.value), takeUntil(this._destroyed))

0 commit comments

Comments
 (0)