Я добавляю линейные диаграммы D3 на страницу, используя ng-repeat в угловом. Внутри ng-repeat есть директива диаграммы, в которой используются данные из ng-repeat.разделение оси X между диаграммами d3 в угловом
Я добавляю данные в ng-repeat динамически на основе ng-click. Это отлично работает, но мне нужны все диаграммы для совместного использования одной оси X. Мне нужно, чтобы ось X обновлялась при добавлении и удалении данных из ng-repeat.
Текущий код моей директивы: В принципе, мне нужно будет установить ось X для всех диаграмм в ng-repeat на основе дат внутри 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
};
};