1

I'm using display-inline in my elements as I want them to lay out horizontally instead of vertically as they do in most of the Angular examples. That said, the CSS behaves very weird. I'm specifically having two issues.

  1. When I click an element to drag it, it grows (shrinks back to regular size after dropped). I'm not sure exactly why this happens but it is definitely not desired. I've tried numerous things to fix this via both css and adding a cdkDragPreview element with matchSize present (this seems to be the method Angular recommends). All of those efforts failed. I came across the following bug report that seems similar to my issue: https://github.com/angular/components/issues/19060. I noted that the bug was closed, so I don't know if that means it has been fixed.
  2. When I start to drag an element from the bottom drop list, the remaining items move around sporadically while that element is still in the drop list (when it goes out of the bottom drop list they behave as I would expect them to). I created a hide style for the cdkDragPlaceholder as this seems to be how Angular provides control of this but it only helped with the top drop lists and seemed to have no effect on the bottom.

Here is a link that illustrates both issues on StackBlitz: https://stackblitz.com/edit/spuzzler. I'm guessing that my issue can be fixed with CSS, but I can't figure out how.

1 Answers1

3

Create a cdkDropList for each word. My idea is to have an outer div and an inner div that is really the element that is dragged. More over, I fixed the size of the outer div. So, when you drag, there's no re-order of the words (simply leaves an empty space instead of the word you drag)

You can see the result in this stackblitz

<div #contenedor class="categories" cdkDropListGroup> 
    <ng-container *ngFor="let item of items;let i=index">
        <div class="categories-item" cdkDropList 
    cdkDropListOrientation="horizontal"
    [cdkDropListData]="{item:item,index:i}" (cdkDropListDropped)="drop($event)" >
            <div class="inner"  cdkDrag>
        <div *cdkDragPlaceholder></div>
        <div class="categories-item-drag" *cdkDragPreview matchSize="true" >
          <div class="inner">{{item}}</div>
        </div>
        {{item}}
        </div>
        </div>
    </ng-container>
</div>

I use an observable that returns an array or words. In subscribe I equal to item and, using a setTimeout() add the size to the outter div

export class AppComponent implements OnInit {
  @ViewChildren(CdkDropList, { read: ElementRef }) pills: QueryList<ElementRef>;
  constructor(private renderer: Renderer2) {}
  items: any[];
  positions: any[];
  ngOnInit() {
    this.getParragraf().subscribe(res => {
      this.items = res;
      setTimeout(() => {
        this.pills.forEach(x => {
          this.renderer.setStyle(
            x.nativeElement,
            "width",
            x.nativeElement.getBoundingClientRect().width + "px"
          );
        });
      });
    });
  }

  drop(event: CdkDragDrop<any>) {
    this.items.splice(event.previousContainer.data.index, 1);
    this.items.splice(event.container.data.index,0,event.previousContainer.data.item)
    /* if we want to interchange the words, replace the two lines by*/
    //this.items[event.previousContainer.data.index]=event.container.data.item
    //this.items[event.container.data.index]=event.previousContainer.data.item
    //event.currentIndex=0;
    

  }

  getParragraf() {
    return of(
      "Let him who walks in the dark, who has no light, trust in the name of the Lord and rely on his God.".split(
        " "
      )
    );
  }
}

Updated Really you needn't make a cdkDropListGroup, you can take advantage of [cdkDropListConnectedTo]. For this, you have two arrays: words and items

if res is an array of strings, you can have

  this.items = res.map((x,index)=>({value:x,ok:false,id:'id'+index}));
  this.words=res.map(x=>({o:Math.random(),value:x}))
             .sort((a,b)=>a.o-b.o)
             .map(x=>(
               {value:x.value,
               connected:this.items.filter(w=>x.value==w.value).map(x=>x.id)
               }))

and use item.value,item.id and word.value,word.connected

See a new stackblitz

Vlad Lego
  • 1,670
  • 1
  • 18
  • 19
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • You've setup a little differently than what I'm going for. The stackblitz I shared actually works exactly as desired except for the resizing on drag and the movement of the items inside the word-bank element. I think I understand that you are suggesting I need an outer element outside each of my cdkDrag elements that has a fixed width. It sounds like with the width fixed, then matchSize will work whereas right now it seems to do nothing for me. I'll report back as right now I'm having trouble injecting your code into mine in a way that works. – Kristopher Bishop Jun 01 '20 at 13:37
  • One thing I noted is that you had used cdkDropListOrientation="horizontal" and I had missed that this was even a property. I added that to my word-bank element and it immediately broke the code. For some reason once I specify horizontal, event.previousIndex always returns 0, no matter which element is grabbed. This also did not seem to fix anything as far as how it displays, but it was an odd side effect. – Kristopher Bishop Jun 01 '20 at 14:07
  • @KristopherBishop, in the stackblitz I put, the cdkListData has only one element: `[cdkDropListData]="{item:item,index:i}"` this element is an object with two peoperties, item (that get the value of "item") and index (that get the value of the index) It's the only thing I imagined. So the "indexes" are event.container.data.index and event.previousContainer.data.item. Sorry for not explain in the answer. The problem using cdk-grad and drop Angular is that it's thinked for an unique row or an unique colum, not for severals. – Eliseo Jun 01 '20 at 14:55
  • There a large conversation in github about this: https://github.com/angular/components/issues/13372, but no an official solution – Eliseo Jun 01 '20 at 15:08
  • I tried manually setting the width and height of an outer div, but this too appears to throw off the index of the item being selected. I have to step away from this for now, but here is a link to a fork showing it in the broken state: https://stackblitz.com/edit/spuzzler-va5cib. I used ngAfterViewInit to loop through and manually set width and height (primarily because I don't really understand constructors, etc. and was having trouble getting them to work). – Kristopher Bishop Jun 01 '20 at 15:32
  • After posting my last comment, I saw your most recent responses. Thank you. I do have to go, but I'll look at it in more detail when I am able to come back tot his. – Kristopher Bishop Jun 01 '20 at 15:36
  • I updated the answer (and adding a new stackblitz) using a cdkDropListConnectedTo – Eliseo Jun 02 '20 at 16:27
  • Thank you for all of your help! I was out of town some last week, so I am just getting back to this and your StackBlitz is exactly what I was trying to achieve. I now just need to wrap my head around how your example is working, but everything I need to do so is in your code. Thanks again! – Kristopher Bishop Jun 08 '20 at 14:41