What would be optimal way to change flot chart dat

2020-04-14 06:53发布

问题:

I am using angularjs flot and slider to show some chart and based on range selection the data visualization should change in flot chart.

My question is what would be the best way to visualize data depending on range selection? Do I need to add/delete flot chart data everytime a range is selected or is there a better way? I am providing my source code below and some screenshots. Please help.

Note: $scope.tasksRunDataChartObject; has json data from server formatted for flot chart

Slider screenshot and code

<rzslider rz-slider-floor="reportTasksRunRange.floor" 
    rz-slider-ceil="reportTasksRunRange.ceil" 
    rz-slider-model="reportTasksRunRange.min" 
    rz-slider-high="reportTasksRunRange.max" 
    rz-slider-translate="translate" rz-slider-step="{{reportTasksRunRange.step}}"></rzslider>

<flot dataset="tasksRunData" options="tasksRunChartOptions" class="center-block" width="100%" height="400px" ></flot>

$scope.reportTasksRunRange = {
    min: 1412380800000,
    max: 1412812800000,       
    floor: 1412380800000,
    ceil: 1412812800000,
    step: 1412467200000-1412380800000
};

$scope.translate = function(value) {
    var monthNames = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
    var myDate = new Date( value );
    return myDate.getDate() + " " + monthNames[myDate.getMonth()] + " '"+myDate.getFullYear();
}

$scope.$on('slideEnded', function () {
     console.log("slideEnded Event Fired : " +$scope.translate($scope.reportTasksRunRange.min)+" - "+$scope.translate($scope.reportTasksRunRange.max));
     $scope.redrawTasksRunDataHistoByChart($scope.translate($scope.reportTasksRunRange.min), $scope.translate($scope.reportTasksRunRange.max));             
});

$scope.redrawTasksRunDataHistoByChart = function(min, max) {
    var mainArray = $scope.tasksRunDataChartObject;
    console.log("mainArray");
    console.log(mainArray);
    /*var dataArray = mainArray[0]["data"];        
    dataArray.splice(2,1);
    mainArray[0]["data"] = dataArray;
    mainArray[0]["data"] = mainArray[0]["data"].splice(2,1);
    $scope.tasksRunData = mainArray;
    console.log(mainArray);*/
}

Json data from server

{
    "date_histo_agg_by_type": {
        "took": 332,
        "timed_out": false,
        "_shards": {
            "total": 5,
            "successful": 5,
            "failed": 0
        },
        "hits": {
            "total": 14868,
            "max_score": 0.0,
            "hits": []
        },
        "aggregations": {
            "task_type": {
                "buckets": [{
                    "key": "DSS",
                    "doc_count": 14868,
                    "run_over_time": {
                        "buckets": [{
                            "key_as_string": "2014-10-04T00:00:00.000Z",
                            "key": 1412380800000,
                            "doc_count": 477
                        },
                        {
                            "key_as_string": "2014-10-05T00:00:00.000Z",
                            "key": 1412467200000,
                            "doc_count": 3015
                        },
                        {
                            "key_as_string": "2014-10-06T00:00:00.000Z",
                            "key": 1412553600000,
                            "doc_count": 2988
                        },
                        {
                            "key_as_string": "2014-10-07T00:00:00.000Z",
                            "key": 1412640000000,
                            "doc_count": 3123
                        },
                        {
                            "key_as_string": "2014-10-08T00:00:00.000Z",
                            "key": 1412726400000,
                            "doc_count": 2970
                        },
                        {
                            "key_as_string": "2014-10-09T00:00:00.000Z",
                            "key": 1412812800000,
                            "doc_count": 2295
                        }]
                    }
                }
            }
        }
    }
}

Chrome debug screenshot

Update

ReportService.getTasksRunDateHistoByType().then(function(result) {
    $scope.renderTasksRunDateHistoByType(result);
});

$scope.renderTasksRunDateHistoByType = function(json) {

    var buckets = json[RUN_AGG_BY_DATE_HISTO].aggregations[TASK_TYPE_AGG].buckets;
    var log = [];
    var mainArray = [];
    var colorCodes = ["#7BC253","#9C77D7","#399CEA","#FF6244","#FF7FB5","#00D3AB","#FFCC4C","#193441","#193441","#BEEB9F","#E3DB9A","#917A56"],
        idx = 0;
    angular.forEach(buckets, function(value, key) {        
        this.push(key + ': ' + value +", "+value["key"]);
        var dataArray = [], index = 0;
        angular.forEach(value[RUN_OVER_TIME_KEY]["buckets"], function(value, key) {
            var dataArr = [];
            dataArr.push('['+value["key"]+', '+value["doc_count"]+']');
            dataArray.push(dataArr);
            index++;
        }, log);
        var barObject = '"bars": {"show": "true", "barWidth":'+23*60*60*1000+', "fillColor": "'+colorCodes[idx]+'", "order": 1, "align": "center"}';            
        var object = '{ "data": ['+dataArray+'], "label": "'+value["key"]+'", '+barObject+'}';            
        mainArray.push(JSON.parse(object));
        idx++;
    }, log);
    $scope.tasksRunData = mainArray;
    $scope.tasksRunChartOptions = {
        legend: {
            show: true,
            margin: 2
        },
        xaxis: {
            mode: "time", timeformat: "%m/%d/%y", minTickSize: [1, "day"]
        },
        grid: {
            labelMargin: 10,
            hoverable: true,
            borderWidth: 0
        },
        series: {
            stack: true
        },
        colors: colorCodes,
        tooltip: true
    };
    return mainArray;
}

Angularjs service

angular.module('myApp')
.service('ReportService', function ReportService($http, $q) {

    var getTasksRunDateHistoByType = function() {
        var deferred = $q.defer();
        $http({
            method: 'POST',
            url: "http://localhost:4040/reports/taskRun",
            data: '{ "client_user_info": { "client_id": "MU03"}}'
        }).
        success(function(result, status, headers, config) {
            deferred.resolve(result);
        }).
        error(function(result, status, headers, config) {
            console.log("Error");
        });
        return deferred.promise;
    };

    return {
        getTasksRunDateHistoByType: getTasksRunDateHistoByType
    };
});

flot chart

回答1:

From your question, it looks like you are making an AJAX call on the slideEnded event and swapping out the data on your chart. This will work but it'll be slow and won't provide the best user experience. Instead, I would draw the entire chart with all the data. I'd then adjust the min/max of the chart when the user slides and redraw. This way you only make one AJAX on load to populate all the data and the chart updates quickly.

BUT, we have a problem. The angular directive you are working with only watches one thing to trigger a proper flow redraw -- the plot's dataset. Wouldn't it be awesome, though, if it could also watch your $scope.reportTasksRunRange.min and $scope.reportTasksRunRange.max and if those changed trigger a redraw? This really is the heart of angularjs databinding!

So, let's "fix" the directive. All you would need to do is first, change the scope:

scope: {
  dataset: '=',
  min: '=',
  max: '=',
  options: '=',
  callback: '='
},

And add a couple of $watch:

  onMinChanged = function(min) {
    if (plot) {
      plot.getOptions().xaxes[0].min = min;
      plot.setupGrid();
      return plot.draw();
    }
  };
  onMaxChanged = function(min) {
    if (plot) {
      plot.getOptions().xaxes[0].max = max;
      plot.setupGrid();
      return plot.draw();
    }
  };
  scope.$watch('min', onMinChanged, true);
  scope.$watch('max', onMaxChanged, true);

Here's my example putting this together.

The power of Angularjs...