« Back

Hooking into Liferay's Javascript functions

Company Blogs February 4, 2008 By Nate Cavanaugh Staff

Have you ever wanted to plugin to Liferay's javascript functions, but didn't want to modify the original Javascript, and didn't want to branch off of it? I know I have.

For instance, let's say that there is a Liferay javascript function that always adds a class, but you don't want to add that class, and instead want to use your own. Well, there is a little known method that does this that's in Liferay 4.3.x.

The method is called Liferay.Util.actsAsAspect. This little method adds AOP (aspect oriented programming) features, that allow you to hook into existing Javascript without a plugin framework being in place.

So, I'll use a good example of this. In Liferay, if you don't want the dock to be a drop down, you can go into dock.vm in your theme, and remove the class name of interactive-mode off of the main dock container.
This allows you to easily disable that feature.
But recently, we needed to remove this class, but didn't want to keep a floating copy of dock.vm where the only difference is one little class name.

So we could either modify the file anyways, or we could use Javascript to remove the class name. But then there become a race condition, where you're trying to race against the Dock javascript to remove the class before it can find the class and begin the transformation.

Well, that's where Liferay.Util.actsAsAspect comes in.

What it does is it takes the object you pass to it, and augments it to contain three new methods: before, after, and around.

So, in our case, we needed to modify Liferay.Dock (the class that creates the dock), and we needed a way to guarantee that the class name interactive-mode was removed, thereby preventing the Dock class from even seeing that there is a dock to modify.

So, here is what we did:

Liferay.Util.actsAsAspect(Liferay.Dock);

Liferay.Dock.before(
    'init',
    function(){
        jQuery('.interactive-mode').removeClass('interactive-mode');
    }
);

So, you see, now every time Liferay.Dock.init() runs, it will always execute our little function we passed into it first.

We can also run Javascript after, using Liferay.Dock.after() and passing in the name of our method, and a function we wish to execute.

We can also wrap the init function with code, like so:

Liferay.Dock.around(
'init',
function () {
    var dock = jQuery('.interactive-mode');
    dock.removeClass('interactive-mode');
   
    this.yield(); // This line executes the original function
   
    dock.addClass('interactive-mode'); // This line now adds the class back after the function fires
}
);

Now, the next question you may be asking is, how do you do this for classically defined functions (you know the ones that look something like: onLoad() or _88_updatePage()), and don't have a parent object that we can manipulate.

Well, we actually can manipulate because all functions defined like this: function _88_updatePage() {}, all exist inside of the "window" object. The "window" object is the global namespace where normal functions are added by default.

So, here is how we would execute a function before _88_updatePage get's executed:

Liferay.Util.actsAsAspect(window);

window.before(
'_88_updatePage',
function (month, day, year){
    alert('I am just about to update the page');
}
);

So, anyways, I thought that this would be helpful to people who need to hook into our existing Javascript, and can't wait till we implement our formal JS event system :)
 

Threaded Replies Author Date
You can't even imagine how timely this post... Ray Augé February 4, 2008 2:06 PM
ok! what if the methods has params... do I just... Ray Augé February 4, 2008 2:15 PM
Yes sir :) Just like that. Nate Cavanaugh February 4, 2008 2:18 PM
I have this: ... Ray Augé February 4, 2008 2:31 PM
Yeah, sorry, I should have mentioned that. For... Nate Cavanaugh February 4, 2008 2:36 PM
Yup, that works.. you rock Nate.. Ray Augé February 4, 2008 2:40 PM
Hi, I'm using Liferay 6.0.5 and I used the your... Alexandra Mereuta October 28, 2010 9:03 AM
I was pleasantly surprised that this post was... Barry Rowe May 17, 2011 1:12 PM
The test. Hamidreza Soleimani February 5, 2008 3:52 AM
Very very cool! Keep this type of post coming :) Jorge Ferrer February 5, 2008 3:55 AM
Great post! Thanks Nate! Edward Shin February 15, 2008 2:30 PM

You can't even imagine how timely this post was... I was about 2 minutes away from asking you how to best extend an existing function...

This is very clever...
Posted on 2/4/08 2:06 PM.
ok! what if the methods has params... do I just cascade those down?

Liferay.Navigation.around(
'_removePage',
function (params) {
...
this.yield(params) ;
...
}
) ;
Posted on 2/4/08 2:15 PM in reply to Ray Auge.
Yes sir emoticon Just like that.
Posted on 2/4/08 2:18 PM in reply to Ray Auge.
I have this:

Liferay.Util.actsAsAspect(Liferay.Navigation);

Liferay.Navigation.around(
'_­removePage',
function (obj, instance) {
alert('test');
}
);

new Liferay.Navigation(
{
layoutIds: [<%= ListUtil.toString(layouts, "layoutId") %>],
navBlock: '#navigation',
hasPermission: <%= GroupPermissionUtil.contains(permissionChecker, portletGroupId.longValue(), ActionKeys.MANAGE_LAYOUTS) %>
}
);

this doesn't work... I'm doing something wrong aren't I?
Does an instance of an object need to be treated differently?
Posted on 2/4/08 2:31 PM in reply to Nate Cavanaugh.
Yeah, sorry, I should have mentioned that. For instantiable classes, you would modify that objects prototype, like so:

Liferay.Util.actsAsAspect(Liferay.Navigation.prototype);

Liferay.Navigation.pr­ototype.around(...)

That will also make sure that every instance of it will get your function with it.
Posted on 2/4/08 2:36 PM in reply to Ray Auge.
Yup, that works.. you rock Nate..
Posted on 2/4/08 2:40 PM in reply to Nate Cavanaugh.
Posted on 2/5/08 3:52 AM.
Very very cool!

Keep this type of post coming emoticon
Posted on 2/5/08 3:55 AM.
Great post! Thanks Nate!
Posted on 2/15/08 2:30 PM.
Hi,
I'm using Liferay 6.0.5 and I used the your method to hook into some navigation javascript. The problem I have is that for IE8 I get this message: 'Liferay.Navigation.prototype' is null or not an object. Do you know what the problem might be?

Thanks
Posted on 10/28/10 9:03 AM in reply to Nate Cavanaugh.
I was pleasantly surprised that this post was still dead on for LR 6.0 EE SP1. The Example Ray was working through above was exactly what we needed to do. Our end result looks like so:

<aui:script position="inline" use="liferay-navigation">
Liferay.Util.actsAsAspect(Liferay.Navigation.prototype);
Liferay.Navigation.prototype.around('_removePage',
function(event){
alert('before');
//do your work here
this.yield(event);
alert('after');
//do more work here
});
</aui:script>
Posted on 5/17/11 1:12 PM in reply to Alexandra Mereuta.