在 Angular 项目里,我们常常需要根据用户操作动态生成界面元素,并且希望这些元素可以被自由拖动、实时配置属性。借助 Angular 的动态组件机制和 CDK 拖拽模块,就能比较优雅地实现这套能力。

一、核心思路
动态创建组件依赖 ComponentFactoryResolver 或新版 Angular 的 ViewContainerRef.createComponent。拖拽能力可直接使用 @angular/cdk/drag-drop 提供的指令。属性配置则通过动态组件的 @Input() 来完成。
1.1 需要准备的依赖
- @angular/core 基础框架
- @angular/cdk 拖拽模块
- 一个用作动态内容的子组件
二、定义可配置的动态组件
我们先写一个最简单的卡片组件,它接收标题与颜色两个属性:
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-drag-card',
template: `
<div class="card" [style.background]="color">
<h4>{{ title }}</h4>
<p>可拖拽内容块</p>
</div>
`
})
export class DragCardComponent {
@Input() title: string = '默认标题';
@Input() color: string = '#e0f7fa';
}
三、在容器中动态创建并支持拖拽
父组件使用 ViewContainerRef 来挂载上述组件,并给外层包裹一个带 cdkDrag 的容器。
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { DragCardComponent } from './drag-card.component';
@Component({
selector: 'app-host',
template: `
<div class="canvas">
<div cdkDrag class="drag-wrapper" *ngFor="let cfg of configs">
<ng-template #container></ng-template>
</div>
</div>
<button (click)="addCard()">添加卡片</button>
`
})
export class HostComponent {
configs: any[] = [];
@ViewChild('container', { read: ViewContainerRef, static: false }) containers!: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {}
addCard() {
const cfg = { title: '新卡片' + (this.configs.length + 1), color: '#ffe082' };
this.configs.push(cfg);
setTimeout(() => {
const ref = this.containers.createComponent(this.resolver.resolveComponentFactory(DragCardComponent));
ref.instance.title = cfg.title;
ref.instance.color = cfg.color;
});
}
}
3.1 模块引入 CDK 拖拽
别忘了在 NgModule 中导入 DragDropModule:
import { DragDropModule } from '@angular/cdk/drag-drop';
@NgModule({
imports: [DragDropModule]
})
export class AppModule {}
四、属性配置的常见做法
实际项目中,动态组件的属性往往来自表单或后端接口。你可以在创建组件后,直接对 ref.instance 赋值,也可以通过服务集中管理配置对象,在子组件内用 ngOnChanges 监听变化。
| 方式 | 适用场景 |
|---|---|
| 直接赋值 instance | 简单一次性配置 |
| 共享 Service | 跨组件同步状态 |
| 表单双向绑定 | 用户实时调整属性 |
五、小结
通过 Angular 动态组件配合 CDK 拖拽指令,我们能够以较低成本实现可拖拽且可配置属性的界面单元。重点在于理解 ViewContainerRef 的创建时机,以及 Input 属性如何正确传入动态实例。