SPA Using AngularJS In Liferay Portlet

blog-banner

AngularJS is a very powerful JavaScript library. It is used to build Single Page Application (SPA) projects. It uses client-side data binding to build dynamic views of data that change immediately in response to user actions.

But the question is:

Can we take advantage of this framework in Liferay portlet development?

Answer is : Yes, we can surely use AngularJS in Liferay portlets with few changes in general web applications.

First of all, let’s see basic structure of AngularJS in general web application :

<div ng-app="sampleapp">

<div ng-view></div>

</div>

When we create AngularJS application, we need to define ng-app attribute as shown below :

When page loads, AngularJS looks for ng-app attribute. If AngularJS is able to find ng-app it will initialize app. Otherwise, it won’t.

Note: There can be only one ng-app per page.

Problem:

But when we use it in portal environment, it not always be possible to have only one ng-app. (e.g) There might be other portlets on the same page developed using AngularJS or there can be instanceable portlet used more than once on the same page. In these cases, there are more than one ng-app on the same page. When we use structure shown above, it fails to work with portlet as only one ng-app is allowed per page.

Angular JS Version 1.3.15 , Angular Route JS 1.3.15, Liferay 6.2 CE GA3

Solution:

If you want to use it in portlets where there might be possibility to have more than one ng-app on a single page, you have to manually bootstrap ng-app as shown below :

<div id=”sampleapp<portlet:namespace/>”>

<div ng-view></div>

</div>

I defined ID to bind it with ng-app manually.

angular.bootstrap(document.getElementById("sampleapp<portlet:namespace/>"),['sampleApp<portlet:namespace/>']);

As shown above, we manually initialize ng-app with angular.bootstrap() method. By this method we can define more than one ng-app on the same page.

Second step is to define controller for the HTML code within ng-app scope.

<div id=”sampleapp<portlet:namespace/>”>

<div ng-controller="RouteController">

<div ng-view></div>

</div>

</div>

Module and controller for the same is defined below:

var module = angular.module("sampleApp<portlet:namespace/>", []);

module.controller("RouteController", function($scope) {

alert(“Controller executed”);

});

When page loads, your controller function is called and alert “Controller executed” is shown.

But if you want it to work with instanceable portlets, you have to add <portlet:namespace/> in controller and module name to make them unique. By adding <portlet:namespace/> in name of variable and controller, we are creating different controller per portlet instance and it will not conflict with other one as shown below:

var module<portlet:namespace/> = angular.module("sampleApp<portlet:namespace/>", []);

module<portlet:namespace/>.controller("RouteController<portlet:namespace/>", function($scope) {

alert(“Controller executed”);

});

Now, let’s add two links in our code with $routeProvider AngularJS directive.

$routeProvider is mechanism in anuglarjs by which you can load content without reloading page.It enable you to create different URL for the content so we can also take advantage of bookmark.

<div id="sampleapp<portlet:namespace/>">

<div ng-controller="RouteController<portlet:namespace/>">

<a href="#/route1">Route 1</a>

<a href="#/route2">Route 2</a>

<div ng-view></div>

</div>

</div>

routeProvider directive for this is defined below :

module<portlet:namespace/>.config(['$routeProvider','$locationProvider',

   function($routeProvider,$locationProvider) {

       $routeProvider.

           when('/route1', {               

               templateUrl: '/angular-instanceable-portlet/html/angular/angular-route-template-1.jsp',

               controller: 'RouteController<portlet:namespace/>'

           }).

           when('/route2', {

               templateUrl: '/angular-instanceable-portlet/html/angular/angular-route-template-2.jsp',

               controller: 'RouteController<portlet:namespace/>'

           });

     }]);

Single Page Application Liferay AngularJS When you click on Route 1 it goes to angular-route-template-1.jsp as shown in $routeProvider directive. Single Page Application Liferay AngularJS

But the problem with $routeProvider is, request it sends to angular-route-template-1.jsp is not a portlet request. It is basic servlet request. So you can’t take advantage of portlet request in angular-route-template-1.jsp.

(e.g) you can’t use any portlet defined object or implicit liferay object and any taglib like liferay-ui in  angular-route-template-1.jsp.

If you try to use you will get an exception as shown below :

Single Page Application Liferay AngularJS

Here, you can see NullPointerException is being thrown.

So if you want to convert basic servlet request into portlet request, you have to create resource URL as shown below :

<portlet:resourceURL var="template1">

   <portlet:param name="id" value="/html/angular/angular-route-template-1"/>

</portlet:resourceURL>

<portlet:resourceURL var="template2">

   <portlet:param name="id" value="/html/angular/angular-route-template-2"/>

</portlet:resourceURL>

Redefine $routeProvider directive as shown below :

$routeProvider.

       when('/route1', {               

              templateUrl: '${template1}',

              controller: 'RouteController<portlet:namespace/>'

          }).

       when('/route2', {

           templateUrl: ${template2}',

           controller: 'RouteController<portlet:namespace/>'

       });

After changing that you can use Liferay implicit objects in the jsp loaded by AngularJS.

Resultant Name from Liferay ThemeDisplay

As shown in above image, I used themeDisplay implicit object to display user name.


Explore more about our Angular JS Development Services.

Download:  Sample AngularJS Liferay Instanceable Portlet(war)

Contact us

For Your Business Requirements

Text to Identify Refresh CAPTCHA
Background Image Close Button

2 - 4 October 2024

Hall: 10, Booth: #B8 Brussels, Belgium