css 如何防止在删除一个子元素时重新排列子元素

y3bcpkx1  于 2023-11-19  发布在  其他
关注(0)|答案(4)|浏览(117)

我有一个父Angular 组件,它使用ngFor指令显示子组件。每个子组件都充当父组件中的一个单独窗口,它们的位置可以通过CdkDrag重新排列。我还在右上角创建了一个小“X”按钮来关闭子组件。当我点击“x”按钮关闭一个索引较低的子组件时,(例如,下面stackbliz示例中的1或2),其他子窗口将自动重新排列。是否有方法阻止这种重新排列,并在关闭任何子窗口时保持原样?

子组件

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

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

字符串

子模板

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

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

父组件

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

父模板

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

父CSS

.ParentMain {
  display: flex;
}

完整的stackbliz示例

Stackbliz example code

col17t5w

col17t5w1#

还有另一种方法,我记得在这个SO中使用过,
如果我们想象一个cdkDropList里面有“items”,我们可以做一些类似的事情,

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

字符串
是的,cdkDropList与列表不同!

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


还有一些.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

vhmi4jdf

vhmi4jdf2#

当cdk拖动时,将样式更改为添加transform的元素:3D。这就是为什么如果更改“origin”(您将元素放置在带有flex的div中),则元素会排列在另一个位置。
如果使用endDrag事件给予一个绝对位置和左上角位置,则可以避免这种情况。但在此之前,将“cdkDrag”改为chuid中的div,您可以拖动整个元素

<!--See the template reference variable "wrapper"-->
<div #wrapper style="position:relative;display:flex" >
 
  <!--in cdkDragStarted I pass the component-->
  <child-comp #el cdkDrag (cdkDragStarted)="dragStart(el)" 
                          (cdkDragEnded)="dragEnd($event,i)" 
                          (onClose)="modalClosed($event)"
    *ngFor="let name of names ; index as i"
    target="{{name}}"
    index="{{i}}"
  >
  </child-comp>
</div>

字符串
在子组件中,我们将在构造函数中执行elementRef注入

//In child
constructor(public elementRef:ElementRef){}


嗯,像这样的拖拽

dragEnd(event:CdkDragEnd)
  {
    //get the element:
    const el=event.source.element.nativeElement

    //change the style to position absolute
    el.style.position='absolute'
    el.style.transform=''
    el.style.top=(this.oldPosition.y+event.distance.y)+'px'
    el.style.left=(this.oldPosition.x+event.distance.x)+'px'
  }


由于你使用的是一个非常老的cdk-drag版本,你在事件中没有“位置”,所以我们需要在启动drag时做一些工作

dragStart(component:any)
  {
    const posWrapper=this.container.nativeElement.getBoundingClientRect()
    const pos=component.elementRef.nativeElement.getBoundingClientRect()
    this.oldPosition={x:pos.x-posWrapper.x,y:pos.y-posWrapper.y}
    const el=component.elementRef.nativeElement;
    el.style.top=''
    el.style.left=''
    el.style.position='relative'

  }


最后是使“ Package 器”在所有元素被拖动时转换宽度和高度

ngAfterViewInit()
  {
    const posWrapper=this.container.nativeElement.getBoundingClientRect()
    this.container.nativeElement.style.width=posWrapper.width+'px'
    this.container.nativeElement.style.height=posWrapper.height+'px'
  }


A stackblitz

2nc8po8w

2nc8po8w3#

它属于拖放时如何处理项目位置,需要在onDragEnded事件后保存项目位置,可以看示例here:

z3yyvxxp

z3yyvxxp4#

如果你想在关闭按钮被按下后保留每个对话框的位置,那么有很多方法可以做到这一点。最简单的方法是在按钮被点击时隐藏对话框,而不是将它们从模板中删除。
这里有一个这样做的例子:https://stackblitz.com/edit/angular-pqf4je-aatkjg?file=src%2Fapp%2Fchild-comp.html
在上面的例子中,点击对话框会导致项目消失,但其余的项目不应该移动,因为项目实际上仍然存在,它只是被设置为不透明度:0。
一个稍微先进一点的方法,不会导致屏幕上有很多不可见的组件,那就是使用CSS将项目定位在特定的位置-这样当一个被删除时,它们就不会移动。

相关问题