James D
James D

Reputation: 2291

angular CDK drag & drop: nested droplist

I'm trying to build a demo app where I can plan students and their desks. When a classroom has too many desks I can put them in another classroom. I can then plan the students on said desks. I'm using cdkDropListGroup because the amount of classrooms is a dynamic.

When I drop a desk with students on the 'not assigned' student list, the desk remains in the class room, but the students drop into the list as expected. However when I try to drop the students on the desks, the predicate, which only allows products to be dropped in the list, doesn't allow them. When I remove the predicate however, the student are dropped in the classrooms list instead of the desks...

Dropping desks with students works properly:

Proper functioning of the app

Dropping students on desks fails:

Failing part

I need help with dropping the students specifically on the desks instead of the classrooms, causing dropStudent to trigger.

I thought raising the container for the desks to a higher Z-Index might help but that didn't work.

references:

Upvotes: 1

Views: 3704

Answers (1)

Poul Kruijt
Poul Kruijt

Reputation: 71911

You need to use the cdkDropListConnectedTo option on the not assigned students lists, and add all instances of the desk lists. That's beacuse the CdkDropListGroup only looks at sibling lists, not at children:

You need to add a template reference to your desk droplist, so you can query this in your component ts, or you can use the a string of IDs. I think the template reference is more versatile, but for the sake of example I'll use the ids:

<div cdkDropList 
      id="{{studentList.listName}}" 
      [cdkDropListData]="studentList.student"
      class="example-list"
      (cdkDropListDropped)="returnStudent($event)"
      [cdkDropListEnterPredicate]="userReturnPredicate"
      [cdkDropListConnectedTo]="desks">
  <div  *ngFor="let person of studentList.student"
        class="example-box" 
        cdkDrag
        [cdkDragData]="person">
    {{person.userName}}
  </div>
</div>

As you can see the cdkDropListConnectedTo has desks binded. In your component you can define desks like this:

desks: string[] = [];

this.desks = this.projectLists.map(
  (list) => list.products.map((product) => product.productName)
).flat(2);

To make it work with ViewChildren, you need something like this in your ngAfterViewInit:

ngAfterViewInit(): void {
  this.deskLists.changes.pipe(
    startWith(true),
    tap(() => {
      this.desks = this.deskLists.toArray();
      this.cd.markForCheck();
      this.cd.detectChanges();
    }),
  ).subscribe();
}

working example

Upvotes: 3

Related Questions