Angular中无jQuery创建动态DOM元素并传入jsPlumb的实现方案
我来帮你解决这个问题,不用jQuery完全可以用Angular原生方式实现,分两种情况给你详细方案:
一、创建原生DOM元素(替代jQuery方案)
你可以直接用原生JavaScript的document.createElement,或者更符合Angular最佳实践的Renderer2来创建DOM元素,完全不需要依赖jQuery。
方案1:原生JavaScript API
直接用浏览器原生方法创建元素并组装,代码逻辑和jQuery版本完全对应:
["Custom", { create: function(component) { // 创建select元素 const select = document.createElement('select'); select.id = 'myDropDown'; // 创建第一个option const optionFoo = document.createElement('option'); optionFoo.value = 'foo'; optionFoo.textContent = 'foo'; select.appendChild(optionFoo); // 创建第二个option const optionBar = document.createElement('option'); optionBar.value = 'bar'; optionBar.textContent = 'bar'; select.appendChild(optionBar); return select; }, location: 0.5, id: "customOverlay" }]
方案2:Angular Renderer2(推荐)
Renderer2是Angular提供的抽象层,兼容不同平台(比如Web、Server Side Rendering),更符合Angular的设计理念。首先在组件类中注入Renderer2,然后使用它来创建元素:
// 先在组件构造函数中注入Renderer2 constructor(private renderer: Renderer2) {} // 然后编写覆盖层的create函数 ["Custom", { create: (component) => { const select = this.renderer.createElement('select'); this.renderer.setAttribute(select, 'id', 'myDropDown'); const optionFoo = this.renderer.createElement('option'); this.renderer.setAttribute(optionFoo, 'value', 'foo'); this.renderer.setProperty(optionFoo, 'textContent', 'foo'); this.renderer.appendChild(select, optionFoo); const optionBar = this.renderer.createElement('option'); this.renderer.setAttribute(optionBar, 'value', 'bar'); this.renderer.setProperty(optionBar, 'textContent', 'bar'); this.renderer.appendChild(select, optionBar); return select; }, location: 0.5, id: "customOverlay" }]
二、挂载自定义Angular组件到jsPlumb覆盖层
要把Angular组件作为覆盖层,核心是让Angular编译并挂载组件,然后把组件的宿主DOM元素交给jsPlumb。这里需要用到Angular的组件工厂API(或者独立组件的createComponent方法),同时要注意组件的销毁,避免内存泄漏。
非独立组件(Angular <14 或未使用standalone)
- 首先确保你的
CustomOverlayComponent在模块中被声明并添加到entryComponents(Angular 9+ Ivy模式下entryComponents可以省略,但保险起见还是加上)。 - 在组件类中注入
ComponentFactoryResolver、ApplicationRef和Injector:
constructor( private componentFactoryResolver: ComponentFactoryResolver, private appRef: ApplicationRef, private injector: Injector ) {}
- 编写覆盖层的create函数:
["Custom", { create: (component) => { // 1. 创建组件工厂 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(CustomOverlayComponent); // 2. 创建组件实例 const componentRef = componentFactory.create(this.injector); // 3. 把组件附加到Angular应用中,触发变更检测 this.appRef.attachView(componentRef.hostView); // 4. 获取组件的宿主DOM元素 const domElement = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement; // 可选:给组件传递输入属性 // componentRef.instance.someInputProperty = '你需要的值'; // 可选:监听组件的输出事件 // componentRef.instance.someOutputEvent.subscribe((data) => { // console.log('组件输出:', data); // }); // 5. 关键:在连接断开时销毁组件,避免内存泄漏 jsPlumb.on('connectionDetached', (conn) => { if (conn.getOverlay('customOverlay')?.component === component) { this.appRef.detachView(componentRef.hostView); componentRef.destroy(); } }); return domElement; }, location: 0.5, id: "customOverlay" }]
独立组件(Angular 14+)
如果你的CustomOverlayComponent是标记为standalone: true的独立组件,代码会更简洁,不需要ComponentFactoryResolver:
// 组件类中注入ApplicationRef constructor(private appRef: ApplicationRef) {} // 覆盖层create函数 ["Custom", { create: (component) => { // 1. 创建组件实例 const componentRef = createComponent(CustomOverlayComponent, { environmentInjector: this.appRef.injector }); // 2. 附加到应用中 this.appRef.attachView(componentRef.hostView); // 3. 获取宿主DOM元素 const domElement = componentRef.location.nativeElement; // 4. 传递输入/监听输出(和非独立组件一致) // componentRef.instance.someInput = 'test'; // 5. 监听连接销毁,清理组件 jsPlumb.on('connectionDetached', (conn) => { if (conn.getOverlay('customOverlay')?.component === component) { this.appRef.detachView(componentRef.hostView); componentRef.destroy(); } }); return domElement; }, location: 0.5, id: "customOverlay" }]
注意事项
- 组件销毁:一定要在连接断开时调用
componentRef.destroy(),否则会导致内存泄漏,Angular无法回收组件实例。 - 变更检测:把组件附加到
ApplicationRef后,Angular会自动处理变更检测,组件的输入输出都能正常工作。 - 样式适配:自定义组件的样式可能需要调整,确保它能正确显示在jsPlumb的连接线上。
内容的提问来源于stack exchange,提问作者yazz
相关产品推荐
相关产品推荐

