通八洲科技

如何在 Angular 中动态创建可拖拽组件并支持自定义属性配置

日期:2025-12-31 00:00 / 作者:心靈之曲

本文介绍如何使用 angular + cdk drag & drop + reactive forms 实现:点击按钮弹出表单,输入组件名称、描述、类型,并动态添加属性(含默认值与数据类型),最终将新组件渲染到拖拽面板中。

在构建可视化低代码平台或流程编排工具时,常需支持用户动态创建可复用的 UI 组件,并将其拖入画布。Angular 提供了强大的响应式表单(Reactive Forms)与 @angular/cdk/drag-drop 模块,结合 Taiga UI(如 tuiDialog、tuiSelect)可快速搭建专业级交互界面。

✅ 核心功能实现要点

1. 弹窗表单:使用 tuiDialog 管理状态

通过 [(tuiDialog)]="open" 双向绑定控制模态框显隐,配合 tuiDialogOptions 设置标题与尺寸。表单采用 FormGroup 管理字段状态:

exampleForm = new FormGroup({
  nameOfComponent: new FormControl('', [Validators.required]),
  description: new FormControl(''),
});
⚠️ 注意:formControlName 必须与 FormGroup 中的键名严格一致,否则无法双向绑定。

2. 动态属性管理:使用 FormArray

每个组件可拥有多个属性(如 defaultValue、type),推荐用 FormArray 统一管理:

// 在 ngOnInit() 中初始化
this.DragAndDropForm = new FormGroup({
  name: this.exampleForm.get('nameOfComponent'),
  description: this.exampleForm.get('description'),
  componentType: new FormControl(this.chosenComponent),
  attributes: new FormArray([]) // ? 关键:空数组承载属性组
});

// 添加新属性方法
addAttribute() {
  const attrGroup = new FormGroup({
    defaultValue: new FormControl('', Validators.required),
    type: new FormControl('int', Validators.required)
  });
  (this.DragAndDropForm.get('attributes') as FormArray).push(attrGroup);
}

对应 HTML 中使用 *ngFor 渲染:

  
  
    
      
      
      
    
  

3. 拖拽区域:CDK 集成与组件渲染

利用 cdkDropList 和 cdkDrag 实现拖拽容器与可拖拽项。关键点:新组件需以 形式动态插入对应分类下的 内部。

示例(动态追加“Kraftwerk”组件):

// 在 submitComponent() 中保存后执行
const newComponent = {
  id: Date.now(),
  name: this.exampleForm.value.nameOfComponent,
  description: this.exampleForm.value.description,
  type: this.chosenComponent,
  icon: '⚡', // 或 SVG 字符串
  attributes: this.DragAndDropForm.get('attributes').value
};

// 将 newComponent 推入对应 category 的组件列表(如 this.einspeiserComponents)
this.einspeiserComponents.push(newComponent);

并在模板中循环渲染:


  Einspeiser
  
    
      {{ comp.icon }}
      {{ comp.name }}
    
  

4. 后端对接与数据持久化

表单提交应调用服务层发送 POST 请求。避免硬编码 URL,建议封装至 ApiService:

submitComponent(): void {
  if (this.DragAndDropForm.invalid) return;

  const payload = {
    ...this.exampleForm.value,
    type: this.chosenComponent,
    attributes: this.DragAndDropForm.get('attributes').value
  };

  this.apiService.postComponent(payload).subscribe({
    next: res => {
      console.log('✅ Component saved:', res);
      this.open = false; // 关闭弹窗
      this.resetForm();  // 重置表单
    },
    error: err => console.error('❌ Save failed:', err)
  });
}

private resetForm(): void {
  this.exampleForm.reset();
  this.chosenComponent = this.componentTypes[0];
  (this.DragAndDropForm.get('attributes') as FormArray).clear();
}

? 注意事项总结

  • 表单嵌套结构清晰化:主表单(exampleForm)负责元信息,FormArray(attributes)专注属性集合,避免深层嵌套失控。
  • 拖拽数据隔离:使用 [cdkDragData] 传递完整组件对象,便于 Drop 区域接收并解析。
  • UI 响应及时性:每次新增属性后立即调用 detectChanges()(若启用了 OnPush 策略)。
  • 类型安全增强:为 componentTypes 定义 enum 或 const array,配合 TypeScript 类型推导提升健壮性。
  • 图标资源优化:SVG 图标建议提取为独立组件或使用 @angular/material-icons,避免内联冗余代码。

通过以上结构化实现,你将获得一个可扩展、易维护、符合 Angular 最佳实践的动态组件管理系统,为后续支持属性绑定、连接线逻辑、JSON Schema 导出等高级功能奠定坚实基础。