You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过拖拽重排序指定子组件的技术实现问题

不可修改子组件的拖拽排序实现方案

要实现这类场景的拖拽排序,核心思路是用数组管理组件的元数据(类型、输入参数),通过数组顺序控制组件渲染顺序,拖拽时直接操作数组即可。以下是具体实现步骤:

1. 定义组件配置数组(核心)

在父组件中创建数组,存储每个子组件的类型、输入参数和唯一标识,后续排序、渲染全靠这个数组驱动:

import { Component, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { App1CardComponent } from './app-1-card.component';
import { App2CardComponent } from './app-2-card.component';
import { App3CardComponent } from './app-3-card.component';
import { App4CardComponent } from './app-4-card.component';

// 统一组件配置类型
interface ComponentConfig {
  id: string; // 唯一标识,用于拖拽区分和排序结果保存
  component: any;
  inputs: { [key: string]: any };
}

@Component({
  selector: 'app-parent',
  template: `
    <div class="drag-container">
      <div 
        class="draggable-item"
        *ngFor="let config of componentConfigs; let i = index"
        draggable="true"
        (dragstart)="onDragStart(i)"
        (dragover)="onDragOver($event)"
        (drop)="onDrop($event, i)"
      >
        <ng-template #componentHost></ng-template>
      </div>
    </div>
  `,
  styles: [`
    .drag-container { display: flex; flex-direction: column; gap: 1rem; padding: 1rem; }
    .draggable-item { padding: 1rem; border: 1px solid #ddd; cursor: move; }
  `]
})
export class ParentComponent {
  componentConfigs: ComponentConfig[] = [
    {
      id: 'app-1',
      component: App1CardComponent,
      inputs: { input: this.input }
    },
    {
      id: 'app-2',
      component: App2CardComponent,
      inputs: { input: this.input }
    },
    {
      id: 'app-3',
      component: App3CardComponent,
      inputs: { input: this.input } // 注意原代码中app-3-card的重复input属性已统一处理
    },
    {
      id: 'app-4',
      component: App4CardComponent,
      inputs: { input: this.input }
    }
  ];

  input = 'your-input-value'; // 你的业务输入值
  draggedIndex: number | null = null;

  constructor(
    private componentFactoryResolver: ComponentFactoryResolver,
    private viewContainerRef: ViewContainerRef
  ) {}

  ngAfterViewInit() {
    this.renderComponents();
  }

  // 动态渲染子组件
  renderComponents() {
    this.componentConfigs.forEach((config, index) => {
      const hostElement = document.querySelectorAll('.draggable-item ng-template')[index];
      const containerRef = this.viewContainerRef.createEmbeddedView(hostElement).viewContainerRef;
      containerRef.clear();
      
      // 创建组件实例
      const componentFactory = this.componentFactoryResolver.resolveComponentFactory(config.component);
      const componentRef = containerRef.createComponent(componentFactory);
      
      // 绑定输入参数
      Object.keys(config.inputs).forEach(key => {
        componentRef.instance[key] = config.inputs[key];
      });
    });
  }

  // 拖拽事件处理
  onDragStart(index: number) {
    this.draggedIndex = index;
  }

  onDragOver(event: DragEvent) {
    event.preventDefault(); // 必须阻止默认行为才能触发drop事件
  }

  onDrop(event: DragEvent, dropIndex: number) {
    event.preventDefault();
    if (this.draggedIndex === null || this.draggedIndex === dropIndex) return;

    // 调整数组顺序
    const draggedItem = this.componentConfigs[this.draggedIndex];
    this.componentConfigs.splice(this.draggedIndex, 1);
    this.componentConfigs.splice(dropIndex, 0, draggedItem);

    // 重新渲染组件
    this.renderComponents();

    // 保存排序结果(示例:存到本地存储)
    localStorage.setItem('componentOrder', JSON.stringify(this.componentConfigs.map(c => c.id)));
  }
}

2. 简化拖拽逻辑:用Angular CDK Drag-and-Drop

如果不想自己写原生拖拽事件,可以用Angular CDK的拖拽组件,代码更简洁可靠:

  • 先安装CDK:
npm install @angular/cdk
  • 父组件修改:
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';

// 模板替换为:
<div class="drag-container" cdkDropList (cdkDropListDropped)="onDrop($event)">
  <div 
    class="draggable-item"
    *ngFor="let config of componentConfigs"
    cdkDrag
  >
    <ng-template #componentHost></ng-template>
  </div>
</div>

// 拖拽处理函数简化为:
onDrop(event: CdkDragDrop<ComponentConfig[]>) {
  moveItemInArray(this.componentConfigs, event.previousIndex, event.currentIndex);
  this.renderComponents();
  // 保存排序结果
  localStorage.setItem('componentOrder', JSON.stringify(this.componentConfigs.map(c => c.id)));
}

3. 恢复保存的排序结果

页面刷新后要维持之前的排序,可在父组件初始化时读取保存的顺序:

ngOnInit() {
  const savedOrder = localStorage.getItem('componentOrder');
  if (savedOrder) {
    const orderedIds = JSON.parse(savedOrder);
    // 根据保存的ID重新排序配置数组
    this.componentConfigs = orderedIds.map((id: string) => 
      this.componentConfigs.find(config => config.id === id)!
    );
  }
}

内容的提问来源于stack exchange,提问作者newdev118

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 01:35:31