« 返回

Hooking into Liferay's Javascript functions

Company Blogs 2008年2月4日 按 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 :)
 

讨论主题回复 作者 日期
You can't even imagine how timely this post... Ray Augé 2008年2月4日 下午2:06
ok! what if the methods has params... do I just... Ray Augé 2008年2月4日 下午2:15
Yes sir :) Just like that. Nate Cavanaugh 2008年2月4日 下午2:18
I have this: ... Ray Augé 2008年2月4日 下午2:31
Yeah, sorry, I should have mentioned that. For... Nate Cavanaugh 2008年2月4日 下午2:36
Yup, that works.. you rock Nate.. Ray Augé 2008年2月4日 下午2:40
Hi, I'm using Liferay 6.0.5 and I used the your... Alexandra Mereuta 2010年10月28日 上午9:03
I was pleasantly surprised that this post was... Barry Rowe 2011年5月17日 下午1:12
The test. Hamidreza Soleimani 2008年2月5日 上午3:52
Very very cool! Keep this type of post coming :) Jorge Ferrer 2008年2月5日 上午3:55
Great post! Thanks Nate! Edward Shin 2008年2月15日 下午2:30

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...
在 08-2-4 下午2:06 发帖。
ok! what if the methods has params... do I just cascade those down?

Liferay.Navigation.around(
'_removePage',
function (params) {
...
this.yield(params) ;
...
}
) ;
在 08-2-4 下午2:15 发帖以回复 Ray Auge
在 08-2-4 下午2:18 发帖以回复 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?
在 08-2-4 下午2:31 发帖以回复 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.
在 08-2-4 下午2:36 发帖以回复 Ray Auge
Yup, that works.. you rock Nate..
在 08-2-4 下午2:40 发帖以回复 Nate Cavanaugh
在 08-2-5 上午3:52 发帖。
Very very cool!

Keep this type of post coming emoticon
在 08-2-5 上午3:55 发帖。
在 08-2-15 下午2:30 发帖。
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
在 10-10-28 上午9:03 发帖以回复 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>
在 11-5-17 下午1:12 发帖以回复 Alexandra Mereuta