« Back

Tutorial of using Chart.js in Soy Portlet

Technical Blogs January 12, 2017 By Neil Jin

The purpose of this tutorial is to let you know how to involve a 3rd part js lib in metal soy portlet and how to use them in ES6 script. 
 
In Liferay DXP we provide abundant OOTB js lib to help the development. Jquery, AlloyUI, metal, lexicon component and etc.
 
They are all good, however, in real world project development nothing can satisfy the requirement once for all. In this changing world, the requirement is unpredictable. 
For this we can extend our ootb library by utilizing nodeJS to get 3rd part js lib works in Liferay.
 
In this journey, I will lead you to take a glimpse of the power of Liferay's integrability.
 
Knowledge:
    Google Closure Soy template.
    Liferay Soy Portlet.
    ECMA script 2015(ES6)
    Chart.js
 
In Tutorial of Creating Soy Portlet article, we have learned how to create a metal soy portlet by Liferay IDE. 
 
Based on that, we are going to do a further development -- to create a Chart. (In my professional life, many business requirement needs some sense of chart, this kind of requirement is very common.)
To build a chart js lib from scratch is not realistic. By utilizing Chart.js or other product on the market would help us to reduce develop effort and save project cost.
 
So let's start working on a portlet that display a chart.
 
Step 1, Create a soy portlet.
 
Create a Soy portlet as it shows in Tutorial of Creating Soy Portlet article with this file structure:
 
 
Step 2, ChartJS dependencies
 
To use ChartJS, we need to add NodeJS dependencies in package.json. Optionally you could manually copy a Chart.js bundle to your project path.
 
"chart.js": "^2.4.0"
 
Step 3, include it to your runtime environment.
 
Because Chart.js is a 3rd part js lib, it doesn't exist in the system(suppose it's not been used before). We have to involve it in our runtime -- in the jar.
So let's config our bnd.bnd to include the js to the right place. Add/Modify the following code to your bnd.bnd
 
Include-Resource: package.json,\
    META-INF/resources/js/Chart.js = node_modules/chart.js/dist/Chart.js
This means, I want a Chart.js in META-INF/resources/js of the jar, and the source is from node_modules/chart.js/dist/Chart.js
And add web context path in bnd.bnd
Web-ContextPath: /chartjs-soy
 
 
Step 4, Work with soy template
 
We are going to add a canvas for chart to use. So let's add the following code to ChartjsSoy.soy
{namespace ChartjsSoy}
/**
 * Display Chart canvas
 */
{template .render}
    <canvas id="chartjs-soy">
    </canvas>
{/template}
 
 
Step 5, import your Chart.js in your Component
 
I believe all audience of this blog know how to use ChartJS in a traditional way, in html and javascript like this:
<script src="Chart.js"></script>
<script>
    var myChart = new Chart({...})
</script>
But this is not going to be our approach. We are going to take advantage ES6 to use Chart, to extend our knowledge :)
So add the import in ChartjsSor.es.js in the head
 
import Chart from 'chartjs-soy/js/Chart';
 
You can see chartjs-soy is the web context path we define in bnd.bnd, and js/Chart is our web resource folder.
 
 
Step 6, create a chart with Chart.js
 
Add the following method to your Component
    /**
     * Create Chart
     *
     * @protected
     */
    createChart_() {
        let chartcanvas = document.getElementById("chartjs-soy");

        let data = {
            labels: ["January", "February", "March", "April", "May", "June", "July"],
            datasets: [
                {
                    label: "My First dataset",
                    backgroundColor: [
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(54, 162, 235, 0.2)',
                        'rgba(255, 206, 86, 0.2)',
                        'rgba(75, 192, 192, 0.2)',
                        'rgba(153, 102, 255, 0.2)',
                        'rgba(255, 159, 64, 0.2)'
                    ],
                    borderColor: [
                        'rgba(255,99,132,1)',
                        'rgba(54, 162, 235, 1)',
                        'rgba(255, 206, 86, 1)',
                        'rgba(75, 192, 192, 1)',
                        'rgba(153, 102, 255, 1)',
                        'rgba(255, 159, 64, 1)'
                    ],
                    borderWidth: 1,
                    data: [65, 59, 80, 81, 56, 55, 40],
                }
            ]
        };

        let options = {
            scales: {
                xAxes: [{
                    stacked: true
                }],
                yAxes: [{
                    stacked: true
                }]
            }
        };

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

    }
 
Step 7, constructor method to call
 
Let's add a constructor method in our component to call this createChart method
 
    constructor(opt_config) {
        super(opt_config);

        this.createChart_();

    }
 
 
Step 8, compile, deploy, test, debug and play around
 
If you have done it correctly, you would have a chart sheet like mine.
 
 
You can see how easy we can involve a 3rd part js lib in our project to improve our UX. Don't be limited with only 1 js lib, play around and test! The only limitation is your imagination. 
 
Hope you enjoy it.
Attachment is a compiled jar for you to deploy and test.
For full workable source code please visit sample.liferayee.com
 
Special Thanks to our Principal UI Engineer Chema Balsas, with his help I can get this tutorial done. Thanks Chema for many precious recommendations and help.
Threaded Replies Author Date
[...] Previously we have talked about how to... Anonymous May 17, 2017 5:36 PM
I pulled the source and am trying to get this... Abre Chase July 6, 2017 1:46 PM
Hi Abre, right, it doesn't work on GA3 . It... Neil Jin July 6, 2017 6:50 PM
I upgraded to CE GA4 and I still see the same... Abre Chase July 7, 2017 10:23 AM
I was able to get my code working by setting... Abre Chase July 7, 2017 11:58 AM

[...] Previously we have talked about how to create a soy portlet, how to use a 3rd party js lib(ChartJS) in SOY portlet, and how to use Liferay Service Builder to create a remote service(web service) in... [...] Read More
Posted on 5/17/17 5:36 PM.
I pulled the source and am trying to get this working in Liferay 7 CE GA3, but I get the following JS error.

Mismatched anonymous define() module: everything.jsp?browserId=other&themeId=classic_WAR_classictheme&colorSchemeId=01­&minifierType=js&mi…:294

I had actually tried building a similar module but without using soy prior to pulling your code and testing it. I was seeing the same problem in my code, but it isn't much different than yours so that's not unexpected.

Any ideas about what I might be missing here. Maybe the tutorial is missing some build / Gradle steps?
Posted on 7/6/17 1:46 PM.
Hi Abre, right, it doesn't work on GA3 . It works on CE GA4.
Posted on 7/6/17 6:50 PM in reply to Abre Chase.
I upgraded to CE GA4 and I still see the same problem.

In my es.js file, if I remove the import then I don't see any errors.

import Chart from 'chartjs-demo/js/Chart';

BND file is placing the file in jar.

Web-ContextPath: /chartjs-demo

Include-Resource: package.json,\
META-INF/resources/js/Chart.js = node_modules/chart.js/dist/Chart.js

That's from the code that I wrote based on your example. However, I see the same problem using your code.

Are there any configuration steps I might be missing?
Posted on 7/7/17 10:23 AM in reply to Neil Jin.
I was able to get my code working by setting the following property in portal-ext.properties

javascript.fast.load=false

Why is this necessary?
Posted on 7/7/17 11:58 AM in reply to Abre Chase.