我遵循了这个示例D3Integration,这是在OpenMUC框架中为我在Angular中使用正确的D3自定义可视化的唯一方法。
我有几个带ID的设备。我设法为条形图分开数据,这些数据是独立更新的。当然我只想为特定设备显示一个条,但控制器保存完整的数据。
是否可以仅显示特定栏(通过索引或过滤器)? ng-Model与索引完美配合:X。在data =“d3Data [deviceIndex]”上设置索引会引发错误。有趣的是,只是deviceIndex的栏消失了(由于错误),另一个工作正常。
此:
<div class="row">
<div class="col-md-6 col-md-offset-3" ng-controller="DemoCtrl">
<span>{{title}}</span>
<d3-bars data="d3Data" label="name" on-click="d3OnClick(item)"></d3-bars>
<br><input type="text" ng-model="d3Data[deviceIndex].value"></input>
</div>
</div>
我很确定它可以通过良好的指令函数定义或类似的东西来管理,但我无法做到:(
用ng-repeat包装以扫描设备:
<div ng-repeat="(deviceIndex,device) in checkedDevices track by $index" ng-switch on= "device.id | limitTo : 3">
我的指令看起来像这样(很少修改D3Integration示例):
(function () {
'use strict';
angular.module('myAppDirective')
.directive('d3Bars', ['d3', function(d3) {
return {
restrict: 'EA',
scope: {
data: "=",
label: "@",
onClick: "&",
onLoad: "&" //can i define something like this?
},
link: function(scope, iElement, iAttrs) {
var svg = d3.select(iElement[0])
.append("svg")
.attr("width", "100%");
// on window resize, re-render d3 canvas
window.onresize = function() {
return scope.$apply();
};
scope.$watch(function(){
return angular.element(window)[0].innerWidth;
}, function(){
return scope.render(scope.data);
}
);
// watch for data changes and re-render
scope.$watch('data', function(newVals, oldVals) {
return scope.render(newVals);
}, true);
//here we catch a broadcast from demoCtrl. In obj currently all Data (d3Data) is inside.
scope.$on('onRerender', function(event, obj) {
//TODO: Solve this dependend on index?!
return scope.data = obj; //triggers $watch
})
// define render function
scope.render = function(data){
// remove all previous items before render
svg.selectAll("*").remove();
// setup variables
var width, height, max;
width = d3.select(iElement[0])[0][0].offsetWidth - 10;
// 20 is for margins and can be changed
height = scope.data.length * 35;
// 35 = 30(bar height) + 5(margin between bars)
max = 250;
// this can also be found dynamically when the data is not static
// max = Math.max.apply(Math, _.map(data, ((val)-> val.count)))
// set the height based on the calculations above
svg.attr('height', height);
//create the rectangles for the bar chart
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.on("click", function(d, i){
return scope.onClick({item: d}); // this is how it worked before
})
.on("load", function(d,i){
return scope.onLoad({item: d, index: i});
})
.attr("height", 30) // height of each bar
.attr("width", 0) // initial width of 0 for transition
.attr("x", 10) // half of the 20 side margin specified above
.attr("y", function(d, i){
return i * 35;
}) // height + margin between bars
.transition()
// .duration(1000) // time of duration
.duration(0) // time of duration
.attr("width", function(d){
return d.value*2/(max/width);
}); // width based on scale
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("fill", "#555")
.attr("y", function(d, i){return i * 35 + 22;})
.attr("x", 15)
.text(function(d){return d[scope.label];});
};
scope.getData = function(indexOfHTML){
return scope.data[indexOfHTML];
};
}
};
}]);
}());
(function () {
'use strict';
angular.module('myAppControllers')
.controller('DemoCtrl', ['$scope', function($scope){
$scope.title = "DemoCtrl";
$scope.d3Data = [];
$scope.d3OnClick = function(item){
console.log(item.name);
};
//tried something here
$scope.d3OnLoad = function(item, index){
console.log(item.name + " | " + index.i);
};
console.log("create initial data:" );
for(var i = 1; i <= 4; i++){
if(i < 10){
$scope.d3Data.push({name: "voltageL1_0x00" + i + "0", value:i});
}else{
$scope.d3Data.push({name: "voltageL1_0x0" + i + "0", value:i});
}
}
$scope.$on('onSearch', function(event, obj) {
for(var len = $scope.d3Data.length, i=0; i<len; ++i){
if(obj[0].name == $scope.d3Data[i].name){
$scope.d3Data[i] = obj[0];
}
};
$scope.$broadcast('onRerender', $scope.d3Data);
});
}]);
}());