使用 D3 在 DOM 中动态添加/删除 Angular 组件

Posted

技术标签:

【中文标题】使用 D3 在 DOM 中动态添加/删除 Angular 组件【英文标题】:Dynamically adding/removing Angular components to/from the DOM using D3 【发布时间】:2018-09-15 18:43:59 【问题描述】:

我正在开发一个 Angular 组件 MyGraphComponent,它应该使用 D3 显示一个图形,其中图形由一些 listOfEdges: Edge[] 和一些 listOfNodes: Node[] 给出,它们作为 MyGraphComponent 传递给 @Input s。 listOfNodes 中的每个 individualNode 都应该作为 Angular 组件 <my-node [node]="individualNode"> 显示在画布上,为给定的节点实例提供功能。 (想象一下,例如一个数据库模式编辑器,其中节点是可以编辑的表。)

现在,我想完全控制将节点添加到画布(即图形的 DOM)的方式和时间,因此,我不能只在图形组件的模板中使用 <my-node *ngFor="let node of listOfNodes">。 (例如,我想添加动画,这些动画需要与正在使用 D3 的图形的边缘和其他部分的相应动画同步。)换句话说,我需要生成 MyNodeComponent 的所有实例动态,这样我就可以使用 D3 将它们添加到 DOM(然后在必要时删除它们)。

我找到了大量解释如何动态生成 Angular 组件的资源(参见例如1、2、3、4,尤其是5),但最后它们都使用 Angular 的 API 将这些组件添加到 ViewContainer。但是,我想做的是使用 D3:

function generateAngularComponent(node: Node): MyNodeComponent 
    // Use Angular's component factory to dynamically create 
    // an instance of MyNodeComponent here and return it. See 
    // the links for code examples.


let nodes: Node[] = [node1, node2, node3, ...];
d3.select(".my-canvas").selectAll(".my-node").data(nodes).enter()
    .append(node => 
        let component = generateAngularComponent(node);
        // Somehow return the DOM of the component here, so that 
        // D3 adds it to the canvas
    );

我该如何做到这一点——不破坏我的MyNodeComponent、它的变更检测、依赖注入等等?这可能吗?

【问题讨论】:

【参考方案1】:

是的,有可能。我为我的应用程序创建了一个旭日形图。我创建了一个带有其类型的图表组件并将其封装在一个模块中。这是结构 -

 |charts.module.ts
 ├──sun-burst/
 |   ├──sun-burst-chart.component.ts
 |   ├──sun-burst-chart.component.html
 |   ├──sun-burst-chart.component.scss
 ├──some-other chart/
 |   ├──...ts

这是我的示例图表组件。我已经删除了不必要的细节。

import  Component, Input, ElementRef, Renderer, OnChanges  from '@angular/core';
import * as d3 from 'd3';    
export class SunburstChartComponent implements OnChanges 
        // inputs
        // objects related to chart like svg
        private htmlElement: HTMLElement;
        public constructor(public renderer: Renderer, public el: ElementRef) 
            this.htmlElement = el.nativeElement;
        

        public ngOnChanges() 
             // remove previously added element when data changes
            d3.select(this.htmlElement).selectAll('svg').remove();
            this.displayChart();
        
        public displayChart() 
            // this requires some additional work before chart can be prepared, refer d3 documentation for your component 
            this.svg = d3.select(this.htmlElement).append('svg')
                .attr('width', this.width)
                .attr('height', this.height)
                .append('g')
                .attr('transform', 'translate(' + this.width / 2 + ',' + (this.height / 2) + ')');

        
        // handle additional functions required for your chart like click, tween etc., refer samples for your component from d3 website.
        private click(d, arc)  
        
      

您需要保留在 d3 组件中添加的某些元素的引用以进行操作。它根据您创建的组件而有所不同。

我希望这会有所帮助。 如果您遇到任何问题,请在 stackblitz 上创建一个示例并分享。

【讨论】:

以上是关于使用 D3 在 DOM 中动态添加/删除 Angular 组件的主要内容,如果未能解决你的问题,请参考以下文章

D3.js入门 select选择器 元素的插入和删除 dataum和data 动态属性

D3.js入门 select选择器 元素的插入和删除 dataum和data 动态属性

使用 Vue 添加/删除动态 DOM 元素

角度 5 - 如何从 dom 中删除动态添加的组件

在版本 4 中将节点动态添加到 D3 Force Layout

JQuery动态创建删除DOM元素