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
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;
}