2016-03-14 3 views
0

Я добавляю линейные диаграммы 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 
    }; 
}; 

ответ

0

Я решил эту проблему, создав отдельный массив дат, который все диаграммы используют для своего домена x.

Каждый раз, когда новый элемент добавляется в график ng-repeat, их даты также переносятся в другой массив, сортируемый по дате.

Я настроил $ часов в директиве, которая смотрит на массиве даты и перерисовывает все графики, когда меняется этот массив

Директивы смотреть

$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 scope 
Смежные вопросы