Angular 18拖拽功能求助:仅迁移Food的名称与ID至Meal
解决Angular 18 CDK拖拽中仅迁移Food的Name和ID到Meal的问题
需求说明
拖拽Food项到Meal列表时,仅将Food的name和id字段添加到目标Meal的foods数组中,不需要携带其他冗余字段。
现有代码
Component HTML
<div cdkDropListGroup> <div cdkDropList [cdkDropListData]="meals" class="makeMeal" (cdkDropListDropped)="drop($event)"> <div *ngFor="let meal of meals" class="meal-drop"> <img src="page_delete.png" (click)="deleteFood()" title="delete" /> <h4>{{meal.name}}</h4> <label id="lblID" class="hidden">{{meal.id}}</label> </div> Add a meal: <input id="mealName" type="text" [(ngModel)]="mealName" name="mealName" required style="margin-left:10px;" /> <img src="page_white_add.png" (click)="addMeal()" /> </div> <p>My foods:</p> <div cdkDropList [cdkDropListData]="foods" class="food-grid" (cdkDropListDropped)="drop($event)"> <div *ngFor="let food of foods" class="food-item"> <img src="page_delete.png" (click)="deleteFood()" title="delete" /> <h4>{{ food.name }}</h4> <ul> --> bunch of data <-- </ul> </div> </div> </div>
Component TypeScript
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; export interface Foods { name: string, calories: number, protein: number, carbs: number, potassium: number, id: number } export interface Meals { name: string, id: string, foods: Foods[] } // 原有drop方法 drop(event: CdkDragDrop<Foods[] | Meals[]>) { if (event.previousContainer === event.container) { moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); } else { if (event.previousContainer.id === 'cdk-drop-list-foods') { const foodItem = event.previousContainer.data[event.previousIndex]; const mealIndex = event.container.data.findIndex(meal => meal.id === event.container.id); if (mealIndex !== -1) { this.meals[mealIndex].foods.push(foodItem); } } else { const mealIndex = this.meals.findIndex(meal => meal.id === event.previousContainer.id); const foodItem = this.meals[mealIndex].foods[event.previousIndex]; this.meals[mealIndex].foods.splice(event.previousIndex, 1); // Add food back to foods array this.foods.push(foodItem); } } }
问题分析与修正方案
1. 给CDK拖拽容器设置明确ID
原代码中cdkDropList未设置id属性,导致无法正确识别拖拽来源/目标容器。需要给两个拖拽容器分别添加id:
<!-- Meal列表容器 --> <div cdkDropList id="meal-drop-list" [cdkDropListData]="meals" class="makeMeal" (cdkDropListDropped)="drop($event)"> <!-- ... 原有内容 ... --> </div> <!-- Food列表容器 --> <div cdkDropList id="food-drop-list" [cdkDropListData]="foods" class="food-grid" (cdkDropListDropped)="drop($event)"> <!-- ... 原有内容 ... --> </div>
2. 修改drop方法,仅提取需要的字段
原代码存在两个核心问题:
- 错误地用容器ID匹配Meal的ID,实际应该获取拖拽目标对应的Meal对象
- 直接推送整个Food对象,未提取仅需的
name和id
修正后的drop方法:
drop(event: CdkDragDrop<Meals[] | Foods[]>) { // 同一容器内拖拽,调整顺序 if (event.previousContainer === event.container) { moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); return; } // 从Food列表拖拽到Meal列表 if (event.previousContainer.id === 'food-drop-list' && event.container.id === 'meal-drop-list') { // 获取拖拽的Food项 const food = event.previousContainer.data[event.previousIndex] as Foods; // 获取目标Meal项(拖拽到的Meal位置) const targetMeal = event.container.data[event.currentIndex] as Meals; // 仅提取name和id,构造新对象添加到Meal的foods数组 const simplifiedFood = { id: food.id, name: food.name, // 若Foods接口要求必填其他字段,可设为默认值(比如0或'') calories: 0, protein: 0, carbs: 0, potassium: 0 }; targetMeal.foods.push(simplifiedFood); // 可选:从原Food列表移除该项(根据需求决定是否保留) event.previousContainer.data.splice(event.previousIndex, 1); } // 从Meal列表拖拽回Food列表(可选) if (event.previousContainer.id === 'meal-drop-list' && event.container.id === 'food-drop-list') { // 获取拖拽的Food项 const draggedFood = event.previousContainer.data[event.currentIndex].foods[event.previousIndex]; // 添加回Food列表 this.foods.push(draggedFood); // 从Meal的foods数组移除 event.previousContainer.data[event.currentIndex].foods.splice(event.previousIndex, 1); } }
3. 类型安全优化
为了避免类型断言的繁琐,可以创建一个简化的Food类型用于Meal关联:
// 新增简化类型,仅包含需要的字段 export interface SimplifiedFood { id: number; name: string; } // 修改Meals接口,使用简化类型 export interface Meals { name: string; id: string; foods: SimplifiedFood[]; }
此时在drop方法中构造对象时无需补全多余字段:
const simplifiedFood: SimplifiedFood = { id: food.id, name: food.name }; targetMeal.foods.push(simplifiedFood);
内容的提问来源于stack exchange,提问作者TheWizardOfTN
相关产品推荐
相关产品推荐

