如何通过 D3 单击事件让 $rootScope.$emit() 和 ng-show 协同工作?

How do I get $rootScope.$emit() and ng-show to work together via a D3 click event?

我在 angular 指令中包装 D3 svg 图像。当用户单击该 D3 图像时,我想在控制器中将变量设置为 true,以便 ng-show 然后显示另一个 D3 图像。

我所做的是向 D3 图像添加一个 .on("click") 函数,并在该函数中使用 $rootScope.$emit() 发送一个事件。在第二张图片的控制器中,我有一个 $rootScope.$on() 来控制事件并将 ng-show 的变量设置为 true。

这种方法行不通。我已经测试了代码以确保事件被正确发出和捕获,但是 ng-show 没有显示第二个 D3 图像。

这是为什么?

我创建了一个小插件来说明我正在尝试做什么。 http://plnkr.co/edit/FnqeAF9kVXxdUdOzT5to.

控制器代码如下:

function CircleCtrl($rootScope) {

    this.render = function(element, attrs) {
        var svg = d3.select(element[0]).append("svg")
            .attr("width", 200)
            .attr("height", 200);

        var circle = svg.append("circle")
            .attr("cx", 30)
            .attr("cy", 30)
            .attr("r", 20);
        circle.on("click", function(d,i) {
            var data = {val0: "zero", val1: "one"};
            $rootScope.$emit("Circle Clicked", data);
        });
    };

}

function SquareCtrl($rootScope) {
    this.showSquare = false;

    $rootScope.$on("Circle Clicked", function(event, data) {
        this.showSquare = true;
    })

    this.render = function(element, attrs) {
        var svg = d3.select(element[0]).append("svg")
            .attr("width", 200)
            .attr("height", 200);

        var rectangle = svg.append("rect")
            .attr("x", 10)
            .attr("y", 10)
            .attr("width", 50)
            .attr("height", 100);
    };

    }

    angular.module("AngularD3Example")
        .controller("CircleCtrl", ["$rootScope", CircleCtrl])
        .controller("SquareCtrl", ["$rootScope", SquareCtrl]);

从外部 Angular 上下文更改 angular 的范围不会更新范围变量和绑定,因为它不会 运行 Angular.In 的摘要循环正在使用事件 click 到 angular 上下文。

您需要运行 $apply() 范围上的方法到$rootScope 到运行 摘要循环将有效地更新绑定。

代码

circle.on("click", function(d,i) {
    var data = {val0: "zero", val1: "one"};
    $rootScope.$emit("Circle Clicked", data);
    $rootScope.$apply();
});

另一件事是你需要确保在使用 this 关键字时你应该用一些变量在外面声明它然后使用它

控制器

function SquareCtrl($rootScope) {
    var square = this; //made this as a global can be accessible through `square` variable 
    square.showSquare = false; //<-- as changed this to square

    $rootScope.$on("Circle Clicked", function(event, data) {
        square.showSquare = true; //<-- as changed this to square
    })

    //....

}

您还必须取消注册 $rootScope 侦听器,否则会导致您的应用程序内存泄漏。 .on 监听器的 return 值实际上是你在 $destroy 事件发生时使用的函数,以实现此

// Start Custom Events
var cleanApplyEvent = $rootScope.$on('submitChanges', function(event, args) {
    self.applyData(args);
})

// manually unregister rootScope listeners to avoid memory leaks
$scope.$on('$destroy', function(){
    cleanApplyEvent();
})
// End Custom Events

Working Plunkr

Note

Its bad practice to have DOM manipulation inside controller, you should create a directive an do DOM manipulation and events should be handle from there