Using Seam Remoting to Add a Little State to Your jQuery UI

Print This Post Print This Post

When using different technologies that were developed completely independent of one another, in the same project, achieving a seamless means of interoperability can be elusive.

At my job I am working on a web application project which, among other things, utilizes JBoss Seam and jQuery in the technology stack. jQuery is a reasonable recent addition to the list, and to my skillset, but the learning curve wasn’t much of a challenge due to the outstanding architecture created by the jQuery developers. I was able to find a couple of areas which jQuery actually could replace existing code and improve performance and overall user experience. This is one of them…

A particular page on the site featured a tabbed interface, implemented via rich:tabPanel. For whatever reason I don’t feel like expanding on, the performance of RichFaces was a bit on the slow side and due to the page UI requirements, made for some convoluted JSF code. This is where jQuery fit right in. I was able to put in the tabbed component from jQuery UI using the AJAX loading strategy and not only received some cleaner code, but a boost in performance both on the server and client side. There is a bit more that I needed to do to meet the UI requirements, but that will be left to a later post. Really, this is nothing novel, or special, but demonstrates a fluid integration of completely separate technologies.

Because the process flow of the site often had users navigate away from and back to the tabbed page, I put in a small EJB3 to remember the user’s active tab selection. To allow users to open new browser tabs, I scoped the bean onto the conversation context. I’m not generally a big fan of making my core EJB3 beans overly specialized, I wrote the tab controller to support several possible tabbed pages.

First, the EJB3 interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Local
public interface ActiveTabController
{
 
	@WebRemote
	public void setActiveTab(String group, String id);
 
	@WebRemote
	public String getActiveTab(String group);
 
	public void create();
	public void destroy();
}

And the implementation (with some unimportant code removed):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Name("activeTabController")
@Stateful
@Scope(ScopeType.CONVERSATION)
@BypassInterceptors
public class ActiveTabControllerBean implements ActiveTabController
{
	private Map tabMap = new HashMap();
 
	public void setActiveTab(String group, String id)
	{
		tabMap.put(group, id);
	}
 
	public String getActiveTab(String group)
	{
		return tabMap.get(group);
	}
 
	@Create
	@Begin(join=true)
	public void create()
	{
 
	}
 
	@Destroy
	@Remove
	@End
	public void destroy()
	{
 
	}
}

In the XHTML facelet you’ll need:

1
2
<!-- Include Seam Remoting interface to EJB bean -->
<s:remote include="activeTabController"/>

Javascript Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//<![CDATA[
 
// Set the conversation id into Seam Remoting.
Seam.Remoting.getContext().setConversationId( #{conversation.id} );
 
// Get the Remoting interface to our EJB3
var activeTabController = Seam.Component.getInstance("activeTabController");
 
// Get the activeTab value up front rather than making any unnecessary AJAX calls
var activeTab = "#{activeTabController.getActiveTab('a-unique-id')}";
 
// You'll probably need to do this
$j = jQuery.noConflict();
 
$j(document).ready(function($j){ 
 
	if (activeTab == "") { 
		activeTab = 0; 
	}
 
	$j('#tabs').tabs({selected : activeTab });
 
	$j('#tabs').bind('tabsshow', function(event, ui) { 
		onTabSwitch(ui.index); 
	}); 
 
});
 
function onTabSwitch(idx) 
{
	activeTabController.setActiveTab("a-unique-id", idx, function() { 
		// Placeholder
	}); 
} 
 
//]]>

HTML Layout:

1
2
3
4
5
6
7
8
<ul>
	<li>
		<span>Page 1</span></li>
	<li>
		<span>Page 2</span></li>
	<li>
		<span>Page 3</span></li>
</ul>

Comments are closed.