Using Cordova / PhoneGap

amCharts can be used for mobile apps using Cordova and PhoneGap. We will be covering some tips how to get charts in your app using Cordova, but a similar work could be done using PhoneGap.

Environment setup

We are assuming you have some knowledge using Cordova or PhoneGap. We are also assuming you may have a favorite setup to create Cordova apps, but we decided to go with a setup that could be easier for most users and we are only using an Android app simulation running on your default browser. Feel free to use your own setup and skip the instructions below.

Creating the app

Open a terminal and choose a directory where you want your app to be. Create the app running the command below:

cordova create AmchartsExampleApp

Go the directory you created:

cd AmchartsExampleApp

Add browser and android as platforms:

cordova platform add browser
cordova platform add android

Open AmchartsExampleApp using Visual Studio Code and then add the debug configurations following the steps below. This will add the ./.vscode/launch.json file containing instructions to launch the app.

Run the app

At this point you can run the app as shown below. This is how we'll be running the app from now on.

Simplify the default Cordova files

The code in this tutorial removes code comments and license notices for the sake of making it easily readable. You still need to comply with Cordova's license requirements.

Let's simplify the default content in the app to make things clear moving foward. Replace the content in ./www/js/index.js for the code below.

var app = {

    initialize: function() {
        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
    },

    onDeviceReady: function() {
        this.receivedEvent('deviceready');
    },

    receivedEvent: function(id) {
        console.log('Received Event: ' + id);
    }
};

app.initialize();

Replace the content in ./www/css/index.css for the code below:

body {
    -webkit-touch-callout: none;
    -webkit-text-size-adjust: none;
    -webkit-user-select: none;
    font-family: Helvetica, Arial, sans-serif;
    font-size: 14px;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    background-color: #EEE;
}

Replace the content in ./www/index.html for the code below:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
        <link rel="stylesheet" href="css/index.css">
        <title>amCharts Example</title>
    </head>
    <body>

        <script src="cordova.js"></script>
        <script src="js/index.js"></script>
    </body>
</html>

Now we have a clean code to start adding our examples.

Add amCharts files

  • Create a directory called amcharts4 under ./www/js/
  • Download amCharts 4 files
  • You can unzip everything, except the /examples directory, or just core.js, chart.jsand themes/kelly.js into ./www/js/amcharts4
  • Add amCharts reference into index.html
<script src="js/amcharts4/core.js"></script>
<script src="js/amcharts4/charts.js"></script>
<script src="js/amcharts4/themes/animated.js"></script>

Adding CSS for the charts

We will be adding 3 charts on this tutorial. It's recommended to have defined dimensions for performance reasons and for smooth scrolling while the page may be still loading.

Add the 3 classes to the ./www/css/index.css file:

#pie-chart {
    width: 100%;
    height: 500px;
    background-color: #FFF;
    border-radius: 0.5em;
}

#line-chart {
    width: 100%;
    height: 500px;
    background-color: #FFF;
    border-radius: 0.5em;
}

#column-chart {
    width: 100%;
    height: 500px;
    background-color: #FFF;
    border-radius: 0.5em;
}

Adding HTML containers for the charts

Now we need to add the HTML elements where the charts will be rendered to. Please add the following into the body tag in your ./www/index.html file just before the script tags.

<h3>amCharts 4 Examples</h3>

<p>
    Please see below same basic charts for this example. It's recommended
    to start all charts inside Cordova's "deviceready".
</p>

<h4>Pie Chart</h4>

<p>
    Have defined dimensions for all charts. It helps to avoid the page
    jumping while loading.
</p>

<div id="pie-chart"></div>

<h4>Line Chart</h4>

<p>
    This chart should only show when in the viewport. That should is
    recommended for performance reason.
</p>

<div id="line-chart"></div>

<h4>Column Chart</h4>

<p>
    This is another basic chart that shows only when on viewport.
</p>

<div id="column-chart"></div>

Adding the first chart

Wrap the chart code into Cordova's "deviceready" event. This will allow the page to load related files and draw static HTML before starting to render the charts. This step will help the page JavaScript to not block the browsers while getting ready.

Add a file called pie.js to ./www/js/charts/ and copy the JavaScript below to it's content.

// Try to start all chart in the deviceready event
document.addEventListener("deviceready", function () {

    // Create chart instance
    var chart = am4core.create("pie-chart", am4charts.PieChart);

    // Add data
    chart.data = [{
        "country": "Lithuania",
        "litres": 501.9
    }, {
        "country": "Czech Republic",
        "litres": 301.9
    }, {
        "country": "Ireland",
        "litres": 201.1
    }, {
        "country": "Germany",
        "litres": 165.8
    }, {
        "country": "Australia",
        "litres": 139.9
    }];

    // Add and configure Series
    var pieSeries = chart.series.push(new am4charts.PieSeries());
    pieSeries.dataFields.value = "litres";
    pieSeries.dataFields.category = "country";

    // Disable ticks and labels
    pieSeries.labels.template.disabled = true;
    pieSeries.ticks.template.disabled = true;

    // Disable tooltips
    pieSeries.slices.template.tooltipText = "";

    // Add a legend
    chart.legend = new am4charts.Legend();

});

Now add the following script tag to your index.html just before the ending body tag.

<script src="js/charts/pie.js"></script>

Now try to run the app.

Render the chart when on viewport

We will be adding 2 other charts on this section and include a script to check when the chart is on the viewport. Please add the following script into a new file in ./www/js/viewport.js.

/**
 * DON'T USE THIS CODE IN PRODUCTION.
 * 
 * This was meant just to be used in the demo with minimal code and testing.
 */
(function () {

    var items = [],
        timeout,
        handler = function (e) {

            clearTimeout(timeout);

            // Delay the checks
            timeout = setTimeout(function () {

                var html = document.documentElement,
                    clientHeight = html.clientHeight,
                    clientRect,
                    visibleHeight,
                    index = items.length,
                    item;

                while (index--) {

                    item = items[index];
                    clientRect = item.element.getBoundingClientRect();
                    visibleHeight = (clientRect.height - 20);

                    if (clientRect.top >= visibleHeight * -1 && clientRect.bottom <= clientHeight + visibleHeight) {

                        item.callback(e);
                        
                        // Remove the element from the list of items as the callback is already
                        // executed
                        items.splice(index, 1);
                    }

                }

                // Unbind the events if there nothing to watch for
                if (!items.length) {
                    
                    window.removeEventListener("resize", handler, false);
                    window.removeEventListener("scroll", handler, false);
                }

            }, 500);
        };

    window.viewPortAction = {

        /**
         * Add elements to be checked when available on the viewport. Also add
         * callback to be executed when the element is on teh viewport.
         * 
         * @method add
         * @param {Element} element The HTML element to be checked
         * @param {Function} callback The function to be executed when on viewport
         */
        add: function (element, callback) {

            // Only bind the events when there is something to check
            if (!items.length) {

                window.addEventListener("resize", handler, false);
                window.addEventListener("scroll", handler, false);
            }

            items.push({
                element: element,
                callback: callback
            });

            // Call the handler right away to check it the element is already in the
            // viewport
            handler();
        }
    };

})();

Now add a column chart with the script in a new file created on the path ./www/js/charts/column.js.

// Try to start all chart in the deviceready event
document.addEventListener("deviceready", function () {

    viewPortAction.add(document.getElementById("column-chart"), function () {

        am4core.useTheme(am4themes_animated);
        
        var chart = am4core.create("column-chart", am4charts.XYChart);
        chart.hiddenState.properties.opacity = 0; // this makes initial fade in effect
        
        chart.data = [{
            "country": "USA",
            "visits": 3025
        }, {
            "country": "China",
            "visits": 1882
        }, {
            "country": "Japan",
            "visits": 1809
        }, {
            "country": "Germany",
            "visits": 1322
        }];
        
        chart.padding(40, 40, 40, 40);
        
        var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
        categoryAxis.renderer.grid.template.location = 0;
        categoryAxis.dataFields.category = "country";
        categoryAxis.renderer.minGridDistance = 20;
        categoryAxis.renderer.labels.template.rotation = 270;
        categoryAxis.renderer.labels.template.hideOversized = false;
        categoryAxis.renderer.labels.template.horizontalCenter = "right";
        categoryAxis.renderer.labels.template.verticalCenter = "middle";
        
        var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
        
        var series = chart.series.push(new am4charts.ColumnSeries());
        series.dataFields.categoryX = "country";
        series.dataFields.valueY = "visits";
        series.tooltipText = "{valueY.value}"
        series.columns.template.strokeOpacity = 0;
        
        chart.cursor = new am4charts.XYCursor();
        
        // as by default columns of the same series are of the same color, we add adapter which takes colors from chart.colors color set
        series.columns.template.adapter.add("fill", function (fill, target) {
            return chart.colors.getIndex(target.dataItem.index);
        });
    });
});

Now we will be adding a line chart at ./www/js/charts/line.js. Please copy the script below.

document.addEventListener("deviceready", function () {

    viewPortAction.add(document.getElementById("line-chart"), function () {

        am4core.useTheme(am4themes_animated);
        
        var chart = am4core.create("line-chart", am4charts.XYChart);
        chart.hiddenState.properties.opacity = 0; // this makes initial fade in effect
        
        chart.paddingRight = 20;
        
        var data = [];
        var visits = 10;
        for (var i = 1; i < 30; i++) {
            visits += Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 10);
            data.push({ date: new Date(2018, 0, i), value: visits });
        }
        
        chart.data = data;
        
        var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
        dateAxis.renderer.grid.template.location = 0;
        dateAxis.renderer.axisFills.template.disabled = true;
        dateAxis.renderer.ticks.template.disabled = true;
        dateAxis.renderer.labels.template.rotation = 270;
        dateAxis.renderer.labels.template.hideOversized = false;
        dateAxis.renderer.labels.template.horizontalCenter = "right";
        dateAxis.renderer.labels.template.verticalCenter = "middle";
        
        var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
        valueAxis.tooltip.disabled = true;
        valueAxis.renderer.minWidth = 35;
        valueAxis.renderer.axisFills.template.disabled = true;
        valueAxis.renderer.ticks.template.disabled = true;
        
        var series = chart.series.push(new am4charts.LineSeries());
        series.dataFields.dateX = "date";
        series.dataFields.valueY = "value";
    
    });
});

Make a reference to these scripts in your HTML. This is how your HTML should look like when complete:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
        <link rel="stylesheet" href="css/index.css">
        <title>amCharts Example</title>
    </head>
    <body>

        <h3>amCharts 4 Examples</h3>

        <p>
            Please see below same basic charts for this example. It's recommended to start
            all charts inside Cordova's "deviceready".
        </p>

        <h4>Pie Chart</h4>

        <p>
            Have defined dimensions for all charts. It helps to avoid the page jumping
            while loading.
        </p>

        <div id="pie-chart"></div>

        <h4>Line Chart</h4>

        <p>
            This chart should only show when in the viewport. That should is recommended
            for performance reason.
        </p>

        <div id="line-chart"></div>

        <h4>Column Chart</h4>

        <p>
            This is another basic chart that shows only when on viewport.
        </p>

        <div id="column-chart"></div>

        <!-- Cordova files -->
        <script src="cordova.js"></script>
        <script src="js/index.js"></script>

        <!-- amCharts files -->
        <script src="js/amcharts4/core.js"></script>
        <script src="js/amcharts4/charts.js"></script>
        <script src="js/amcharts4/themes/animated.js"></script>

        <!-- Viewport file -->
        <script src="js/viewport.js"></script>

        <!-- Charts files -->
        <script src="js/charts/pie.js"></script>
        <script src="js/charts/line.js"></script>
        <script src="js/charts/column.js"></script>
    </body>
</html>

At last, run the app again and notice that the last two charts are rendered only when on the viewport. This could be an important step for better performance and overral experience.

Downlad the example

Download the example here and run npm i to load the dependencies.