JQuery for Everyone: Aspect-Oriented Programming with jQuery
The jQuery community grows daily with new converts and new projects. I try to stay on top of it, but with no programming background I also find myself back-peddling through wiki articles when I come across foreign concepts like Aspect-Oriented Programing (AOP).
The more I read about AOP and what the jquery-AOP project aims to do, the more excited I got about adding it to my SharePoint toolbox. However, after my first test case, I started wondering if I would ever use it.
What it does:
The jquery-AOP project used jQuery to create functions that can treat object methods and global functions as aspects. That means, that you can run other code before, after, or around them.
For example, let’s say you want a pop-up message box to appear before someone uses the document check-out feature. You have a couple of choices: you can rewrite the check-out function(s) to include your message, rewrite the links/code that trigger the check-out to include your code first, or you can treat the check-out function as an aspect.
jQuery.aop.before( {target: window, method: 'SomeSharePointFunction'}, function() { alert('About to execute SomeSharePointFunction'); } );
By treating the OOB function as an aspect, we accomplish a few goals:
- We implement our change “globally” with minimal change to the rendered page.
- We keep OOB code in tact so future updates don’t trash our stuff and we can still use the OOB code as intended.
- We implement our change seamlessly avoiding pause routines and other timing hacks.
What it Doesn’t Do (Easily):
My first choice for this pattern, the infamous ExpGroupRenderData(htmlToRender, groupName, isLoaded) function. As an aspect, we could fire our code before, after or around it. That’s brilliant because I’m always rewriting that function for my work with dynamically loading content. SharePoint uses that function to render HTML it loads on demand due to events like expanding groups.
However, my code running before or after ExpGroupRenderData runs completely independently and without sharing of variables or parameters. The event that triggers ExpGroupRenderData does so passing important information: htmlToRender, groupName, and isLoaded. If my code needs that data, I have no easy way to get it.
Do we need that data?
Well, in cases where we don’t care if a process runs over an element multiple times (like HTML calculated columns), we can use it. However, if we’re binding events (like an onmouseover to trigger a preview), the event will get bound multiple times. This could cause problems as we trigger the event and our code fires off multiple times (if it’s a GET request, we’re about to make our server admin very unhappy).
In my rewrite of ExpGroupRenderData, I use the groupName information to only bind events to the elements in that group. Since ExpGroupRenderData will only fire once per page load per group, I don’t have to worry about “stacking” events.
function ExpGroupRenderData(htmlToRender, groupName, isLoaded) { $("#tbod"+groupName+"_").attr("isloaded",isLoaded) .html(htmlToRender) .show("fast",initjLoadMe("#tbod"+groupName+"_")); }
Around to Help:
This is how I’m using around in my most recent tests. The aop.around method can tell us the parameters passed during the “invocation” (the instance of the target method) as arguments. However, we can’t simply fire our initjLoadMe function during the around method because the target method has not completed yet (and no HTML has been rendered).
So, what I’ve done is use the jQuery.data method to write the data I need to the main content div. Then I use the aop.after method to check for data attached to the main content div.
$.aop.around( {target: window, method: 'ExpGroupRenderData'}, function (invocation) { if (invocation.arguments[2] == 'true') { //check to see if html loaded var group = "#tbod"+invocation.arguments[1]+"_"; //get groupname var elm = $("#MSO_ContentTable").get(0); $.data(elm, "group", group); //write data to div } return invocation.proceed(); } ); $.aop.after( {target: window, method: 'ExpGroupRenderData'}, function() { var elm = $("#MSO_ContentTable").get(0); var group = $.data(elm, "group"); if (group) { initjLoadMe(group); //if data exists run init(data) $.data(elm, "group", ""); //clear out data } } );
Conclusion:
While more code, the end product also seems more elegant. For a modular approach to enhancing the SP interface, I could see using this technique. But for most projects, it’s probably a little more work than otherwise necessary.
Are there any areas you could use AOP to improve your SharePoint interface?
- JQuery for Everyone: Accordion Left Nav
- JQuery for Everyone: Print (Any) Web Part
- JQuery for Everyone: HTML Calculated Column
- JQuery for Everyone: Dressing-up Links Pt1
- JQuery for Everyone: Dressing-up Links Pt2
- JQuery for Everyone: Dressing-up Links Pt3
- JQuery for Everyone: Cleaning Windows Pt1
- JQuery for Everyone: Cleaning Windows Pt2
- JQuery for Everyone: Fixing the Gantt View
- JQuery for Everyone: Dynamically Sizing Excel Web Parts
- JQuery for Everyone: Manually Resizing Web Parts
- JQuery for Everyone: Total Calculated Columns
- JQuery for Everyone: Total of Time Differences
- JQuery for Everyone: Fixing Configured Web Part Height
- JQuery for Everyone: Expand/Collapse All Groups
- JQuery for Everyone: Preview Pane for Multiple Lists
- JQuery for Everyone: Preview Pane for Calendar View
- JQuery for Everyone: Degrading Dynamic Script Loader
- JQuery for Everyone: Force Checkout
- JQuery for Everyone: Replacing [Today]
- JQuery for Everyone: Whether They Want It Or Not
- JQuery for Everyone: Linking the Attachment Icon
- JQuery for Everyone: Aspect-Oriented Programming with jQuery
- JQuery for Everyone: AOP in Action - loadTip Gone Wild
- JQuery for Everyone: Wiki Outbound Links
- JQuery for Everyone: Collapse Text in List View
- JQuery for Everyone: AOP in Action - Clone List Header
- JQuery for Everyone: $.grep and calcHTML Revisited
- JQuery for Everyone: Evolution of the Preview
- JQuery for Everyone: Create a Client-Side Object Model
- JQuery for Everyone: Print (Any) Web Part(s) Plugin
- JQuery for Everyone: Minimal AOP and Elegant Modularity
- JQuery for Everyone: Cookies and Plugins
- JQuery for Everyone: Live Events vs. AOP
- JQuery for Everyone: Live Preview Pane
- JQuery for Everyone: Pre-populate Form Fields
- JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)
- Use Firebug in IE
- JQuery for Everyone: Extending OWS API for Calculated Columns
- JQuery for Everyone: Accordion Left-nav with Cookies Speed Test
- JQuery for Everyone: Email a List of People with OWS
- JQuery for Everyone: Faster than Document.Ready
- jQuery for Everyone: Collapse or Prepopulate Form Fields
- jQuery for Everyone: Hourly Summary Web Part
- jQuery for Everyone: "Read More..." On a Blog Site
- jQuery for Everyone: Slick Speed Test
- jQuery for Everyone: The SharePoint Game Changer
- JQuery For Everyone: Live LoadTip
I must confess, this is too complicated for my understanding. Can you show an example in action?
@George,
I’m almost ready with my first implementation. It will postback here when it publishes.
Thanks,
Paul
I just written a post discussing jQuery as the SharePoint Band Aid. Be interested to hear your thoughts:
I’ve just written a post discussing jQuery and SharePoint. Be interested to hear your thoughts… http://wss.made4the.net/archive/2009/02/23/jquery-the-sharepoint-band-aid.aspx
Jeremy – I’ll jump over and comment on your post, but this is also part of a discussion on Joel’s blog last week. There is a camp of people who are requesting the removal of the Content Editor Web Part from the web part gallery selection because of jQuery and the things it can do.
This might turn out to be a pretty good WWF Smackdown, which I’m not sure translates well for you guys down under, but means “Let’s get it on!”
Mark
I posted my reply. My opinions are my own and do not represent EUSP or its authors.
jQuery is not the “problem.” Poor planning and governance coupled with overworked or under-trained administrators raises demand for something like jQuery.