« 返回到 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 附件
106275 查看
平均 (3 票)
满分为 5,平均得分为 3.33333333333333。
评论
讨论主题回复 作者 日期
bump Andre Darian Bonner 2008年10月14日 下午1:55
I doesn´t work for me: My browser tells me... Daniel Breitner 2008年11月10日 上午4:54
It still doesn´t work: I am using Liferay 5.01... Daniel Breitner 2008年11月11日 上午5:01
OK, I found out the following: I works with... Daniel Breitner 2008年11月12日 上午4:12
I know this is late in this post but when I try... Glen K Brown 2009年3月9日 下午2:50
... but what about Liferay 6? It seems to be... Steffen Schuler 2010年5月2日 下午2:10
Yeah, we are transitioning to LR 6.0.3 (soon... Scott Langeberg 2010年7月27日 上午10:39
Ok, so if you look at js/liferay/events.js,... Scott Langeberg 2010年7月27日 下午12:03
The functionality is available in 6.0, but I... Blaine Boule 2010年7月28日 上午10:46
Update to my post. I looked into the JS of 6.x... Blaine Boule 2010年7月28日 上午11:32
Blaine, Please post some example code. Thanks, Long N. 2011年8月5日 上午7:03
I want to use IPC with Ajax. I am having 2... Keyur Ashra 2012年2月21日 下午11:02
I think there is an error in the post: the... Christian Straube 2012年8月16日 上午7:45

在 08-10-14 下午1:55 发帖。
I doesn´t work for me:

My browser tells me Liferay.trigger is not a function.
What did I do wrong ?
在 08-11-10 上午4:54 发帖。
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 ?
在 08-11-11 上午5:01 发帖。
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 ...
在 08-11-12 上午4:12 发帖。
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
在 09-3-9 下午2:50 发帖。
... 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?
在 10-5-2 下午2:10 发帖。
Yeah, we are transitioning to LR 6.0.3 (soon 6.0.4), and we need this capability.
在 10-7-27 上午10:39 发帖以回复 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!
在 10-7-27 下午12:03 发帖以回复 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
在 10-7-28 上午10:46 发帖以回复 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
在 10-7-28 上午11:32 发帖以回复 Blaine Boule
Blaine,
Please post some example code.

Thanks,
在 11-8-5 上午7:03 发帖以回复 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,......
在 12-2-21 下午11:02 发帖以回复 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 :-)
在 12-8-16 上午7:45 发帖以回复 Keyur Ashra