我是添加D3线图表到一个页面使用纳克重复角.在ng-重复中有一个图表指令,它使用来自ng-重复的数据.
我是添加数据到ng-重复的动态基础上的ng-点击。这很好,但是我需要所有的图表来共享同一个X轴。当数据被添加并从ng-重复中删除时,我需要更新X轴。
我的指令的当前代码:基本上,我将需要为ng重复中的所有图表设置X轴,基于stackedChartData中的日期而不是每个单独的图表。
<div ng-repeat="chart in stackedChartData">
<stacked-chart chart-data="chart.list" x="date" y="value"
width="600" height="270"
margin="{top: 40, right:20, bottom:50, left:40}"></stacked-chart>
</div>
function StackedChartController($scope, $element, $attrs){
this.x = $scope.x;
this.y = $scope.y;
this.xLabel = ($scope.xlabel || capitalize(this.x));
this.yLabel = ($scope.ylabel || capitalize(this.y));
this.height = $scope.height;
this.width = $scope.width;
this.margin = $scope.margin;
this.data = $scope.chartData || [];
this.xScale = null;
this.yScale = null;
this.svg = null;
this.svg = d3.select($element[0])
.append("svg")
.attr("width", this.width)
.attr("height", this.height)
.append("g")
.attr("transform",
"translate(" + this.margin.left + "," + this.margin.top + ")");
this.innerWidth = this.width - this.margin.left - this.margin.right;
this.innerHeight = this.height - this.margin.top - this.margin.bottom;
$scope.$watch("chartData", (function(newVal, oldVal) {
this.data = newVal;
this.xDomain = (typeof $scope.xdomain === 'function' ? $scope.xdomain : xDomainCommand);
this.yDomain = (typeof $scope.ydomain === 'function' ? $scope.ydomain : yDomainCommand);
// Redraw the graph after new data loads.
this.drawAxes();
this.append($scope.lines || [], this.data);
this.plotData(this.data);
}).bind(this));
/* Debugging
$scope.$watch("ydomain", (function(newVal, oldVal) {
console.log('ydomain', newVal, oldVal);
}).bind(this));
*/
var x = this.x,
y = this.y;
var xDomainCommand = function(data, d3){
data = data || [];
return [d3.min(data, function(d) {
return d[x];
}), d3.max(data, function(d) {
return d[x];
})];
};
var yDomainCommand = function(data, d3){
data = data || [];
return [
((d3.min(data, function(d){
return d[y];
}) - 1)), (d3.max(data, function(d){
return d[y];
}) + 1)
];
}
// Setting these methods to default functions.
// But these will often be override by different scopes
// which need to be able specify different different functions
// for caclulating the data ranges.
this.xDomain = xDomainCommand;
this.yDomain = yDomainCommand;
}
/**
* Plotting the data (an array of objects) passed to the function.
* @param data
*/
StackedChartController.prototype.plotData = function(data){
data = data || [];
var xScale = this.xScale.bind(this);
var yScale = this.yScale.bind(this);
var x = this.x;
var y = this.y;
var valueline = d3.svg.line()
.x(function(d) {
return xScale(d[x]);
})
.y(function(d) {
return yScale(d[y]);
});
// Add the valueline path.
this.svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the points
this.svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) {
return xScale(d[x])
})
.attr("cy", function(d) {
return yScale(d[y]);
})
// Tooltip
.append("svg:title")
.text((function(d){
if (typeof this.tooltip === 'function'){
return this.tooltip(d)
} else {
return null;
}
}).bind(this));
};
/**
* Sets the size of the graph (range) and max/min values to plot (domain)
*/
StackedChartController.prototype.setParams = function(){
// Set the ranges
this.xScale = d3.time.scale().range([0, this.innerWidth]);
//xScale = d3.scale.linear().range([0, width]);
this.yScale= d3.scale.linear().range([this.innerHeight, 0]);
// Scale the range of the data
this.xScale.domain(this.xDomain(this.data, d3));
this.yScale.domain(this.yDomain(this.data, d3));
// Define the axes
var axisGenerators = {};
axisGenerators.x = d3.svg.axis().scale(this.xScale)
.orient("bottom").ticks(5);
axisGenerators.y = d3.svg.axis().scale(this.yScale)
.orient("left").ticks(5);
return axisGenerators;
};
/**
* Adds extra lines beyond the initial one created by the initial dataset.
*
*/
StackedChartController.prototype.append = function(lines, data){
data = data || [];
var xScale = this.xScale.bind(this);
var yScale = this.yScale.bind(this);
lines.forEach(function(item){
var valueline = d3.svg.line()
.x(function(d) {
return xScale(d[item.x]);
})
.y(function(d) {
return yScale(d[item.y]);
});
this.svg.append("path")
.attr("class", "line")
.style("stroke", item.color || 'black')
.attr("d", valueline(this.data));
if (item.showPoints === true) {
this.svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function (d) {
return xScale(d[item.x])
})
.attr("cy", function (d) {
return yScale(d[item.y]);
})
}
}, this);
};
/**
* Clears the graph before redrawing the axes.
*/
StackedChartController.prototype.drawAxes = function(){
var svg = this.svg;
// Remove original lines drawn on the axes.
// PF (2016/03/04) This is interfering with multiple datasets.
svg.selectAll('*').remove();
var axisGenerators = this.setParams();
// Add the X Axis
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + this.innerHeight + ")")
.call(axisGenerators.x);
// text label for the x axis
// Add the Y Axis
svg.append("svg:g")
.attr("class", "y axis")
.call(axisGenerators.y);
};
return {
restrict: 'E',
replace: true,
scope: {
x: '@',
y: '@',
height: '=',
width: '=',
margin: '=',
chartData: '=',
tooltip: '=',
lines: '=',
xdomain:'=',
ydomain: '='
},
template: '<svg></svg>',
controller: StackedChartController
};
};发布于 2016-03-24 21:28:10
我通过创建一个单独的日期数组来解决这个问题,所有的图表都使用这些日期作为x域。
每次将新项添加到图表ng重复时,它们的日期也会被推入另一个按日期排序的数组中。
我在指令中设置了一个$watch,用于查看日期数组,并在此数组更改时重新绘制所有图形。
指令监视
$scope.$watch('at', (function (newVal, oldVal) {
this.xDomain = $scope.xdomain;
this.yDomain = (typeof $scope.ydomain === 'function' ? $scope.ydomain : yDomainCommand);
// Redraw the graph after new data loads.
drawAxes();
plotData(this.data);
append($scope.lines || [], this.data);
$compile($element)($scope);
}).bind(this));我需要访问指令范围之外的数据,所以我在parentData中使用了下面的语法
指令范围
scope: {
x: '@',
y: '@',
height: '=',
width: '=',
margin: '=',
chartData: '=',
at: '=parentData', // used to access outside scopehttps://stackoverflow.com/questions/35991449
复制相似问题