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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 23:44:55