The Highchart Angular directive is failing to update when receiving new data from a dynamic (ajax) data table

My AngularJs application fetches data from an API and populates a table with it. I then use Highcharts to create a chart based on the table data. Everything works fine when I use static data in the table, but it doesn't update properly when I try to change the table data using AJAX. The chart fails to redraw.

Here is the directive:

app.directive('highChart', function ($parse) {
    return {        
        link: function(scope, element, attrs) {
            scope.$watch('chartConfig', function (newVal) {
                if (newVal) {
                    var props = $parse(attrs.highChart)(scope);
                    props.chart.renderTo = element[0];
                    new Highcharts.Chart(props);
                }
            });
        }
    };
});

And the controller:

appControllers.controller('SummaryController', ['$scope', '$timeout', 'Summaries',
    function ($scope, $timeout, Summaries) {
        // Fetch data from the API
        Summaries.get({ table: 'level'}).$promise.then(
            // success callback
            function(data) {
                $scope.levels = data.level[0][today];
                $scope.chartConfig = {
                    data: {
                        table: document.getElementById('daily-level')
                    },
                    chart: {
                        type: 'column'
                    },
                    title: {
                        text: 'Data extracted from a HTML table in the page'
                    },
                    yAxis: {
                        allowDecimals: false,
                        title: {
                            text: 'Units'
                        }
                    },
                    tooltip: {
                        formatter: function () {
                            return '<b>' + this.series.name + '</b><br/>' +
                                this.y + ' ' + this.x;
                        }
                    }
                 };
            }
    }]);

Corresponding HTML code:

<div id="top-chart" high-chart="chartConfig"></div>

Despite updating the table through AJAX, the chart remains empty. I have verified that the updated table data is being passed correctly to the directive. I have tried various solutions, but the chart still does not redraw. Currently, I am using a jQuery timeout to refresh the chart, which is not ideal as it involves DOM manipulation in the controller. I am looking for a way to accomplish this using the directive.

Answer №1

I successfully got it to function. It seems that Highcharts may not be aware of changes or, if it is, the DOM may not have finished rendering at that moment. Below is the revised code that works:

Directive:

app.directive('highchart', function($parse, $timeout) {
    return {
        restrict: 'E',
        template: '<div></div>',
        replace: true,
        link: function(scope, element, attrs) {
            var config = $parse(attrs.config)(scope);
            $(element[0]).highcharts(config);

            scope.$watch(attrs.watch, function(newVal) {
                if (newVal) {
                    var complete = function (options) {
                        var chart = $(element[0]).highcharts();
                        // capture all available series
                        var allSeries = chart.series;
                        for (var i = 0; i < allSeries.length; i++) {
                            allSeries[i].setData(options.series[i].data, false);
                        }

                        chart.redraw();
                    };

                    // need to use timeout for it to work 
                    $timeout(function() {
                        Highcharts.data({
                            table: config.data.table,
                            complete: complete
                        });   
                    }, 0);
                }
            });
        }
    };
});

In the controller, we set up the configuration like this:

    $scope.chartConfig = {
         // configuration details here
    };

To implement it:

<highchart config="chartConfig" watch="levels"></highchart>

Here, the config attribute is linked to $scope.chartConfig, and watch triggers watch.$scope() in the directive. When $scope.levels changes in the controller, it will update the chart.

I am not particularly fond of the directive's dependency on the watch attribute as it stands, so I am open to any better suggestions. Feel free to share if you have a more effective approach.

Keep in mind that this method is specifically for converting a table into a Highchart. For other scenarios, you may need to explore different AngularJS Highcharts directives.

Answer №2

In the example below, the graph can be updated based on new data in the database using the watch feature. It continuously monitors for changes and adjusts accordingly:

app.directive('line',['$compile', function ($compile) {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        scope: {
            data: '=',
        },
        link: function (scope, element) {
            scope.lineGraphInstance = Highcharts.chart(element[0],{
                chart: {
                    type: 'spline',
                    style: {
                        fontSize: '8px'
                    },
                    spacingBottom: scope.data.spacingBottom,
                    spacingTop: scope.data.spacingTop,
                    spacingLeft: scope.data.spacingLeft,
                    spacingRight: scope.data.spacingRight,
                },
                title: {
                    text: '',
                },                      
                xAxis: {
                    type: 'datetime',
                    title: {
                        text: scope.data.xtitle,
                        enabled: false
                    },
                    labels: {
                        style: {
                            fontSize: '8px'
                        },                  
                        autoRotation: 0,
                        formatter: function () {
                            var x=this.value;
                            if (scope.data.hasOwnProperty("dateformat"))
                                x= Highcharts.dateFormat(scope.data.dateformat, this.value);
                            if (scope.data.hasOwnProperty("xSuffix"))
                                x=x+scope.data.xSuffix;         
                            if(scope.data.hasOwnProperty("xPrefix"))
                                x=scope.data.xPrefix+x;

                            return x;
                        },
                    enabled:scope.data.xtitleEnable
                    },
                    tickWidth: 3,
                    tickLength:scope.data.tickLength,
                    tickInterval: 3600 * 100
                        },
                yAxis: {
                    opposite: true,
                    title: {
                        text: scope.data.ytitle,
                        enabled: false
                    },
                    min: 0,
                    labels: {
                        style: {
                            fontSize: '10px'
                        },
                        formatter: function () {
                            var y=this.value;
                            if (scope.data.hasOwnProperty("ySuffix"))
                                y=y+scope.data.ySuffix;         
                            if(scope.data.hasOwnProperty("yPrefix"))
                                y=scope.data.yPrefix+y;

                            return y;
                        },
                    },
                    plotLines: [{
                        value: scope.data.ucl,
                        color: '#F39C12',
                        dashStyle: 'shortdash',
                        width: 1,
                    }, {
                        value: scope.data.lcl,
                        color: '#F39C12',
                        dashStyle: 'shortdash',
                        width: 1,
                    }]
                },
                tooltip: {
                    headerFormat: '<b>{series.name}</b><br>',
                    pointFormat: '{point.x:%e. %b}: {point.y:.2f} '
                },
                plotOptions: {
                    spline: {
                        marker: {
                            enabled: true
                        }
                    }
                },
                legend:{
                    enabled:false
                },
                series:[{data:scope.data.data,
                marker: {
                    enabled: true,
                    radius: 2
                },
                lineWidth: 1
                }],
                credits: {
                    enabled: false
                },  
                exporting: { enabled: false 
                }
            }); 
            ***scope.$watch('data', function (newValue, oldValue) {
                if(newValue && newValue.data){
                    var graphData=newValue.data[0].data.slice(-5);
                    scope.lineGraphInstance.series[0].setData(graphData, true);
                }
            }, true);
        }***
    };
}]); 

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for creating a JSON array for an AJAX post

const myObject = { subjects: [{ area: "Math"}, {area: "English"}] } $.ajax({ type: 'POST', data: {"mydata": JSON.stringify(myObject)}, url: '/tosubjects', dataType: "json", contentType: "application/json", processData: fals ...

Using JavaScript to obtain the coordinates of a click event from a programmatically triggered click on an HTML element

Is there a way to programmatically simulate a click event on an HTML DOM element and still retrieve the screenX/screenY and clientX/clientY coordinates successfully? When manually clicking the element, the coordinates are visible in the console, but when t ...

Employing jQuery AJAX XML method to dynamically refresh input fields within TD cells

My web application generates a dynamic table server-side with cells containing various elements like HTML markup, input fields, and plain text. There's also a slider widget that lets users adjust percentages in the table cells. This slider triggers an ...

Incorporating Files from Varied Folders

So I have these files: require_once("includes/database.php"); This poses a problem when the file is included from shopping_cart.php because the path makes sense in that context. But if the table.php file is called directly, the current directory will be ...

The HTML element update event was not triggered due to the excessive load of a JavaScript function

I am currently running a JavaScript function that is quite CPU intensive. To provide visual feedback to users when it starts and stops, I am attempting to display a badge on the webpage. Below is the code snippet: function updateView() { console.log(1 ...

Step-by-step guide on integrating Bulma page loader extension as a Vue component

Is there a way to integrate the Bulma-extension page-loader element as a Vue component in my project? I attempted to import page-loader.min.js and use it, but unfortunately, it didn't work as expected. <template> <div class="steps"> ...

Displaying an additional section using hover effects in Bootstrap

I recently utilized Bootstrap to create pricing tables which can be viewed here: http://www.bootply.com/VyHsJBDoNc Is there a way for me to implement hover functionality on a span element (+ More Information!) that will display additional information as s ...

Utilize the csv-parser module to exclusively extract the headers from a file

Recently, I've been exploring the NPM package csv-parser which is designed to parse CSV files into JSON format. The example provided demonstrates how you can read a CSV file row by row using the following code snippet: fs.createReadStream('data.c ...

Fill an object with data and send it using jQuery's ajax function

I'm facing an issue in my JSP page with multiple input tags having the same ID 'opt'. My goal is to extract the values of all these input tags and generate a list as shown below: Votes= { vote={opt:'one'}, vote={opt:'two&apos ...

Spinning text within a circular rotation in CSS

Looking for guidance on how to center the "hallo" text inside a circle? Currently experiencing issues with the circle display in IE8 and Firefox. Any suggestions on how to fix this would be greatly appreciated. And here is the link to my code snippet: CSS ...

Is it possible to retrieve JSON data from an external URL using JavaScript or jQuery?

My goal is to retrieve JSON data from the following URL: I have a button that triggers the function called "jsonplz()", which should display an alert message with the fetched JSON data. This is what my JavaScript code looks like: <script src="htt ...

Tap, swipe the inner contents of a <div> element without being inside it

(Make sure to check out the image below) I'm facing an issue with a <div> on my website. It's not taking up the full width of the page, and the scrolling functionality within the <body> is not working as expected. I just want the cont ...

Next.js is throwing an error: "Module cannot be found: Unable to resolve 'canvg'"

I am facing an issue in my next.js project where I keep encountering the following error message: error - ./node_modules/jspdf/dist/jspdf.es.min.js:458:25 Module not found: Can't resolve 'canvg' I'm confused because I have not included ...

I possess a dataset and desire to correlate each element to different elements

[ { "clauseId": 1, "clauseName": "cover", "texts": [ { "textId": 1, "text": "hello" } ] }, { "clauseId": 3, "clauseName": "xyz", "te ...

Updating the text of a Mat-Label dynamically without the need to reload the page

In my application, there is a mat-label that shows the Customer End Date. The end date is fetched initially through a GET request to an API. Let's say the end date is 16-05-2099, which is displayed as it is. There is also a delete button implemented f ...

What is the process for generating a new array of objects by leveraging the contents of two given arrays?

In my data collection, I have multiple objects stored in arrays like so: tableCols = [ { "id": 50883, "header": "ABC", "operator": "Sum", "order": 2 }, ...

AngularJS - Organize Item Hierarchy with Separate Containers for Each Group

My goal is to incorporate a $scope variable within an AngularJS controller that has the following structure: $scope.hierarchy = { name: 'bob', selected: true, children: [ { name: 'frank' }, { name: 'spike' ...

Exploring the process of extracting a nested JSON value using Postman

I am currently facing an issue with parsing the json response from a post request, and then sending the parsed data to a put request. Here is the response body: { "createdBy": "student", "createdOn": "2019-06-18", "Id1": "0e8b9 ...

Troubleshooting a React JS and material-ui issue

Having an issue with material-ui integration in reactjs. Attempting to render a FlatButton, but encountering the error message: TypeError: _context$muiTheme is undefined As a newcomer to reactjs, I'm unsure of where the problem lies. Below is my code ...

Using regular expressions to eliminate text located outside of the tags within a string

Presented is a string consisting of an XML string below: var xmlString = "<str>rvrv</str>rvrv<q1>vrvv</q1>vrvrv<q2>rtvrvr</q2>"; I am seeking assistance on how to eliminate text that lies outside the tags (text no ...