DBoi
DBoi

Reputation: 687

Push last object in array to the next array

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

Answers (1)

Eliseo
Eliseo

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

Related Questions