在指令和控制器之间使用通用范围

时间:2015-08-31 18:33:51

标签: javascript angularjs

我想创建一个svg形状,这是一个圆形并阅读规范,例如:半径或中心坐标,来自html文档中存在的应用程序范围:

<div ng-app="myApp" >
<div id="body"  ng-controller="testCtrl">
    cx: <input type="number" ng-model="cx"/><br/>
    cy: <input type="number" ng-model="cy"/><br/>
    R : <input type="number" ng-model="radius" />

    <draggable-circle/>
</div>
</div>

该应用程序的控制器也是:

 var testCtrl = angularApp.controller('testCtrl', function ($scope) {
            $scope.cx = 100, $scope.cy = 100, $scope.radius = 50;
     });

指令是:

 testCtrl.directive('draggableCircle', function () {
       function link(scope, el, attr) {
        var w = 600, h = 400;
        var drag = d3.behavior.drag().origin(Object).on("drag", dragmove);
           var svg = d3.select(el[0]).append("svg")
                        .attr("class", "svgContainer")
                        .attr("width", w)
                        .attr("height", h);
         var newg = svg.append("g").data([{ x: scope.cx , y: scope.cy}]);

         var dragCircle = newg.append("circle")
                        .attr("r", scope.radius)
                        .attr("cx", function(d) {
                            return d.x;
                        })
                        .attr("cy", function (d) {
                            return d.y;
                        })
                        .attr("fill", "red")
                        .style("fill-opacity", 0.8)
                        .style("stroke", "steelblue")
                        .style("cursor"," pointer")
                        .call(drag);
         function dragmove(d) {
            var mousePosition = d3.mouse(this);
           dragCircle.attr("cx", d.x =scope.cx= mousePosition[0])
               .attr("cy", d.y=scope.cy = mousePosition[1]);
                        scope.$apply()
         }
       }
         return {
                    link: link
                };
     });

问题是,当控制器改变坐标值时,必须在指令中更改形状。但它没有像我预期的那样工作。此外,当用户拖动形状时,由于使用scope.$apply(),指令中更改的值会自动广播到控制器和模型,我认为这不是最佳解决方案。 我想进行双工连接,即在指令中更改规范时,它会影响控制器,反之亦然。此外,我想为此目标实施最佳解决方案。

Plunker链接:http://plnkr.co/edit/3itxi9DSvGHJzyRRvrUr?p=preview

更新

我想将控件和形状绑定在一起,这意味着由于更改绑定到模型的控件的值而更改了形状坐标,反之亦然。

5 个答案:

答案 0 :(得分:0)

三安,

您可以使用名为accessor的概念。它工作得很好,它是一个轻量级的解决方案。您无需过度使用$watch$broadcast$emit

答案 1 :(得分:0)

你的问题根本不是关于范围的;问题是您将dragmove函数注册为回调函数。 此回调未在AngulasJs环境中注册,这就是您需要使用$apply的原因。当此回调对范围变量进行更改时,除非您调用$apply,否则角度框架无法识别它。当您使用非角度库以及角度代码时,这是一个常见问题。

我认为你是正确的;可能现在你需要的就是

function updatePosition(){ 
    dragCircle.attr("cx", d.x =scope.cx)
           .attr("cy", d.y=scope.cy);
}

$scope.$watch('cx', updatePosition);
$scope.$watch('cy', updatePosition);

如果有任何问题请告诉我,因为我从未使用过d3.js,所以我可能不知道那里发生的其他事情。

答案 2 :(得分:0)

您应该阅读isolated scope。之后,您需要在指令中监视控制器中的$属性,并在应用它们之后生成$ scope.apply,这不会导致父作用域,因为对象位于隔离范围内。

我已经为你做了一件事:

&#13;
&#13;
<!DOCTYPE html>
<html>

<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.4/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
</head>

<body>
<div ng-app="myApp">
    <div id="body" ng-controller="testCtrl">
        cx:         <input type="number" ng-model="initial.cx" />
        <br />

        cy:         <input type="number" ng-model="initial.cy" />
        <br />

        R :         <input type="number" ng-model="initial.radius" />
        <draggable-circle initial="initial"></draggable-circle>
    </div>
</div>
<script>

    var angularApp = angular.module('myApp', []);

    var testCtrl = angularApp.controller('testCtrl', function ($scope) {
        $scope.initial = {};
        $scope.initial.cx = 100;
        $scope.initial.cy = 100;
        $scope.initial.radius = 50;
    });

    testCtrl.directive('draggableCircle', function () {
        function link(scope, el, attr) {

            var w = 600, h = 400;
            var drag = d3.behavior.drag().origin(Object).on("drag", dragmove);

            var svg = d3.select(el[0]).append("svg")
                    .attr("class", "svgContainer")
                    .attr("width", w)
                    .attr("height", h);
            var newg = svg.append("g").data([{ x: scope.initial.cx , y: scope.initial.cy}]);

            var dragCircle = newg.append("circle")
                    .attr("r", scope.initial.radius)
                    .attr("cx", function(d) {
                        return d.x;
                    })
                    .attr("cy", function (d) {
                        return d.y;
                    })
                    .attr("fill", "red")
                    .style("fill-opacity", 0.8)
                    .style("stroke", "steelblue")
                    .style("cursor"," pointer")
                    .call(drag);

            scope.$watch('initial.cx', applyNewProperties);
            scope.$watch('initial.cy', applyNewProperties);

            function applyNewProperties() {
                dragCircle.attr("cx", scope.initial.cx)
                        .attr("cy", scope.initial.cy);
            }


            function dragmove(d) {
                console.log(scope.initial);
                var mousePosition = d3.mouse(this);
                dragCircle.attr("cx", d.x =scope.cx = scope.initial.cx = mousePosition[0])
                        .attr("cy", d.y=scope.cy =  scope.initial.cy =mousePosition[1]);
                scope.$apply()
            }
        }
        return {
            link: link,
            scope: {
                initial: '='
            }
        };

    });
</script>
</body>

</html>
&#13;
&#13;
&#13;

答案 3 :(得分:0)

我阅读了所有的答案,他们接近解决方案,但没有一个是正确的。所以,我把Plunker改成了这个:

scope.$watch('coord', updateCoord,true);

function updateCoord() {
      dragCircle.attr("cx", scope.coord.cx)
        .attr("cy", scope.coord.cy).attr("r",scope.coord.radius);
 }


function dragmove(d) {
    var mousePosition = d3.mouse(this);
    dragCircle.attr("cx", d.x  = scope.coord.cx = mousePosition[0])
         .attr("cy", d.y =  scope.coord.cy =mousePosition[1]);
                scope.$apply();
 }

关键是要观察模型的变化,您需要使用$watch(... ,true)来监控所有变化。

这是新的Punkler:http://plnkr.co/edit/X9uDMLmSUuCD1bLdv5Ml?p=preview

答案 4 :(得分:0)

我分叉了你的plunker

添加了更新功能和一个观察者。它现在正在运作

 function update() {
    dragCircle = newg.selectAll("circle")
                .attr("r", scope.data.radius)
                .attr("cx", scope.data.cx)
                .attr("cy", scope.data.cy)
                .call(drag);
 }    

请查看plunker链接以获取完整代码