markreyes
markreyes

Reputation: 1289

Save order on page refresh of cdkDrag, cdkDropList Angular Material

Intro

I have a To Do List which leverages Angular Material's cdkDrag directive on elements inside of a cdkDropList. Marking items as 'to-do' or 'done' and vice versa is straightforward.

Problem

There's a level of detail in the user interaction, namely, a page refresh, which results in items not necessarily being in the order in which they were dragged.

Take this example in which a user is assigned 3 tasks: eat breakfast, eat lunch, eat dinner: enter image description here

The user doesn't like that priority and decides to tackle his/her eating assignments in this order instead. The drag and drop interactions take place and the To Do list is finalized as: enter image description here

On page refresh, that's where the gotcha kicks in. It goes back into the original state of:

eat breakfast, eat lunch, eat dinner

When it clearly should be:

eat dinner, eat breakfast, eat lunch

This observation holds true as well for items marked as done.

What is the missing piece I need to acknowledge the current state of this interaction. Do note, this implementation is based on How To Build an App With Drag and Drop With Angular

Thanks for your time.

Upvotes: 1

Views: 4646

Answers (2)

here's an approach I have encountered the same problem. I am creating a kanban board with to manage task and to change its status by drag and drop cdk so here's what I did:

html

Tasks To Do
<div class="example-container">
  <h2 style="text-align: center;">Tasks In Progress</h2>

  <div
    cdkDropList
    [cdkDropListData]="progresslist"
    class="example-list"
    (cdkDropListDropped)="drop($event)">
    <div   (click)=" openDialogAssign(item.taskId,item)" style="background-color: #78C0E0;"class="example-box" *ngFor="let item of progresslist" cdkDrag>        <app-boarditem 
      [item]="item" ></app-boarditem></div>
  </div>
</div>

<div class="example-container">
  <h2 style="text-align: center;">Tasks To Be Tested</h2>

  <div
    cdkDropList
    [cdkDropListData]="testedslist"
    class="example-list"
    (cdkDropListDropped)="drop($event)" >
    <div  (click)=" openDialogAssign(item.taskId,item)" style="background-color: #F8E290;" class="example-box" *ngFor="let item of testedslist" cdkDrag>        <app-boarditem 
      [item]="item" ></app-boarditem></div>
  </div>
</div>
<div class="example-container">
  <h2 style="text-align: center;">Tasks Done</h2>

  <div
    cdkDropList 
    [cdkDropListData]="donelist" 
    class="example-list"
    (cdkDropListDropped)="drop($event)">
    <div  (click)=" openDialogAssign(item.taskId,item)" style="background-color: rgb(173, 233, 173);" class="example-box" *ngFor="let item of donelist" cdkDrag>        <app-boarditem 
      [item]="item" ></app-boarditem></div>
  </div>
</div>

typescript:

drop(event: CdkDragDrop<any[]>) { if (event.previousContainer === event.container) { moveItemInArray( event.container.data, event.previousIndex, event.currentIndex );

} else {
  transferArrayItem(
    event.previousContainer.data,
    event.container.data,
    event.previousIndex,
    event.currentIndex
  );
  // console.log("ahayhayhy",event.container.id);
 console.log( event.container.data[event.currentIndex.valueOf()] );

 
  var taskupdatedStatus:any= event.container.data[event.currentIndex.valueOf()]
    // console.log("dattttttttttta",event.container.data);
    
    
    // x.itemId=index
  switch (event.container.id) {
    case "cdk-drop-list-0":
      {console.log("set to todo");
      
      this.taskservice.changeTaskStatus(taskupdatedStatus.taskId,"todo",taskupdatedStatus).subscribe(res=>{
        console.log("task updated to todo",res);
    console.log(taskupdatedStatus);
        
      })
      
    }
      

      break;
    case "cdk-drop-list-1":
        console.log("set to inprogress");
    console.log(taskupdatedStatus);
    this.taskservice.changeTaskStatus(taskupdatedStatus.taskId,"prog",taskupdatedStatus).subscribe(res=>{
      console.log("task updated to prog",res);
  console.log(taskupdatedStatus);
      
    })

        break;
    case "cdk-drop-list-2":
      console.log("set to test");
    console.log(taskupdatedStatus);
    this.taskservice.changeTaskStatus(taskupdatedStatus.taskId,"test",taskupdatedStatus).subscribe(res=>{
      console.log("task updated to test",res);
  console.log(taskupdatedStatus);
      
    })
      break;
        
    case "cdk-drop-list-3":
      console.log("set to DONE");
    console.log(taskupdatedStatus);
    this.taskservice.changeTaskStatus(taskupdatedStatus.taskId,"done",taskupdatedStatus).subscribe(res=>{
      console.log("task updated to done",res);
  console.log(taskupdatedStatus);
      
    })
      break;
          
                
    default:
      console.log("neiver");
      
      break;
  }
}

}

this is the line that i implemented to capture the element i just dropped so that i can access its attributs

var taskupdatedStatus:any= event.container.data[event.currentIndex.valueOf()]

Upvotes: 0

Eliseo
Eliseo

Reputation: 57999

You need convert you array of "strings" in an array of "object"

Imagine you has an array of items like

[{task:"eat breakfast",order:0},
 {task:"eat lunch",order:1},
 {task:"eat dinner",order:2}
]

In your drop function you need re-calculate the property order. using the typical drop function

drop(event: CdkDragDrop<any[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    } else {
      transferArrayItem(event.previousContainer.data,
                        event.container.data,
                        event.previousIndex,
                        event.currentIndex);
      //if transfer, recalculate the order of previous (the list from drag)
      event.previousContainer.data.forEach((x,index)=>{
          x.order=index
      })
    }
    //always, recalculate the order of the container (the list to drag)
      event.container.data.forEach((x,index)=>{
          x.order=index
      })
  }

Then you need save the data in anyway (I think you can use the ngOnDestroy), when you recived the data don't forget sort by order

NOTE: In this SO there're are a cdkDragList using a formArray (only for sorting) if you want use a FormArray but it's not necesary, just with two arrays must be enougth

Upvotes: 5

Related Questions