Reputation: 687
I have an object like this:
container = {
row1: [ '1', '2', '3', '4' ],
row2: [ '5', '6', '7', '8' ],
row3: [ '9', '10', '11', '12' ]
}
This data populates a grid drag n drop component in my Angular project.
When an 'item' is moved from one array to the other I would like the last item in that array to move to the next array and push all objects forward by 1 so that I can ensure there is only ever 4 objects in each array.
How can I achieve this? I've been at it for way too many hours now!
I have this 'drop' method that is called when the item is added/dropped into a new array :
drop(event: CdkDragDrop<string[]>) {
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);
}
}
So what I need now is something like :
this.container.data.unshift() //to the next array within the container array.
I also have access to this array in my component so maybe even passing in the row name:
drop(event: CdkDragDrop<string[]>, rowName: string) {
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);
}
}
How can I do this and it would also be nice to be able to reverse it if need be.
Here is a StackBlitz: https://stackblitz.com/edit/angular-cdk-drag-drop-cmyh7k?file=app%2Fcdk-drag-drop-connected-sorting-example.html
Upvotes: 0
Views: 1153
Reputation: 57971
First you need update the references of the stackblitz to get the new version of Angular 8 and cdk. Remove the references to core.js in pollyfill.ts too.
This make that you can drag among the rows
After change the "drop function" as, e.g.
drop(event: CdkDragDrop<string[]>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
const dataFrom=event.previousContainer.data[event.previousIndex]
const dataTo=event.container.data[event.currentIndex]
event.previousContainer.data.splice(event.previousIndex,1,dataTo)
event.container.data.splice(event.currentIndex,1,dataFrom)
}
}
You needn't use obligatory the function "transferArrayItem". you has in event.previousContainer.data the data from and in event.container.Data the data to, so you can "play" using splice to remove one element from another.
In my case I use the indexex, to put interchange the positions, but you can make in the way you want
Tip: it's interesting make a console.log() of event.previousContainer.data, event.container.data, event.previousIndex and event.currentIndex to see the values
Here is your forked stackbliz
Update Another function
drop(event: CdkDragDrop<string[]>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
//get the data we are dragging
const dataFrom=event.previousContainer.data[event.previousIndex]
//get the last element of the row where dropped
const dataTo=event.container.data[event.container.data.length-1]
//remove the element dragged
event.previousContainer.data.splice(event.previousIndex,1)
//Add at last the dataTo
event.previousContainer.data.push(dataTo)
//Add the data dragged
event.container.data.splice(event.currentIndex,0,dataFrom)
//remove the last element
event.container.data.pop()
}
}
Update2 it's looks like some "stanger", So we can think about if using (cdkDropListEntered) (cdkDropListExited) and (cdkDragStarted) we can improve a better effect.
The idea is add one element to each row that will be invisible (display:none) if we are reorden the row or we are not draggind anything. If we are dragging between rows, this element gets the value of the last element of the row where we are dropped.
Puff, first declare three variables
fade=[" "," "," "] //to show the elements
indexDrag:number=-1 //the row from we picked the element
indexEnter:number=-1 //the row where we want dropped the element
After we are goind to add a div before the drag items
<div cdkDropList cdkDropListOrientation="horizontal"
[cdkDropListData]="container.row1" class="example-list"
(cdkDropListDropped)="drop($event)"
(cdkDropListEntered)="enter($event,0)" > //<--for the second row will be enter($event,1)
// and for the third row enter($event,2)
<!-- our element before the drags elements -->
<div class="example-box"
[style.display]="(indexDrag!=0 ||
indexEnter==indexDrag ||
indexEnter<0)?'none':null">
<div class="tile">
<div class="tile-container">{{fade[0]}}</div>
</div>
</div>
<!--our dragabbles elements -->
<div class="example-box" *ngFor="let item of container.row1" cdkDrag
(cdkDragStarted)="startDrag(0)"> //<--for the second row will be startDrag(1)
// and startDrag(2) for the third row
<div class="tile">
<div class="tile-container">{{item}}</div>
</div>
</div>
Then, just add the two functions
startDrag(index) {
this.indexDrag=index;
}
enter(event: CdkDragEnter<any>,index) {
this.indexEnter=index;
const data=event.container.data
this.fade[this.indexDrag]=data[data.length-1]
}
And, in drop function we "restart" the variables indexDrag,indexEnter and fade
drop(event: CdkDragDrop<string[]>) {
this.indexDrag = -1;
this.indexEnter = -1;
this.fade=[" "," "," "]
...rest of the code..
}
the result in new stackblitz
Well, there's a problem becaouse we can drag over the las element of a row and the app fails, sorry. So we need make another change
In drop function check if the currentIndex is equal to data.length
const currentIndex=(event.currentIndex==event.container.data.length)?
event.container.data.length-1:event.currentIndex
In the div make another *ngIf is last
<div class="tile" *ngIf="!last || indexEnter!=1 || indexEnter==indexDrag">
<div class="tile-container">{{item}}</div>
</div>
Upvotes: 2