Recent Bloggers

Jorge Ferrer

Staff
59 Publications
23 décembre 2014

Olaf Kock

Staff
90 Publications
18 décembre 2014

David Kubitza

2 Publications
14 décembre 2014

Meera Prince

19 Publications
4 décembre 2014

James Falkner

Staff
100 Publications
2 décembre 2014

Juan Fernández

Staff
17 Publications
28 novembre 2014

Andrea Di Giorgi

2 Publications
26 novembre 2014

Gregory Amerson

Staff
26 Publications
25 novembre 2014

Cody Hoag

Staff
6 Publications
25 novembre 2014

Duke H

Staff
2 Publications
24 novembre 2014

Trying the AngularJS with Liferay

Company Blogs 3 février 2014 Par Sampsa Sohlman Staff

One of the current front-end trends is seems to be AngularJS. Last week we had HelsinkiJS Meetup and again there were couple presentations talking about AngularJS. Our Developer Unconfrence at Berlin we had an unconfrence discussion about AngularJS. So AngularJS is for sure the framework that Liferay developers are keen to use.
 
I decided to take a look AngularJS and how to use that in portlet development. Using any framework with portlets means that they should behave well in portlet world. So the first task is to see if AngularJS is up to it. Behaving well in portlet world means that the framework has to have tools so it can respect the portal environment. As a normal web application, front end developer has total freedom to create what they want, but when they are stepping into portal world, their creation has to fit in limitation of the portlet. 
 
 
As most of you already know, portlet's UI limitation is rendering area, which means that portlet should always stick to its area. See the image. Traditional portlet developers we have tools as <portlet:namespace/> to achieve this. So I want to test if AngularJS can achieve this.
 
By looking AngularJS tutorial I see that AngularJS application contains modules, which have controllers. AngularJS tutorial shows, how bind module to view:
 
<html ng-app="myapp">
..
</html>
 
By looking this, my first impression is that AngularJS is not portlet ready since portlet does not own the page. As I study further reading it is clear ng-app does not have to be bound to html tag, but it can be bound to any tag. Next question can you have multiple tags with ng-app attributes same time at page as we can have multiple portlets and also multiple portlet instances. Quick proof of concept proofs other vice, so there can be only one ng-app attribute per page. So AngularJS seems to be failing the portlet world, but I decided to dig deeper and after further study I did find out that ng-app can be replaced by API call:
angular.bootstrap(<dom element>,<list of modules>);

Finally, I could confirm that with this API call it is possible have multiple AngularJS modules at one page and be bound to multiple dom elements.

This did lead me to integrate this Liferay.Portlet.ready(..) event and I did result following framework (angular-portlet.js):

(function(Liferay, angular) {
   if (angular.portlet)
      return;

   angular.portlet = {};

   var angularPortlets = {};

   angular.portlet.add = function(pluginName, portletName, angularFunction) {
      var portletId = "_WAR_" + pluginName.replace(/[_]|[-]/g, "");

      portletId = portletName.replace(/[_]|[-]/g, "") + portletId;
      angularPortlets[portletId] = angularFunction;
   };

   Liferay.Portlet.ready(function(portletInstanceId, node) {
      var portletId = portletInstanceId.replace(/[_]INSTANCE[_].+/g, "");

      if (angularPortlets[portletId]) {
         angular.bootstrap(node.getDOMNode(), angularPortlets[portletId](
            portletInstanceId, node.getDOMNode()));
      }
   });
})(Liferay, angular);

The framework is using plugin name + portlet name to register AngularJS modules to specific portlet. During the Liferay.portlet.ready event the module is bound to the portlet's dom element.

Following example demonstrate how this is done:

(function(Liferay, angular) {
   angular.portlet.add("poc-angular-portlet", "poc-angular-portlet",
      function() {
         var myModule = angular.module("myModule", []);

         myModule.controller("MyController", function($scope) {
            $scope.mythings = [ {
               name : "Thing 1"
            }, {
               name : "Thing 2",
            } ];

            $scope.add = function() {
               $scope.mythings.push({name: $scope.newThing.name});
            };

            $scope.remove = function(index) {
               $scope.mythings.splice(index, 1);
            };
         });

         return [ myModule.name ];
      });
})(Liferay, angular);
Here you can see that code is same as AngularJS JavaScript except you just have to wrap it to function and that to angular.portlet.add(..) with plugin name and portlet-name.
 
and correseponding html:
<div ng-controller="MyController">
    <h1>My Things</h1>
    <input ng-model="newThing.name"/>
    <button ng-click="add();">Add</button>
    <div ng-repeat="mything in mythings">
        <span>{{mything.name}}</span>
        <button ng-click="remove($index);">Remove</button>
    </div>
</div>
As you can see that there is no <portlet:namespace/> tags in HTML, since AngularJS only is scanning markup inside bootstrap element. 
As you can see portlet works well also as instantiable and this seems to be a good start to AngularJS portlet development.
 
Example app:
 
Thanks to make this possible:
EDIT 11th May 2014: It seems that Liferay js minifier does not like angularjs and as on my development portal it is disabled, so it did not appeared to me. Thanks for Miika Alonen for finding this.
 
You can disable minifier from portal-ext.properties
minifier.enabled=false
 
Affichage de 1 résultat(s).