Issue
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
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;
}
Answered By - Eliseo
 
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.