我想创建一个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
更新
我想将控件和形状绑定在一起,这意味着由于更改绑定到模型的控件的值而更改了形状坐标,反之亦然。
答案 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,这不会导致父作用域,因为对象位于隔离范围内。
我已经为你做了一件事:
<!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;
答案 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链接以获取完整代码