通过拖拽重排序指定子组件的技术实现问题
不可修改子组件的拖拽排序实现方案
要实现这类场景的拖拽排序,核心思路是用数组管理组件的元数据(类型、输入参数),通过数组顺序控制组件渲染顺序,拖拽时直接操作数组即可。以下是具体实现步骤:
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
相关产品推荐
相关产品推荐

