This is a quick tutorial on how to create charts using PrimeFaces in Liferay portlets. PrimeFaces does provide charting facilities out of the box. To be honest, those provided charts do not look appealing to end users, therefore requiring some UI tweaking. Alas, not all is lost, PrimeFaces does provide extension modules which includes the ability to create some charts by using Google Charts.

This short tutorial will be using the following charting API for PrimeFaces

Create Liferay JSF PrimeFaces portlet

  1. The simpler way to create a Lifeay Project is through Maven using the Liferay artefacts.
    mvn archetype:generate -Dfilter=liferay

Add the Charts Dependencies

  1. Maven would have created a pom.xml file for your project. Open the file and add the following dependencies
    
    <dependency>
                <groupId>org.jsf</groupId>
                <artifactId>ChartistJSF</artifactId>
                <version>3.0</version>		
            </dependency>
            <dependency>
                <groupId>org.primefaces.extensions</groupId>
                <artifactId>primefaces-extensions</artifactId>
                <version>6.1.1</version>
    </dependency>
    

    Make sure that your PrimeFaces version is 6.1.

Create the JSF Beans

  1. Our JSF backing beans will generate the data for charts to display. Here is the full source code of our backing bean
    
    package com.kiktronik.tut.bean;
    
    import java.util.Random;
    import javax.annotation.PostConstruct;
    import javax.faces.application.FacesMessage;
    import javax.faces.bean.ManagedBean;
    import javax.faces.context.FacesContext;
    import org.chartistjsf.model.chart.AspectRatio;
    import org.chartistjsf.model.chart.Axis;
    import org.chartistjsf.model.chart.AxisType;
    import org.chartistjsf.model.chart.ChartSeries;
    import org.chartistjsf.model.chart.LineChartModel;
    import org.chartistjsf.model.chart.LineChartSeries;
    import org.primefaces.event.ItemSelectEvent;
    import org.primefaces.extensions.component.gchart.model.GChartModel;
    import org.primefaces.extensions.component.gchart.model.GChartModelBuilder;
    import org.primefaces.extensions.component.gchart.model.GChartType;
    
    @ManagedBean
    public class ChartDataBean {
    
        private LineChartModel lineModel;
          private GChartModel chartModel = null;
    
        public ChartDataBean() {
            createLineModel();
        }
    
        public void createLineModel() {
            Random random = new Random();
            lineModel = new LineChartModel();
            lineModel.setAspectRatio(AspectRatio.GOLDEN_SECTION);
    
            lineModel.addLabel("1");
            lineModel.addLabel("2");
            lineModel.addLabel("3");
            lineModel.addLabel("4");
            lineModel.addLabel("5");
            lineModel.addLabel("6");
            lineModel.addLabel("7");
            lineModel.addLabel("8");
    
            LineChartSeries series1 = new LineChartSeries();
            series1.setName("Series 1");
    
            series1.set(random.nextInt(10));
            series1.set(random.nextInt(10));
            series1.set(random.nextInt(10));
            series1.set(random.nextInt(10));
            series1.set(random.nextInt(10));
            series1.set(random.nextInt(10));
            series1.set(random.nextInt(10));
            series1.set(random.nextInt(10));
    
            LineChartSeries series2 = new LineChartSeries();
            series2.setName("Series 2");
    
            series2.set(random.nextInt(10));
            series2.set(random.nextInt(10));
            series2.set(random.nextInt(10));
            series2.set(random.nextInt(10));
            series2.set(random.nextInt(10));
            series2.set(random.nextInt(10));
            series2.set(random.nextInt(10));
            series2.set(random.nextInt(10));
    
            LineChartSeries series3 = new LineChartSeries();
            series3.setName("Series 3");
    
            series3.set(random.nextInt(10));
            series3.set(random.nextInt(10));
            series3.set(random.nextInt(10));
            series3.set(random.nextInt(10));
            series3.set(random.nextInt(10));
            series3.set(random.nextInt(10));
            series3.set(random.nextInt(10));
            series3.set(random.nextInt(10));
    
            Axis xAxis = lineModel.getAxis(AxisType.X);
            lineModel.addSeries(series1);
            lineModel.addSeries(series2);
            lineModel.addSeries(series3);
    
            lineModel.setAnimateAdvanced(true);
            lineModel.setShowTooltip(true);
        }
    
        public void itemSelect(ItemSelectEvent event) {
            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Item selected", "Item Value: "
                    + ((ChartSeries) lineModel.getSeries().get(event.getSeriesIndex())).getData().get(event.getItemIndex())
                    + ", Series name:" + ((ChartSeries) lineModel.getSeries().get(event.getSeriesIndex())).getName());
    
            FacesContext.getCurrentInstance().addMessage(event.getComponent().getClientId(), msg);
        }
    
        /**
         * @return the lineModel
         */
        public LineChartModel getLineModel() {
            return lineModel;
        }
    
        /**
         * @param lineModel the lineModel to set
         */
        public void setLineModel(LineChartModel lineModel) {
            this.lineModel = lineModel;
        }
    
        @PostConstruct
        public void generateModel() {
            chartModel = new GChartModelBuilder().setChartType(GChartType.COLUMN)
                    .addColumns("Year", "Salves", "Expenses")
                    .addRow("2010", 1000, 400)
                    .addRow("2011", 1200, 800)
                    .addRow("2012", 2000, 1800)
                    .addRow("2013", 1500, 1800)
                    .addRow("2014", 1300, 1000)
                    .build();
        }
    
        public GChartModel getChart() {
            return chartModel;
        }
    }
    

Display the Charts

  1. We update the view.xml to call our backing bean and display the charts
    
    <?xml version="1.0"?>
    
    <f:view
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://xmlns.jcp.org/jsf/core"
        xmlns:h="http://xmlns.jcp.org/jsf/html"
        xmlns:p="http://primefaces.org/ui"
        xmlns:pe="http://primefaces.org/ui/extensions"
        xmlns:ct="http://www.chartistjsf.org/charts"
        >
        <h:head>
            <h:outputStylesheet library="css" name="main.css" />
    
        </h:head>
        <h:body>
          
            <div class="panel panel-default">
                <div class="panel-body">
                    <pe:gChart value="#{chartDataBean.chart}" /> 
                </div>
            </div>
            <div class="panel panel-default">
                <div class="panel-body">
                    <ct:chart id="lineChart" type="line" model="#{chartDataBean.lineModel}">
                        <p:ajax event="itemSelect" listener="#{chartDataBean.itemSelect}" update="lineChartMessage" />
                    </ct:chart>
    
                    <p:message id="lineChartMessage" for="lineChart" showDetail="true" />
                </div>
            </div>
        </h:body>
    </f:view>
    

Update JavaScript References

  1. Developers usually struggle to understand why their charts are not being displayed. The web browser will log some errors to the console about undefined object and null pointer exception. The main culprit here is that Liferay is not loading the JavaScript files. To ensure that your chart is display in its full glory, make sure to update Liferay’s liferay-portlet.xml. This is what our liferay-portlet.xml for this tutorial looks like.
    
    <?xml version="1.0"?>
    <!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 7.0.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_7_0_0.dtd">
    
    <liferay-portlet-app>
    	<portlet>
    		<portlet-name>LiferayTuts</portlet-name>
    		<icon>/resources/images/icon.png</icon>
    		<requires-namespaced-parameters>false</requires-namespaced-parameters>
    		<ajaxable>false</ajaxable>
                    <header-portal-javascript>https://www.gstatic.com/charts/loader.js</header-portal-javascript>
                    <header-portal-javascript>https://gionkunz.github.io/chartist-js/scripts/all.js</header-portal-javascript>
    	</portlet>
    	<role-mapper>
    		<role-name>administrator</role-name>
    		<role-link>Administrator</role-link>
    	</role-mapper>
    	<role-mapper>
    		<role-name>guest</role-name>
    		<role-link>Guest</role-link>
    	</role-mapper>
    	<role-mapper>
    		<role-name>power-user</role-name>
    		<role-link>Power User</role-link>
    	</role-mapper>
    	<role-mapper>
    		<role-name>user</role-name>
    		<role-link>User</role-link>
    	</role-mapper>
    </liferay-portlet-app>
    
    

Deploy the Portlet

  1. All that’s left to do now is to build the project and copy your .war file to your Liferay deploy folder. Create a new page and add your portlet to it, refresh the page and voila! All done.

The following chart will be displayed in your browser.

hope this saves you time on your development.