How can AngularJS be used to implement lazy loading for Google jsapi charts?

I have a single page application with different tabs. I want to include a Google chart only on one specific tab without directly loading the charts js file, jsapi, unless that tab is accessed.

To achieve this, I need to dynamically add the JavaScript as shown below:

var script = document.createElement('script');
script.src = '//http://www.google.com/jsapi?ext.js';
document.body.appendChild(script);

The problem arises when trying to detect if the JavaScript has been fully loaded so that I can proceed accordingly:

if (whenJspaiLoaded()) {
    google.load('visualization', '1', {packages: ['corechart']});
    google.setOnLoadCallback(drawChart); //my custom function
}

Answer №1

Check out my coding solution below:

    <style>
    .preloader {
        height:350px; background:url(images/719.gif) left no-repeat;
    }
    </style>
    <div id="columnchart_material"><div class="preloader">&nbsp;</div></div>
<script type="text/javascript"
          src="https://www.google.com/jsapi?autoload={
            'modules':[{
              'name':'visualization',
              'version':'1',
              'packages':['corechart']
            }]
          }">
</script>
    <script>
google.setOnLoadCallback(drawChart);

      function drawChart() {
    var chart = new google.visualization.LineChart(document.getElementById('columnchart_material'));
                options.title='English'
                options.series[1].lineWidth = 0;
            chart.draw(data,options );
}
    </script>

You can view and interact with the code on Codepen by clicking here

Answer №2

If you're looking to simplify the management of charts in Angular, consider giving Angular Google Chart a shot. This library abstracts the chart handling process with a directive, allowing you to simply provide an object containing the data, options, and type of chart you want to create. The script tag is only added when necessary, either upon first loading the directive or when requesting the promise that confirms the library has loaded.

To ensure that the library isn't loaded until needed, consider using ng-if instead of ng-show or placing it in a separate routed view to prevent background loading of the directive element.

Answer №3

If you're struggling to find a solution online, I've come up with a method that could be beneficial for someone in the future. The main idea here is to postpone the loading of both the js file and the initialization of the charts component using a callback.

This is how the service loads the js files:

app.service('gchart', ['$window', '$q', '$rootScope', function ($window, $q, $rootScope) {
      var deferred = $q.defer();

      //execute when the jsapi is loaded
      $window.callback = function () {
            $window.google.load('visualization','1',{
                packages: ['corechart'],
                callback: function() {
                    //using a callback again to ensure the visualization is fully loaded
                    $rootScope.$apply(function() {
                        deferred.resolve();
                    });
                }
            });
      }

      // dynamically adding the js file on page load
      var script = document.createElement('script');
      script.src = '//www.google.com/jsapi?callback=callback';
      document.body.appendChild(script);

      return deferred.promise;
}]);

Here's how you can use it as a directive:

app.directive(.., ['gchart', function(.., gchart) {
   return {
      //...
      link: function(scope, elm, attr) {
          scope.init = function() {
              var viz = new google.visualization.arrayToDataTable(data);
              //add options, linechart, draw, whatever
          };

          // delay the creation until jsapi files are loaded
          loadGoogleChartAPI.then(function () {
              scope.init();
          }, function () {
              //ignore
          });           
       }
   }
}

Answer №4

Using async function with callback:

function loadScript(url, callback) {
  var document = document,
      tag = 'script',
      script = document.createElement(tag),
      firstScriptTag = document.getElementsByTagName(tag)[0];
  script.src = '//' + url;
  if (callback) { 
    script.addEventListener('load', function (event) { callback(null, event); }, false); 
  }
  firstScriptTag.parentNode.insertBefore(script, firstScriptTag);
}

Example of Usage:

loadScript('www.google.com/jsapi?ext.js', function() {
    google.load('visualization', '1', {packages: ['corechart']});
    google.setOnLoadCallback(drawChart);
});

Source: Reference

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

Understanding the unpredictability of A-Sync in Node.js/Express: Why does it appear to operate in a non-linear fashion?

Recently, I delved into the world of Node.js/Express and encountered asynchronous calls that must complete before moving on. My code features three different endpoints that need to be hit. Considering the asynchronous nature, I attempted to structure my c ...

Testing Jest with NodeJS involves simulating input from standard input (stdin)

I'm currently working on a command-line application that takes user input from standard input using the following code: const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, }); rl.on('li ...

Is it possible to protect assets aside from JavaScript in Nodewebkit?

After coming across a post about securing the source code in a node-webkit desktop application on Stack Overflow, I began contemplating ways to safeguard my font files. Would using a snapshot approach, similar to the one mentioned in the post, be a viable ...

Refreshing the page using location.reload triggers a reload of various elements

I am currently working on a website that supports multiple languages and utilizes a cookie named nav_lang to determine the user's preferred language for navigation. Whenever a user selects a new language, the cookie is updated accordingly and the page ...

Leveraging promises in HTTP requests within Angular has proven to be a

When using $http or $resource to fetch data, everything works fine and the data is successfully bound to the view. However, when using promises, the data is received by the client but does not bind to the view. Here is my code: <div class="pull-left sp ...

MongoDB date query with $gte and $le operators mm/dd/yy

Apologies in advance for any language errors. The problem I am dealing with involves a column in the database called "Date" inside the "startOn" Object. This setup creates a structure like "startOn.Date" with data format as yyyy/dd/mm. For example, if we ...

Utilizing Node.js and Express alongside EJS, iterating through objects and displaying them in a table

Today I embarked on my journey to learn Node.js and I am currently attempting to iterate through an object and display it in a table format. Within my router file: var obj = JSON.parse(`[{ "Name": "ArrowTower", "Class" ...

Using Vue.js to pass a variable from a parent component to a child component

Parent component: ShowComment Child component: EditComment I'm attempting to pass the value stored in this.CommentRecID to the child component. In the template of ShowComment, I have included: <EditComment CommentRecID="this.CommentRecID" v-if= ...

Utilize AJAX to fetch and display the response from a Node JS route directly onto an HTML page

At the moment, I am retrieving client-side HTML/JS inputs (var1 and var2) which are then sent to server-side Node JS. The input values are stored in variables through an AJAX post call made to a specific route. What I want now is to define the values of v ...

Executing several GET requests in JavaScript

Is there a more efficient way to make multiple get requests to 4 different PHP files within my project and wait for all of them to return successfully before appending the results to the table? I have tried nesting the requests, but I'm looking for a ...

Is there a way to access and invoke a exposed function of a Vue component within a default slot?

Exploring the realms of a vue playground. The functions interfaceFunction in both ChildA and ChildB are exposed. In App, these functions can be called by obtaining references to the components that expose them. This allows direct function calls from with ...

What is the best way to reference a const from a different class in React?

I'm relatively new to ReactJS, specifically using NextJS. In my project, I have two files - index.js and welcome.js In index.js, I've added Welcome as a component, along with a const called hideWelcome to hide the component and perform some acti ...

How can I place a div using pixel positioning while still allowing it to occupy space as if it were absolutely positioned?

I successfully created an slds path element with multiple steps. I want to display additional subtext information below each current step, but there might not always be subtext for every step. To achieve this, I created an absolute positioned div containi ...

Exploring the Relationship Between jQuery and JSON through Iterating Over JSON Arrays

There is an array stored in a database: a:4:{i:1;s:4:"1993";i:2;s:4:"1994";i:3;s:4:"1995";i:4;s:4:"1996";} To manipulate this array, I first unserialize it using PHP and then encode it with JSON. The code snippet is as follows: $unwp = unserialize(&apos ...

What is the most efficient method for defining a string structure in TypeScript?

Consider the following example of a JavaScript object: const myObject = { id: 'my_id', // Base value which sets the structure for the following values (always pascal case). upperID: 'MY_ID', // Uppercase version of `id`. camel ...

Optimizing performance: Enhance readback operations in Canvas2D by enabling the willReadFrequently attribute for faster getImageData processing

I am currently utilizing the react-wordcloud package and encountering an issue where the word cloud adjusts to fit 70% of the screen size whenever the container size changes. My console is being flooded with this warning message: https://i.sstatic.net/vdV ...

Angular model remains static when incorporated within the document.addEventListener() attribute

Can anyone help me figure out why my attempt to update the model name this.name on click inside document.getElementById('source-selection').addEventListener is not working? Surprisingly, a simple alert within that function does trigger successful ...

Switching the color scheme between mobile and desktop devices to make the background look

As a newcomer to threejs, I have just begun learning and developing with this technology. I am aware that certain features may be different on mobile browsers compared to desktop ones due to limitations. However, I have encountered an issue that I cannot s ...

Issue with form array patching causing value not to be set on material multiple select

When attempting to populate a mat-select with multiple options using an array of ids from Firestore, I encountered an issue. The approach involved looping through the array, creating a new form control for each id, and then adding it to the formArray using ...

Deploy Node.js on a Debian server hosted on Google Compute Engine

Currently, I am operating a Debian server on Google Compute Engine using a host called example.com. My goal is to run a node.js app within a specific directory on this server, for instance, example.com/mynodeapp. Fortunately, the necessary components such ...