Liferay is a Gartner Magic Quadrant Leader for the Sixth Year! Find out why
« Back to Inter-portlet...

Client-side Inter-Portlet Communication

Note: This document refers to Liferay 5. To see a similar document valid for Liferay 6, refer to Portlet to Portlet Communication or an article especially for using Java Script.

Introduction #

Communication across portlets is one of the hottest topics of portlet and portal application development. By allowing communication across portlets it's possible to show changes in one portlet based on user interactions with another portlet.

The JSR-286 (Portlet 2.0) specification provides a standard mechanism to do inter-portlet communication (IPC). Actually, it provides two ways: a simple method based on shared parameters, and a more complex event-based approach. If either of these methods fits your needs, by all means use them, since they are started and fully supported in Liferay since version 5.1. But IPC is a very broad topic and there is no solution that fits all, so Liferay provides an additional method based on JavaScript to perform communication purely within the client.

Liferay JavaScript Event System #

The client-side mechanism to communicate portlets is based on events. Using events instead of direct scripting calls across portlets is very important since it's not possible to know at deployment time which other portlets will be available in a given page. An event-based model allows one portlet to provide a notification that something of significance has happened, without establishing a hard dependency. Any other portlets on the same page that are interested in that event and have registered listeners are then able to react accordingly.

The API of this system is very simple and is based on two methods

 Liferay.trigger(eventName, data)
 Liferay.bind(eventName, function, [scope])

These methods have been deprecated in Liferay 6 in favor of

 Liferay.fire(eventName, data)
 Liferay.on(eventName, function, [scope])

Binding to events #

So, what Liferay.bind does is that it lets you listen in for any arbitrary event (it could be anything the developer decides, or an existing one, though we don't have many just yet). I'll use two examples of bind using events that are currently in place:

Let's say we want to listen for when a portlet has loaded, and if it's a journal content portlet, we'd like to limit the article so that it only shows the first 3 paragraphs, and you have to click a "More" link to show the rest. Here is how we would do it:

 Liferay.bind(
 'portletReady',
 function(event, data){
   var portletId = data.portletId;
   var portlet = data.portlet;

   if(portletId.indexOf('56_INSTANCE') > -1){
     var paragraphs = portlet.find('p');
     var hiddenP = paragraphs.slice(3);
     hiddenP.hide();

     var showLink = jQuery('<a href="javascript: ;">More</a>');
     hiddenP.after(showLink);

     showLink.click(
         function(){
         hiddenP.show();
         showLink.remove();
       }
     );
   }
 }
 );

So, that's one way, if we wanted to execute when a portlet is ready. But let's say we wanted to listen for when portlets are closed. Currently, this is how we'd do it:

 Liferay.bind(
   'closePortlet', 
   function(event, data){
     // run javascript
     // data contains data.plid and data.portletId
   }
 );

The optional scope argument allows the code listening to the event say what scope the function should run in. For instance, by default, when you run the function, and you use the "this" variable, it points to the document object. But let's say you have an existing function that sits inside another object (let's say Liferay.Navigation, for instance), and the function has to have access to the methods of that object. Since the method is already defined you wouldn't want to overwrite it, so what you can do is say:

 Liferay.bind('portletReady', Liferay.Navigation.init, Liferay.Navigation);

This will tell the Liferay.Navigation.init to point this to itself, rather than to the document object.

Okay, so that's the listening aspect. The pushing aspect is Liferay.trigger.

Triggering events #

Liferay.trigger() pushes the event out there so that any possible events that are listening will be executed. Liferay.trigger takes the two arguments, the eventName, and the data you wish to send to the listening functions. It doesn't have to be an object hash, but it's highly recommended, since it can contain multiple values.

Let's see a quick example of how you would quickly communicate two portlets: Let's say in Portlet 1 you have a list of users, and in Portlet 2 you have a list of articles published by different users. Assuming both are on the same page, you could have functionality where, when you click on a user in Portlet 1, it does an ajax call and grabs all of the articles by that user. But you want it loosely coupled, and only want it to happen if they're both on the same page.

I'm going to make a big assumption with the HTML, but it doesn't have to be this way. But in Portlet 1, we have our user links, which all have a class of .user-name, and just for simplicity in this case, a hidden input right next to the users link.

In Javascript, we would do this:

 jQuery(
    function () {
      jQuery('a.user-name').click(
        function(event) {
          var userId = jQuery(this).next().val();
          // Other related javascript...
          Liferay.trigger('userSelected', {userId: userId});
          return false;
        }
      )
    }
 );

This says, on page load, grab all links with the class name of user-name, and assign a click event (what we would normally do with JS actions on links). We'll grab the users ID, run some normal portlet specific JS (in this case where the comment would be) and then we would trigger our event, which is userSelected, in this case.

Then, in Portlet 2, you would have a listener set up, like this:

 Liferay.bind(
   'userSelected',
   function(event, data) {
     var userId = data.userId;
     jQuery('.article-results').html('');
     if (userId) {
       jQuery.ajax(
         {
           url: '/our/portlet/url',
           data: {
           userId: userId
         },
         error: function() {
           jQuery('.article-results').html('' + 
                   Liferay.Language.get('sorry-there-was-an-error') + '');
         },
         success: function(message) {
           jQuery('.article-results').html(message);
         }
       );
     }
   }
 );

This does an ajax call which grabs our results, and handles the different scenarios.

0 Attachments
93202 Views
Average (3 Votes)
The average rating is 3.33333333333333 stars out of 5.
Comments
Threaded Replies Author Date
bump Andre Darian Bonner October 14, 2008 1:55 PM
I doesn´t work for me: My browser tells me... Daniel Breitner November 10, 2008 4:54 AM
It still doesn´t work: I am using Liferay 5.01... Daniel Breitner November 11, 2008 5:01 AM
OK, I found out the following: I works with... Daniel Breitner November 12, 2008 4:12 AM
I know this is late in this post but when I try... Glen K Brown March 9, 2009 2:50 PM
... but what about Liferay 6? It seems to be... Steffen Schuler May 2, 2010 2:10 PM
Yeah, we are transitioning to LR 6.0.3 (soon... Scott Langeberg July 27, 2010 10:39 AM
Ok, so if you look at js/liferay/events.js,... Scott Langeberg July 27, 2010 12:03 PM
The functionality is available in 6.0, but I... Blaine Boule July 28, 2010 10:46 AM
Update to my post. I looked into the JS of 6.x... Blaine Boule July 28, 2010 11:32 AM
Blaine, Please post some example code. Thanks, Long N. August 5, 2011 7:03 AM
I want to use IPC with Ajax. I am having 2... Keyur Ashra February 21, 2012 11:02 PM
I think there is an error in the post: the... Christian Straube August 16, 2012 7:45 AM

Posted on 10/14/08 1:55 PM.
I doesn´t work for me:

My browser tells me Liferay.trigger is not a function.
What did I do wrong ?
Posted on 11/10/08 4:54 AM.
It still doesn´t work:

I am using Liferay 5.01 together with ICEfaces and I used your example 1:1 - but still I get:
"Liferay.trigger()" ist not a function.

Do I have to import something ?
Is Liferay 5.01 the correct Version ?
Posted on 11/11/08 5:01 AM.
OK, I found out the following:

I works with Liferay 5.1.2 but not with Liferay 5.0.1.

I changed the wiki entry above ...
Posted on 11/12/08 4:12 AM.
I know this is late in this post but when I try (using 5.2.1) to use Liferay.bind in asset publisher using web content the process is triggered before any content is loaded into the asset publisher. Is that correct? In the end none of my a tags get processed because, it seems, they are not loaded yet. Am I thinking about this wrong?

thanks
Posted on 3/9/09 2:50 PM.
... but what about Liferay 6? It seems to be not available anymore by default. Is it obsolet or do we need to add it manually?
Posted on 5/2/10 2:10 PM.
Yeah, we are transitioning to LR 6.0.3 (soon 6.0.4), and we need this capability.
Posted on 7/27/10 10:39 AM in reply to Steffen Schuler.
Ok, so if you look at js/liferay/events.js, you'll see that Liferay is simply wrapping function calls into jQuery, with same names. Therefore, I'm guessing it's just a matter of hosting jquery yourself, and calling direct to jquery. Is my current solution!
Posted on 7/27/10 12:03 PM in reply to Steffen Schuler.
The functionality is available in 6.0, but I think the method signature has changed. The callback used in Liferay.bind used to be a function with signature of functionName(event,data){}, in 6.0 all of our ClientSide Eventing broke because 'data' was undefined. I removed the 'event' from the method signature, so it looked like functionName(data){} and everything is working again.

Blaine
Posted on 7/28/10 10:46 AM in reply to Scott Langeberg.
Update to my post. I looked into the JS of 6.x and they have depricated the use of Liferay.bind() and Liferay.trigger() - they will be present for now, but removed in later versions. They have been replaced with Liferay.on() and Liferay.fire()

Check out: http://issues.liferay.com/browse/LPS-2155
Posted on 7/28/10 11:32 AM in reply to Blaine Boule.
Blaine,
Please post some example code.

Thanks,
Posted on 8/5/11 7:03 AM in reply to Blaine Boule.
I want to use IPC with Ajax. I am having 2 Portlets (MVC Portlet). For eg. Portelt A has 1 textbox through which you can serach something from DB. And in Portlet B I wish to display the Data. I achieve this functionality without AJAX, but as it is refreshing the page every time i wish to do it through AJAX now. Can anyone please kindly help me in this,......
Posted on 2/21/12 11:02 PM in reply to Long Nguyen.
I think there is an error in the post: the function in the on area has only one(!) parameter:

Liferay.bind(
'userSelected',
function(event, data) {

should look like
Liferay.bind(
'userSelected',
function(data) {

Besides this a very good post and a nice functionality :-)
Posted on 8/16/12 7:45 AM in reply to Keyur Ashra.