Search code examples
cssangularangular-cdkangular-cdk-drag-drop

How to prevent rearrange of children elements upon deleting one child in angular


I have a parent angular component that displays children components with ngFor directive. Each child acts as an individual window within the parent and their position can be rearranged by CdkDrag. And I also created a small "X" button on the right top corner to close the child component. And when I hit "x" button to close one child with low index (e.g., 1 or 2 in the stackbliz example below), the other children are rearranged automatically. Is there a way to prevent such rearrangements and stay as is when closing any child window?

child component

@Input('target') target: string = '';
@Input('index') index: string = '';
@Output() onClose: EventEmitter<number> = new EventEmitter();

closeModal() {
  const i: number = +this.index;
  this.onClose.emit(i);
}

child template

<div class="example-box" cdkDrag>
  {{target}}
  <button class="CloseButton" (click)="closeModal()">X</button>
</div>

child css

.example-box {
  width: 100px;
  height: 100px;
  border: solid 1px #ccc;
  color: rgba(0, 0, 0, 0.87);
  display: flex;
  justify-content: center;
  position: relative;
  resize: both;
}

.CloseButton {
  position: absolute;
  top: 10px;
  right: 10px;
}

parent component

  names: string[] = ['1', '2', '3'];
  modalClosed(id: any) {
    this.names.splice(id, 1);
    console.log(id);
  }

parent template

<div class="ParentMain">
  <child-comp
    *ngFor="let name of names ; index as i"
    (onClose)="modalClosed($event)"
    target="{{name}}"
    index="{{i}}"
  >
  </child-comp>
</div>

parent css

.ParentMain {
  display: flex;
}

Complete stackbliz example

Stackbliz example code


Solution

  • There're another approach, that I remember used in this SO

    If we imagine a cdkDropList with "items" inside we can do some like

    <div
      cdkDropList
      #doneList="cdkDropList"
      [cdkDropListData]="done"
      class="drag-zone"
      cdkDropListSortingDisabled="true"
    >
      <div
        *ngFor="let item of done;let i=index"
        cdkDrag
        class="item-box"
        [style.top.px]="item.y"
        [style.left.px]="item.x"
        [style.z-index]="item['z-index']"
        (cdkDragStarted)="changeZIndex(item)"
        (cdkDragDropped)="changePosition($event, item)"
      >
        <child-comp
          class="item-box"
          [target]="item.name"
          [index]="i"
          (onClose)="modalClosed($event)"
        >
        </child-comp>
        <div *cdkDragPlaceholder class="field-placeholder"></div>
      </div>
    </div>
    

    Yes a cdkDropList na be different that a list!

      names: string[] = ['1', '2', '3'];
      done=this.names.map((x,index)=>({name:x,x:index*100,y:0,"z-index":0}))
    
    
      modalClosed(id: any) {
        this.done.splice(id, 1);
        console.log(id);
      }
    
      @ViewChild('doneList', { read: ElementRef, static: true }) dropZone: ElementRef;
    
      changeZIndex(item: any) {
        this.done.forEach((x) => (x['z-index'] = x == item ? 1 : 0));
      }
      changePosition(event: CdkDragDrop<any>, field:any) {
        const rectZone = this.dropZone.nativeElement.getBoundingClientRect();
        const rectElement =
          event.item.element.nativeElement.getBoundingClientRect();
    
        let y = +field.y + event.distance.y;
        let x = +field.x + event.distance.x;
          field.y = y;
          field.x = x;
          this.done = this.done.sort((a, b) =>
            a['z-index'] > b['z-index'] ? 1 : a['z-index'] < b['z-index'] ? -1 : 0
          );
      }
    

    And some of .css

    .drag-zone{
      position:relative;
      flex-grow:1;
      height: 20rem;
      border:1px solid silver;
      overflow: hidden;
    }
    .item-box {
      position:absolute;
    }
    
    //see that you die a shadow to de component
    //using .cdk-drag-preview + selector of the child component
    .cdk-drag-preview child-comp {
      box-sizing: border-box;
      border-radius: 4px;
      box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
                  0 8px 10px 1px rgba(0, 0, 0, 0.14),
                  0 3px 14px 2px rgba(0, 0, 0, 0.12);
    }
    drag-zone
    .cdk-drag-placeholder {
      opacity: 0;
    }
    

    Stackblitz