动态创建ChartJS

时间:2016-04-18 12:27:55

标签: javascript angularjs json charts

我有一个简单的表单,用户选择日期和日期,然后工具自动从网站检索数据(它返回一个JSON)。

以下是我的角度控制器的样子:

(function () {
    angular.module("app-machines", ['ngFlatDatepicker'])
        .factory('MachinesService', ['$http', MachinesService])
        .controller('mainController', ['$scope', 'MachinesService', mainController]);

    function mainController($scope, MachinesService) {
        $scope.datepickerConfig_From = {
            allowFuture: true,
            dateFormat: 'DD.MM.YYYY',
            minDate: moment.utc('2015-09-13'),
            maxDate: moment.utc('2015-09-17')
        };

        $scope.datepickerConfig_To = {
            allowFuture: true,
            dateFormat: 'DD.MM.YYYY',
            minDate: moment.utc('2015-09-13'),
            maxDate: moment.utc('2015-09-17')
        };

        $scope.date_from = "14.09.2015";
        $scope.date_to = "15.09.2015";

        $scope.machines = [];
        $scope.errorMessage = "";

        $scope.change = function () {
            MachinesService.getMachines($scope.date_from, $scope.date_to).then(function (response) {
                angular.copy(response.data, $scope.machines);
            }, function (error) {
                $scope.errorMessage = "Failed to load data:" + error;
            });
        };

        $scope.change();
    }

我的getMachines中我在调用一个看起来或多或少像这样的简单GET请求(示例):

return $http.get("/api/machine/2015-09-14_2015-09-16");

返回的JSON是具有以下结构的对象数组(仅供参考)

  • 机器名称
    • 类别(在特定机器下)
      • 天(每个类别包含带有数据的天数集合)

我现在可以毫无问题地检索数据(得到了大家的帮助)。我现在正在尝试为每台退回的机器显示一个图表。这意味着在我的页面上我试图做这样的事情:

        <div class="col-md-12" ng-repeat="machine in machines">
            <h1> {{ machine.name }}</h1>

            <div class="col-md-6" ng-repeat="category in machine.categories">
                <h3> {{ category.name }}</h3>

                <div class="col-md-6" ng-repeat="day in category.days">
                    <p>{{day.date | date : 'dd.MM' }}</p>
                </div>
            </div>

        </div>

这里(只是简单的例子)我在循环机器,我正在显示类别与天。我不想显示类别(天数),而只想插入带有数据的条形图。

我发现ChartJs允许我这样做。这是我的示例脚本,它在我的页面上显示一个图表:

var data = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [
        {
            label: "My First dataset",

            // The properties below allow an array to be specified to change the value of the item at the given index
            // String  or array - the bar color
            backgroundColor: "rgba(100,220,220,0.2)",

            // String or array - bar stroke color
            borderColor: "rgba(220,220,220,1)",

            // Number or array - bar border width
            borderWidth: 1,

            // String or array - fill color when hovered
            hoverBackgroundColor: "rgba(220,220,220,0.2)",

            // String or array - border color when hovered
            hoverBorderColor: "rgba(220,220,220,1)",

            // The actual data
            data: [65, 59, 80, 81, 56, 55, 40],

            // String - If specified, binds the dataset to a certain y-axis. If not specified, the first y-axis is used.
            yAxisID: "y-axis-0",
        },
        {
            label: "My Second dataset",
            backgroundColor: "rgba(220,220,220,0.2)",
            borderColor: "rgba(220,220,220,1)",
            borderWidth: 1,
            hoverBackgroundColor: "rgba(220,220,220,0.2)",
            hoverBorderColor: "rgba(220,220,220,1)",
            data: [28, 48, 40, 19, 86, 27, 90]
        }
    ]
};
var options = {
    scales: {
        xAxes: [{
            stacked: true
        }],
        yAxes: [{
            stacked: true
        }]
    }
};

var ctx = document.getElementById("myChart");


var myBarChart = new Chart(ctx, {
    type: 'bar',
    data: data,
    options: options
});

这就像一个魅力,但只适用于一个图表 - 因为我们使用document.getElementById("myChart")定位上下文。

问题是 - 如何更改此设置以从我返回的数据创建图表?作为备份解决方案,我提前预先设计了页面(我知道返回的最大机器数量)并简单地隐藏那些不应该出现的页面....但我知道这不是正确的方法(它&#39 ;备用计划)。我想学习如何正确地做到这一点。

对此事的任何帮助都将不胜感激。我是一名AngularJS新手,因此您的代码示例将非常受欢迎!

修改

根据建议,我已将HTML代码更新为以下内容:

        <div class="col-md-12" ng-repeat="machine in machines">
            <h1> {{ machine.name }}</h1>
            <canvas id="{{'myChart_' + $index}}" width="400" height="400"></canvas>

        </div>

将这些图表命名为没有问题。然后,在我的控制器下,我将代码更改为以下内容:

    $scope.change = function () {
        MachinesService.getMachines($scope.date_from, $scope.date_to).then(function (response) {
            //$scope.machines = response.data;
            angular.copy(response.data, $scope.machines);
        }, function (error) {
            $scope.errorMessage = "Failed to load data:" + error;
        }).finally(function () {

            var data = {..same as above};
            var options = {...same as above};

            //now assign those values to the representative charts
            for (var i = 0; i < $scope.machines.length -1; i++) {
                var ctx = document.getElementById("myChart_" + i);


                var myBarChart = new Chart(ctx, {
                    type: 'bar',
                    data: data,
                    options: options
                });
            }



        });
    };

我遇到的问题是,在我的代码执行后,我的图表会呈现。这意味着我尝试在我的页面上由Angular实际创建之前找到我的图表。我已尝试(如您所见)将.finally子句添加到我的代码中,但它不起作用。

是否需要使用开关/代码才能使此解决方案正常工作?

EDIT2

我还尝试将以下参数$timeout添加到控制器中,如下所示:

.controller('mainController', ['$scope', 'MachinesService', '$timeout', mainController]);

然后我使finally子句成为这样的外部函数(在同一个控制器内):

    var changeValues = function () {
        var data = {...same as before};
        var options = {...same as before};

        for (var i = 0; i < $scope.machines.length - 1; i++) {
            var ctx = document.getElementById("myChart_" + i);


            var myBarChart = new Chart(ctx, {
                type: 'bar',
                data: data,
                options: options
            });
        }
    };

从finally子句中我调用了我的函数$timeout(changeValues, 0);,但它仍然不起作用。我现在很迷茫。我错过了什么?

FINAL

以下是我最终编辑代码的方法:

angular.module("app-machines", ['ngFlatDatepicker'])
    .factory('MachinesService', ['$http', MachinesService])
    .controller('mainController', ['$scope', 'MachinesService', '$timeout', mainController])
    .directive('onFinishRender', function ($timeout) 
    {
        return {
            restrict: 'A',
            link: function (scope, element, attr) {
                if (scope.$last === true) {
                    $timeout(function () {
                        scope.$emit('ngRepeatFinished');
                    });
                }
            }
        }
    });

1 个答案:

答案 0 :(得分:2)

可能有更好的答案,但是......

您可以使用角度循环来创建初始HTML元素。

<div class="col-md-12" ng-repeat="machine in machines">
  <h1> {{ machine.name }}</h1>
  <canvas id="{{'myChart_' + $index}}" width="400" height="400"></canvas>
</div>

然后在你的控制器中将元素传递给DOM并$broadcast一个事件来绘制图表。

$scope.change = function () {
        MachinesService.getMachines($scope.date_from, $scope.date_to).then(function (response) {
            angular.copy(response.data, $scope.machines);
            $scope.$broadcast('chartReady'); //broadcast the event
        }, function (error) {
            $scope.errorMessage = "Failed to load data:" + error;
    });
};

同样在您的控制器中处理广播事件。我从here.

借用并修改了此代码
directive('drawCharts', ['$timeout', function ($timeout) {
    return {
        link: function ($scope, element, attrs) {
            $scope.$on('chartReady', function () {
                $timeout(function () { // You might need this timeout to be sure its run after DOM render.
                  //get chart elements and draw chart here
                  for (var i = 0; i < $scope.machines.length -1; i++) {
                      var ctx = document.getElementById("myChart_" + i);

                      var myBarChart = new Chart(ctx, {
                        type: 'bar',
                        data: data,
                        options: options
                      });
                  }  
                }, 0, false);
            })
        }
    };
}]);