【问题标题】:Call custom made function from svg element using AngularJs and D3.js使用 AngularJs 和 D3.js 从 svg 元素调用自定义函数
【发布时间】:2021-02-03 06:07:29
【问题描述】:

我目前正在使用 AngularJS 我正在尝试在 D3.js 中实现一个需要一些功能的图表。

所以我的第一步是尝试在我双击图表的一个元素时启动一些自定义函数。

起初我尝试添加一个 AngularJS 指令,如“ng-click”或“ng-dblclick”,但它根本不起作用。

现在我添加“.on("dblclick", function (d) { alert('You have clicked the circle.'); })" 到圆形元素,它正在工作,但前提是该功能类似于“警报”而不是像定制的东西

self.GoBackToPage = function (PageId) {
    viewModelHelper.navigateToPage(PageId);
};

无论如何我都不是 Javascript 专家,所以我不确定这里有什么问题,也许 self.GoBackToPage 没有在“self.update”操作的范围内定义,但我也尝试过在“self.update”中定义我的自定义函数,但没有成功。

当我尝试调用 GoBackToPage 而不是警报时,它在控制台中没有给我任何错误,看起来该函数甚至不存在。

function MyController($scope) {

  var self = this;
  self.model = {
    id: -1,
    links: {},
  };

  self.$onInit = function() {
    self.GetLinks(self.model.id);
    self.firstRender();
  };

  self.GoBackToPage = function(PageId) {
    viewModelHelper.navigateToPage(PageId);
  };

  var links = [];
  var nodes = {};
  var w = 1400,
    h = 900;
  var force;
  var svg;
  var paths;
  var circles;
  var texts;
  var pathLabels;

  self.GetLinks = function(id) {
    viewModelHelper.apiGet("Api/GetLinks/" + id,
      null,
      function(result) {
        links = result;
        self.update();
      }
    )
  };

  self.firstRender = function() {
    force = d3.layout.force()
      .size([w, h])
      .linkDistance(200)
      .charge(-1200)
      .on("tick", tick);

    svg = d3.select("body").append("svg:svg")
      .attr("width", w)
      .attr("height", h);

    // Per-type markers, as they don't inherit styles.
    svg.append("svg:defs").selectAll("marker")
      .data(["suit", "licensing", "resolved"])
      .enter().append("svg:marker")
      .attr("id", String)
      .attr("viewBox", "0 -5 10 10")
      .attr("refX", 15)
      .attr("refY", -1.5)
      .attr("markerWidth", 6)
      .attr("markerHeight", 6)
      .attr("orient", "auto")
      .append("svg:path")
      .attr("d", "M0,-5L10,0L0,5");

    paths = svg.append("svg:g");
    circles = svg.append("svg:g");
    texts = svg.append("svg:g");
    pathLabels = svg.append("svg:g");
  }

  self.update = function() {
    // Compute the distinct nodes from the links.
    links.forEach(function(link) {
      link.source = nodes[link.source] || (nodes[link.source] = {
        name: link.source
      });
      link.target = nodes[link.target] || (nodes[link.target] = {
        name: link.target
      });
    });

    force
      .nodes(d3.values(nodes))
      .links(links)
      .start();

    var path = paths.selectAll("path")
      .data(force.links());

    path.exit()
      .remove();
    path.enter()
      .append("svg:path");

    path.attr("id", function(d) {
        return d.source.index + "_" + d.target.index;
      })
      .attr("class", function(d) {
        return "link " + d.type;
      })
      .attr("marker-end", function(d) {
        return "url(#" + d.type + ")";
      });

    var circle = circles.selectAll(".circle")
      .data(force.nodes());

    circle.exit()
      .remove();
    circle.enter()
      .append("svg:circle")
      .attr("class", "circle")
      .attr("r", 16)
      .on("dblclick", function(d) {
        console.log('You have clicked the circle.');
      })
      .call(force.drag);

    var text = texts.selectAll("g")
      .data(force.nodes());

    text.exit()
      .remove();
    text.enter()
      .append("svg:text")
      .attr("x", 2)
      .attr("y", 50) //".31em"

    text.text(function(d) {
      return d.name;
    });

    var pathLabel = pathLabels
      .selectAll(".path_label")
      .data(force.links());

    pathLabel.exit().remove();
    pathLabel.enter()
      .append("svg:text")
      .attr("class", "path_label")
      .append("svg:textPath")
      .attr("startOffset", "50%")
      .attr("text-anchor", "middle")
      .style("fill", "#000")
      .style("font-family", "Arial");

    pathLabel
      .attr("xlink:href", function(d) {
        return "#" + d.source.index + "_" + d.target.index;
      })
      .text(function(d) {
        return d.type;
      });
  }

  // Use elliptical arc path segments to doubly-encode directionality.
  function tick() {
    paths.selectAll("path").attr("d", function(d) {
      var dx = d.target.x - d.source.x,
        dy = d.target.y - d.source.y,
        dr = Math.sqrt(dx * dx + dy * dy);
      return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
    });

    circles.selectAll("circle").attr("transform", function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    });

    texts.selectAll("text").attr("transform", function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    });
  }
}

angular.module("app", [])
  .controller("ctrl", MyController);

var viewModelHelper = {
  apiGet: function(a, b, then) {
    setTimeout(
      function() {
        console.log("Response from server");
        then(viewModelHelper.links);
      },
      2000
    );
  },
  navigateToPage: function(pageId) {
    console.log("Navigate to page", pageId);
  },
  links: [{
      source: "Microsoft",
      target: "Amazon",
      type: "licensing"
    },
    {
      source: "Microsoft",
      target: "HTC",
      type: "licensing"
    },
    {
      source: "Samsung",
      target: "Apple",
      type: "suit"
    },
    {
      source: "Motorola",
      target: "Apple",
      type: "suit"
    },
    {
      source: "Nokia",
      target: "Apple",
      type: "resolved"
    },
    {
      source: "HTC",
      target: "Apple",
      type: "suit"
    },
    {
      source: "Kodak",
      target: "Apple",
      type: "suit"
    },
    {
      source: "Microsoft",
      target: "Barnes & Noble",
      type: "suit"
    },
    {
      source: "Microsoft",
      target: "Foxconn",
      type: "suit"
    },
    {
      source: "Oracle",
      target: "Google",
      type: "suit"
    },
    {
      source: "Apple",
      target: "HTC",
      type: "suit"
    },
    {
      source: "Microsoft",
      target: "Inventec",
      type: "suit"
    },
    {
      source: "Samsung",
      target: "Kodak",
      type: "resolved"
    },
    {
      source: "LG",
      target: "Kodak",
      type: "resolved"
    },
    {
      source: "RIM",
      target: "Kodak",
      type: "suit"
    },
    {
      source: "Sony",
      target: "LG",
      type: "suit"
    },
    {
      source: "Kodak",
      target: "LG",
      type: "resolved"
    },
    {
      source: "Apple",
      target: "Nokia",
      type: "resolved"
    },
    {
      source: "Qualcomm",
      target: "Nokia",
      type: "resolved"
    },
    {
      source: "Apple",
      target: "Motorola",
      type: "suit"
    },
    {
      source: "Microsoft",
      target: "Motorola",
      type: "suit"
    },
    {
      source: "Motorola",
      target: "Microsoft",
      type: "suit"
    },
    {
      source: "Huawei",
      target: "ZTE",
      type: "suit"
    },
    {
      source: "Ericsson",
      target: "ZTE",
      type: "suit"
    },
    {
      source: "Kodak",
      target: "Samsung",
      type: "resolved"
    },
    {
      source: "Apple",
      target: "Samsung",
      type: "suit"
    },
    {
      source: "Kodak",
      target: "RIM",
      type: "suit"
    },
    {
      source: "Nokia",
      target: "Qualcomm",
      type: "suit"
    },
    {
      source: "Pippo",
      target: "Pippo"
    },
    {
      source: "Paperino",
      target: "Pippo",
      type: "suit"
    }
  ],
};
path {
  fill: none;
  stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<div ng-app="app" ng-controller="ctrl">
</div>

这里最后var viewModelHelper 是用来模拟ApiGet 调用GetLinks 的结果。

MyHTML.cshtml 中几乎没有 HTML,只有 d3 在 "self.firstrender" 中用于将 svg 元素附加到页面的这一行,HTML 的所有元素都是在 JS 中创建的:

    <bodyMYHTML></bodyMYHTML>

【问题讨论】:

    标签: javascript angularjs svg d3.js scope


    【解决方案1】:

    ng-clickng-dblclick 不起作用的原因是AngularJS $compiler 不知道这个元素已经被添加到DOM 中,所以它不能进行必要的准备来制作@987654324 @工作。

    但是由于您将代码插入到 AngularJS 控制器中,因此您不需要使用这些类型的 ng-* 指令。相反,您可以直接在函数内部访问selfviewModelHelper。这就是为什么以下内容才有效。

    我认为viewModelHelper 上可能没有navigateToPage 函数,或者您可能拼错了某些内容。我在您的示例中的任何地方都找不到PageId,所以我改用d.name

    function MyController($scope) {
    
      var self = this;
      self.model = {
        id: -1,
        links: {},
      };
    
      self.$onInit = function() {
        self.GetLinks(self.model.id);
        self.firstRender();
      };
    
      self.GoBackToPage = function(PageId) {
        viewModelHelper.navigateToPage(PageId);
      };
    
      var links = [];
      var nodes = {};
      var w = 1400,
        h = 900;
      var force;
      var svg;
      var paths;
      var circles;
      var texts;
      var pathLabels;
    
      self.GetLinks = function(id) {
        viewModelHelper.apiGet("Api/GetLinks/" + id,
          null,
          function(result) {
            links = result;
            self.update();
          }
        )
      };
    
      self.firstRender = function() {
        force = d3.layout.force()
          .size([w, h])
          .linkDistance(200)
          .charge(-1200)
          .on("tick", tick);
    
        svg = d3.select("body").append("svg:svg")
          .attr("width", w)
          .attr("height", h);
    
        // Per-type markers, as they don't inherit styles.
        svg.append("svg:defs").selectAll("marker")
          .data(["suit", "licensing", "resolved"])
          .enter().append("svg:marker")
          .attr("id", String)
          .attr("viewBox", "0 -5 10 10")
          .attr("refX", 15)
          .attr("refY", -1.5)
          .attr("markerWidth", 6)
          .attr("markerHeight", 6)
          .attr("orient", "auto")
          .append("svg:path")
          .attr("d", "M0,-5L10,0L0,5");
    
        paths = svg.append("svg:g");
        circles = svg.append("svg:g");
        texts = svg.append("svg:g");
        pathLabels = svg.append("svg:g");
      }
    
      self.update = function() {
        // Compute the distinct nodes from the links.
        links.forEach(function(link) {
          link.source = nodes[link.source] || (nodes[link.source] = {
            name: link.source
          });
          link.target = nodes[link.target] || (nodes[link.target] = {
            name: link.target
          });
        });
    
        force
          .nodes(d3.values(nodes))
          .links(links)
          .start();
    
        var path = paths.selectAll("path")
          .data(force.links());
    
        path.exit()
          .remove();
        path.enter()
          .append("svg:path");
    
        path.attr("id", function(d) {
            return d.source.index + "_" + d.target.index;
          })
          .attr("class", function(d) {
            return "link " + d.type;
          })
          .attr("marker-end", function(d) {
            return "url(#" + d.type + ")";
          });
    
        var circle = circles.selectAll(".circle")
          .data(force.nodes());
    
        circle.exit()
          .remove();
        circle.enter()
          .append("svg:circle")
          .attr("class", "circle")
          .attr("r", 16)
          .on("dblclick", function(d) {
            self.GoBackToPage(d.name)
          })
          .call(force.drag);
    
        var text = texts.selectAll("g")
          .data(force.nodes());
    
        text.exit()
          .remove();
        text.enter()
          .append("svg:text")
          .attr("x", 2)
          .attr("y", 50) //".31em"
    
        text.text(function(d) {
          return d.name;
        });
    
        var pathLabel = pathLabels
          .selectAll(".path_label")
          .data(force.links());
    
        pathLabel.exit().remove();
        pathLabel.enter()
          .append("svg:text")
          .attr("class", "path_label")
          .append("svg:textPath")
          .attr("startOffset", "50%")
          .attr("text-anchor", "middle")
          .style("fill", "#000")
          .style("font-family", "Arial");
    
        pathLabel
          .attr("xlink:href", function(d) {
            return "#" + d.source.index + "_" + d.target.index;
          })
          .text(function(d) {
            return d.type;
          });
      }
    
      // Use elliptical arc path segments to doubly-encode directionality.
      function tick() {
        paths.selectAll("path").attr("d", function(d) {
          var dx = d.target.x - d.source.x,
            dy = d.target.y - d.source.y,
            dr = Math.sqrt(dx * dx + dy * dy);
          return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
        });
    
        circles.selectAll("circle").attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        });
    
        texts.selectAll("text").attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        });
      }
    }
    
    angular.module("app", [])
      .controller("ctrl", MyController);
    
    var viewModelHelper = {
      apiGet: function(a, b, then) {
        setTimeout(
          function() {
            console.log("Response from server");
            then(viewModelHelper.links);
          },
          2000
        );
      },
      navigateToPage: function(pageId) {
        console.log("Navigate to page", pageId);
      },
      links: [{
          source: "Microsoft",
          target: "Amazon",
          type: "licensing"
        },
        {
          source: "Microsoft",
          target: "HTC",
          type: "licensing"
        },
        {
          source: "Samsung",
          target: "Apple",
          type: "suit"
        },
        {
          source: "Motorola",
          target: "Apple",
          type: "suit"
        },
        {
          source: "Nokia",
          target: "Apple",
          type: "resolved"
        },
        {
          source: "HTC",
          target: "Apple",
          type: "suit"
        },
        {
          source: "Kodak",
          target: "Apple",
          type: "suit"
        },
        {
          source: "Microsoft",
          target: "Barnes & Noble",
          type: "suit"
        },
        {
          source: "Microsoft",
          target: "Foxconn",
          type: "suit"
        },
        {
          source: "Oracle",
          target: "Google",
          type: "suit"
        },
        {
          source: "Apple",
          target: "HTC",
          type: "suit"
        },
        {
          source: "Microsoft",
          target: "Inventec",
          type: "suit"
        },
        {
          source: "Samsung",
          target: "Kodak",
          type: "resolved"
        },
        {
          source: "LG",
          target: "Kodak",
          type: "resolved"
        },
        {
          source: "RIM",
          target: "Kodak",
          type: "suit"
        },
        {
          source: "Sony",
          target: "LG",
          type: "suit"
        },
        {
          source: "Kodak",
          target: "LG",
          type: "resolved"
        },
        {
          source: "Apple",
          target: "Nokia",
          type: "resolved"
        },
        {
          source: "Qualcomm",
          target: "Nokia",
          type: "resolved"
        },
        {
          source: "Apple",
          target: "Motorola",
          type: "suit"
        },
        {
          source: "Microsoft",
          target: "Motorola",
          type: "suit"
        },
        {
          source: "Motorola",
          target: "Microsoft",
          type: "suit"
        },
        {
          source: "Huawei",
          target: "ZTE",
          type: "suit"
        },
        {
          source: "Ericsson",
          target: "ZTE",
          type: "suit"
        },
        {
          source: "Kodak",
          target: "Samsung",
          type: "resolved"
        },
        {
          source: "Apple",
          target: "Samsung",
          type: "suit"
        },
        {
          source: "Kodak",
          target: "RIM",
          type: "suit"
        },
        {
          source: "Nokia",
          target: "Qualcomm",
          type: "suit"
        },
        {
          source: "Pippo",
          target: "Pippo"
        },
        {
          source: "Paperino",
          target: "Pippo",
          type: "suit"
        }
      ],
    };
    path {
      fill: none;
      stroke: black;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
    <div ng-app="app" ng-controller="ctrl">
    </div>

    【讨论】:

      猜你喜欢
      • 2016-04-01
      • 2018-02-27
      • 2012-06-15
      • 2014-03-14
      • 1970-01-01
      • 1970-01-01
      • 2017-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多