First ajax portlet shows the seconds ones response - ajax

I am new to liferay. I want two portlet to fetch data from the two different tables of the database. The data has to be fetched automatically without refreshing the page(using ajax). The problem is the portlets I created are fetching the data and showing it properly, if one of them is deployed in a page. If both of them are deployed then they are showing the second portlets tables data i.e if only one portlet is in the portal its showing the correct data (the specific portlet's table) and automatically fetching, if both are in portal they are fetching the second deployed portlet's data(showing same data for both portlets).
Here is the jsp code of both of my portlet, I am using service builder for fetching data.
Portlet A
view.jsp
<%#page contentType="text/html"%>
<%#page pageEncoding="UTF-8"%>
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%# taglib uri="http://alloy.liferay.com/tld/aui" prefix="aui"%>
<%# taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui"%>
<%# page import="javax.portlet.PortletContext"%>
<%# page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
<%# page import="javax.portlet.RenderRequest"%>
<%# page import="java.util.*"%>
<%# page import="javax.portlet.*"%>
<portlet:defineObjects />
<portlet:actionURL
windowState="<%=LiferayWindowState.EXCLUSIVE.toString()%>"
var="fetchDatabase">
<portlet:param name="databaseFetch" value="fetchWorkData"></portlet:param>
</portlet:actionURL>
<%
PortletPreferences prefs = renderRequest.getPreferences();
%>
<script type="text/javascript">
var url = '<%=fetchDatabase.toString()%>';
$(document).ready(function() {
$("#fetchLink").click(function() {
$.post(url).done(function(data) {
$("#fetchData").html(data);
});
});
});
$(document).ready(function() {
//For Initial loading of database
$('#fetchData').load(url);
function timeRefresh() {
// setTimeout("location.reload(true);",timeoutPeriod);
// make a ajax call here.
$.post(url).done(function(data) {
$("#fetchData").html(data);
});
}
//Recalling the function repeatedly in given interval
setInterval(function() {
timeRefresh();
}, 6000);
});
</script>
<aui:layout id="fetchedData">
<aui:button value="Refresh" id="fetchLink"></aui:button>
<hr />
<aui:layout id="fetchData"></aui:layout>
</aui:layout>
FetchData.java
package com.cherry.ajax.database;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import com.cherry.ajax.database.service.TestLocalServiceUtil;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.util.bridges.mvc.MVCPortlet;
/**
* Portlet implementation class FetchData
*/
public class FetchData extends MVCPortlet {
String action = "";
#Override
public void processAction(ActionRequest request, ActionResponse response)
throws IOException, PortletException {
action = request.getParameter("databaseFetch");
try {
TestLocalServiceUtil.add();
} catch (SystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void doView(RenderRequest request, RenderResponse response)
throws IOException, PortletException {
response.setContentType("text/html");
PortletRequestDispatcher dispatcher = null;
if (action.equals("fetchWorkData")) {
dispatcher = getPortletContext().getRequestDispatcher(
"/html/jsp/showData.jsp");
} else {
dispatcher = getPortletContext().getRequestDispatcher(
"/html/jsp/view.jsp");
}
action = "";
dispatcher.include(request, response);
}
}
Portlet B
view.jsp
<%#page contentType="text/html"%>
<%#page pageEncoding="UTF-8"%>
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%# taglib uri="http://alloy.liferay.com/tld/aui" prefix="aui"%>
<%# taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui"%>
<%# page import="javax.portlet.PortletContext"%>
<%# page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
<%# page import="javax.portlet.RenderRequest"%>
<%# page import="java.util.*"%>
<%# page import="javax.portlet.*"%>
<portlet:defineObjects />
<portlet:actionURL
windowState="<%=LiferayWindowState.EXCLUSIVE.toString()%>"
var="workloadUrl">
<portlet:param name="drAction" value="getWorkData"></portlet:param>
</portlet:actionURL>
<%
PortletPreferences prefs = renderRequest.getPreferences();
%>
<script type="text/javascript">
var url = '<%=workloadUrl.toString()%>';
$(document).ready(function() {
$("#workloadLink").click(function() {
$.post(url).done(function(data) {
$("#drData").html(data);
});
});
});
$(document).ready(function() {
//For Initial loading of database
$('#drData').load(url);
function refresh() {
// setTimeout("location.reload(true);",timeoutPeriod);
// make a ajax call here.
$.post(url).done(function(data) {
$("#drData").html(data);
});
}
//Recalling the function repeatedly in given interval
setInterval(function() {
refresh();
}, 6000);
});
</script>
<aui:layout id="DrWorkload">
<aui:button value="Refresh" id="workloadLink"></aui:button>
<hr />
<aui:layout id="drData"></aui:layout>
</aui:layout>
DrStatus.java
package com.cherry.ajax.database;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import com.cherry.ajax.database.service.WorkloadLocalServiceUtil;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.util.bridges.mvc.MVCPortlet;
/**
* Portlet implementation class DrStatus
*/
public class DrStatus extends MVCPortlet {
String action = "";
#Override
public void processAction(ActionRequest request, ActionResponse response)
throws IOException, PortletException {
action = request.getParameter("drAction");
try {
WorkloadLocalServiceUtil.check();
} catch (SystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void doView(RenderRequest request, RenderResponse response)
throws IOException, PortletException {
response.setContentType("text/html");
PortletRequestDispatcher dispatcher = null;
if (action.equals("getWorkData")) {
dispatcher = getPortletContext().getRequestDispatcher(
"/html/drstatus/workloadData.jsp");
} else {
dispatcher = getPortletContext().getRequestDispatcher(
"/html/drstatus/view.jsp");
}
action = "";
dispatcher.include(request, response);
}
}
Please suggest me if I am doing any thing wrong ...
Thanks in advance
Samith K S

The problem is most likely that you have two DOM elements with the same ID (from both portlets). This is a common problem in portals: You never know whom you share the page with, thus you'll have to generate guaranteed-to-be-unique identifiers.
One way to do this is to use the output of <portlet:namespace/> - this is a standardized tag that generates a unique value per portlet. It's always identical within the same portlet. Then use this to generate your ID values:
<div id="<portlet:namespace />fetchData">
...
</div>
<script type="text/javascript">
var url = '<%=fetchDatabase.toString()%>';
$(document).ready(function() {
$("#fetchLink").click(function() {
$.post(url).done(function(data) {
$("#<portlet:namespace />fetchData").html(data);
});
});
});
Note: I've only used this on your example fetchData, not yet on fetchLink - that'll be your task now that you know what goes wrong :)
As you state in your comment, the same holds for variable names that end up as global variables in the DOM: Be aware that they all share the same HTML document in the end - one way to work around this is to use <portlet:namespace/> to "decorate" variable names as well, but the resulting code is ugly and hard to maintain. This is part of the reason why Liferay ended up with AlloyUI as replacement for jQuery: AUI defaults to namespacing and enables dynamic module loading. Within one namespace you didn't rely on global variables but have the variables local to that one block - including the modules that you wanted to load:
AUI().use('node', 'module2', 'module3', function (A) {
A.foo.bar()
// this variable is scoped to just this function
// no conflict with other content on the same page!
var someVariable = 'something';
doSomething();
});

As a rule, in Javascript, you should call your <aui:> components like
$('#<portlet:namespace/>aui_component_id')
If you view your html source, you'll see that all <aui> components have the 'portlet_id' as prefix in the 'id' attribute
Also, I can't understand hoe you make that work by using processAction, that is called from actionUrls. You need the 'resource' phase to fetch data without refreshing the page, so you'll need to override the MVCPortlet's serveResource function, and call it in your jsp's by calling resourceUrls

Thank you all of your resopnse.
I figure out what the problem is.
I am using same variable name for the both portlets urls (var url = '<%=workloadUrl.toString()%>'; && var url = '<%=fetchDatabase.toString()%>';).
I changed the variable name of the second portlet and now its working.
I didn't know I can't use same variable names in different portlets.

Related

struts2 onblur server side ajax validation

How would I implement AJAX validation in struts2 which lets you know if a username is available onblur? This is what I've managed so far but the docs aren't clear:
JSP:
<%# page
language="java"
contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# taglib
prefix="s"
uri="/struts-tags"%>
<%# taglib
prefix="sj"
uri="/struts-jquery-tags"%>
<!DOCTYPE html>
<html>
<head>
<sj:head jqueryui="true" />
<!-- Don't forget these 2 files!! -->
<script
src="${pageContext.request.contextPath}/struts/utils.js"
type="text/javascript"></script>
<script
src="${pageContext.request.contextPath}/struts/css_xhtml/validation.js"
type="text/javascript"></script>
<script
type="text/javascript"
src="js/register.js"></script>
<title>Register</title>
</head>
<s:url
action="loginLink"
var="loginHREF" />
<s:url
action="forgotPasswordLink"
var="forgotPasswordHREF" />
<s:url
action="Register"
var="registerHREF" />
<body>
<s:form
theme="css_xhtml"
action="Register"
onsubmit="return myValidate()">
<s:textfield
name="user.username"
label="Username"
id="uname"
required="true"
pattern="[a-zA-Z0-9_-]{5,12}"
title="5-12 Alphanumeric characters required" />
<s:textfield
name="user.email"
label="Email"
required="true" />
<s:password
id="pass1"
name="user.password"
label="Password"
required="true"
pattern="{5,12}"
title="5-12 characters" />
<s:password
id="pass2"
label="Confirm Password"
required="true"
pattern="{5,12}" />
<s:textfield
name="user.firstName"
label="First Name"
required="true" />
<s:textfield
name="user.lastName"
label="Last Name" />
<sj:datepicker
yearRange='-90:-3'
changeYear="true"
name="user.birthDate"
readonly="true"
label="Date of Birth" />
<s:submit value="Register" />
</s:form>
<s:a href="%{loginHREF}">Login</s:a>
<s:a href="%{forgotPasswordHREF}">Forgot Password</s:a>
<script>
$("#uname").blur(function() {
var fieldData = $("#uname").serialize();
fieldData = fieldData.slice(5);
//do the POST thingies
$.ajax({
type : "POST",
url : "ValidateUsername",
cache : false,
data : fieldData,
dataType : "json",
complete : function(request) {
alert("In complete");
var form = $('form');
//clear previous validation errors, if any
//StrutsUtils.clearValidationErrors(form);
//get errors from response
var text = request.responseText;
alert(text);
var errorsObject = StrutsUtils.getValidationErrors(text);
//show errors, if any
if (errorsObject.fieldErrors) {
StrutsUtils.showValidationErrors(form, errorsObject);
}
}
});
return false;
});
</script>
</body>
</html>
Action:
#Action("ValidateUsername")
#Results(value = { #Result(name = "success", type = "json"), #Result(name = "input", type = "json") })
#ParentPackage("default")
#InterceptorRef("jsonValidationWorkflowStack")
public class ValidateUsername extends ActionSupport {
private String username;
#Override
public String execute() {
return SUCCESS;
}
#Override
public void validate() {
UserService service = new UserService();
User isTaken = service.findByUsername(username);
service.close();
if (isTaken != null)
addFieldError("user.username", "That username is taken");
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
The docs make it seem like struts2 will handle it for you if you simply add the json interceptor and set validate="true" in the sx:submit tag.
In register.js I check to see that password and confirmation password match.
Basically I would like it to validate even before submit is pressed. Right now On submit it validates it like any other normal form.
Edit: Also, is there a working method other than using dojo? It seems to block all my client side validation including the required attributes, js, etc.
Edit2:I ditched the dojo plugin, learnt some js and managed to put together what i have now. Now the only thing left is to display an error. Alerting the response text shows up a json string with only a username key value pair in it. Should i add another member to the action and process it to display an error message? because the StrutsUtils js is not working. addFieldError("user.username", "That username is taken"); does not modify the json result.
Basically, how can i add fielderrors to my json response?
Edit: I got my action to add my field errors to the json response by modifying the annotations as follows:
#Action("ValidateUsername")
#Results(value = {
#Result(name = "success", type = "json"),
#Result(name = "input", type = "json", params = { "ignoreHierarchy",
"false", "includeProperties",
"actionErrors.*,actionMessages.*,fieldErrors.*" }) })
#ParentPackage("default")
#InterceptorRef("jsonValidationWorkflowStack")
Now the only problem is in:
//clear previous validation errors, if any
**//StrutsUtils.clearValidationErrors(form);**
//get errors from response
var text = request.responseText;
alert(text);
**var errorsObject = StrutsUtils.getValidationErrors(text);**
//show errors, if any
if (errorsObject.fieldErrors) {
StrutsUtils.showValidationErrors(form, errorsObject);
}
None of the StrutsUtils functions are working. When i uncomment clearValidationErrors it does nothing and breaks the rest of the js code. errorsObject is empty and showValidation does nothing even though on printing my jon response i can see a field error with a message. How do i get StrutsUtils to work?
Also, is there a better way to implement my use case? I haven never learnt jquery/ajax and this is my first struts2 application.
Struts is available server-side. You are trying to call
var errorsObject = StrutsUtils.getValidationErrors(text); in the client-side.
StrutsUtils is a class and is not available on client-side/javascript.
There are various ways through which you can send errors.
Please refer my answer in the below link, on how to handle ajax request & response when using struts2.
How to get Ajax response in Struts2

How to call specific method of portlet.java class rather then overide serveResource method?

I want some help in liferay with ajax.
Right now I am calling the ajax method from my view.jsp page to submit some data.
Here is sample code I am using in view.jsp:
<%# include file="/init.jsp"%>
<portlet:actionURL name="AddTest" var="add1" />
<portlet:resourceURL id="AddTest" var="AddTest"></portlet:resourceURL>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript">
function addToDo(addToDo){
var todo =document.getElementById('toDo').value;
$.ajax({
url :addToDo,
data: {"todo":todo,"CMD":"addToDo"},
type: "GET",
dataType: "text",
success: function(data) {
$("#toDoList").html(data);
}
});
}
</script>
</head>
<body>
<portlet:resourceURL var="addToDo" id="addToDo"></portlet:resourceURL>
<form>
<input type="text" name="toDo" id="toDo">
<button name="Add" type="button" onclick="addToDo('<%=addToDo%>')">Add</button>
<div id="toDoList">
</div>
</form>
</body>
</html>
and in my portlet.java class there is one method which is called by this ajax call:
#Override
public void serveResource(ResourceRequest request, ResourceResponse response){
if(request.getParameter("CMD").equals("addToDo")) {
System.out.println("came here for add");
mediatype userToDo = new mediatypeImpl();
//userToDo.setMediaId(12345);
try {
userToDo.setPrimaryKey((CounterLocalServiceUtil.increment()));
userToDo.setMedianame(request.getParameter("todo"));
mediatypeLocalServiceUtil.addmediatype(userToDo);
}
catch (SystemException e) {
e.printStackTrace();
}
}
}
So my question is that right now it is just caling by default #override method from any ajax class.
But how can I call specific method of portlet.java class on ajax call?
I am new bee in ajax. So please guide me anyways u can.....
I got following error when calling ajax with url of following
<portlet:actionURL name="ajax_AddAdvertise" var="addToDo" windowState="<%= LiferayWindowState.EXCLUSIVE.toString()%>"> </portlet:actionURL>
06:47:03,705 ERROR [http-bio-8080-exec-23][render_portlet_jsp:154] java.lang.NoSuchMethodException: emenu.advertise.portlet.RestaurantPortlet.ajax_AddAdvertise(javax.portlet.ActionRequest, javax.portlet.ActionResponse)
at java.lang.Class.getMethod(Class.java:1605)
my process action method as follows
#ProcessAction(name = "ajax_AddAdvertise")
public void ajax_AddAdvertise(ResourceRequest request,ResourceResponse response) {
}
how can I call specific method of portlet.java class on ajax call?
I think we can't have two different versions of serveResource methods like we do for action methods atleast not with the default implementation.
If you want different methods you would have to go the Spring MVC (#ResourceMapping) way to have that.
Still, you can define different logic in your serveResource method using the resourceId as follows (a full example):
In the JSP:
<portlet:resourceURL var="myResourceURL" id="myResourceID01" />
In the portlet class the serveResource method will contain the following code:
String resourceID = request.getResourceID();
if(resoureID.equals("myResourceID01")) {
// do myResourceID01 specific logic
} else {
// else do whatever you want
}
Please keep in mind [Important]
In a portlet you should not use <html>, <head>, <body> tags since portlets generate fragment of the page and not the whole page. Even if it is allowed your resultant page will not be well-formed and will behave differently on different browsers. And moreover the javascript which modifies DOM element will be totally useless.
Edit after this comment:
You can also use ajax with action methods:
People use <portlet:actionURL> with ajax generally for <form>-POST.
For this the actionURL is generated in a slightly different way in your jsp like this:
<portlet:actionURL name="ajax_AddAdvertise" var="addToDo" windowState="<%= LiferayWindowState.EXCLUSIVE.toString()%>">
</portlet:actionURL>
And in your portlet you can have (as in the question):
#ProcessAction(name = "ajax_AddAdvertise")
public void ajax_AddAdvertise(ActionRequest request, ActionResponse response) {
// ... your code
}

Servlet to jsp communication best practice

I'm learning how to write java servlets and jsp pages on google app engine. I'm attempting to use an MVC model but I'm not sure if I'm doing it right. Currently, I have a servlet that is called when a page is accessed. The servlet does all the processing and creates a HomePageViewModel object that is forwarded to the jsp like this:
// Do processing here
// ...
HomePageViewModel viewModel = new HomePageViewModel();
req.setAttribute("viewModel", viewModel);
//Servlet JSP communication
RequestDispatcher reqDispatcher = getServletConfig().getServletContext().getRequestDispatcher("/jsp/home.jsp");
reqDispatcher.forward(req, resp);
Over on the jsp side, I have something like this:
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# page import="viewmodels.HomePageViewModel" %>
<%
HomePageViewModel viewModel = (HomePageViewModel) request.getAttribute("viewModel");
pageContext.setAttribute("viewModel", viewModel);
%>
<html>
<body>
<% out.println(((HomePageViewModel)pageContext.getAttribute("viewModel")).Test); %>
</body>
</html>
So my question is two fold. First, is this a reasonable way to do things for a small webapp? This is just a small project for a class I'm taking. And second, in the jsp file, is there a better way to access the viewmodel data?
If you adhere the Javabeans spec (i.e. use private properties with public getters/setters),
public class HomePageViewModel {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
then you can just use EL (Expression Language) to access the data.
<%# page pageEncoding="UTF-8" %>
<html>
<body>
${viewModel.test}
</body>
</html>
See also:
Our Servlets wiki page
Our JSP wiki page
Our EL wiki page
How to avoid Java code in JSP files?

request.getParameter() returns null

Got a homework assignment that is giving me problems.... Its modifying a JSF project with two pages and a bean to fit MVC2 by adding two more pages and a controller servlet and another bean for the two additional pages. the new main page forwards to either the second new page or the old first page. My issue is response.getParameter() always results in null.
<%#page session="false" import="java.util.Iterator"%>
<%#taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%#taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<jsp:useBean id="status" scope="request" class="JSFRegistration.Status" />
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>JSP Page</title>
</head>
<body>
<% if (status!=null && !status.isSuccessful()){%>
<font color="red">Processing errors:
<ul><%Iterator errors=status.getExceptions();
while (errors.hasNext()){
Exception e = (Exception) errors.next();%>
<li><%= e.getMessage()%><%}%></ul></font><%}%>
<form action="LoginServlet" method="POST">
<% String username = request.getParameter("username");
if (username==null) username="";%>
<input type="text" name="usernameTF" value="<%=username%>" />
<% String password = request.getParameter("password");
if (password==null) password="";%>
<input type="password" name="passwordTF" value="<%=password%>" />
<input type="submit" value="Login" />
</form>
</body>
</html>
this is basically a direct copy from our book but the fields I need for the new main page. Same for the controller servlet, a direct copy except only contains the fields I need.
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher view = null;
Status status = new Status();
request.setAttribute("status", status);
String username = request.getParameter("username");
String password = request.getParameter("password");
if (username==null || username.length()==0)
status.addException(new Exception("Please enter username"));
if (password==null)
status.addException(new Exception("Please enter password"));
if (!status.isSuccessful()){
view = request.getRequestDispatcher("Login.jsp");
//view.forward(request, response);
}
else
try{
request.setAttribute("username", username);
request.setAttribute("password", password);
view = request.getRequestDispatcher("Registration.jsp");
} catch (Exception e) {
status.addException(new Exception("Error"));
view = request.getRequestDispatcher("Login.jsp");
}
view.forward(request, response);
}
and the Status class, again a direct copy from the book.
public class Status {
private ArrayList exceptions;
public Status(){
exceptions = new ArrayList();
}
public void addException(Exception exception) {
exceptions.add(exception);
}
public boolean isSuccessful(){
return (exceptions.isEmpty());
}
public Iterator getExceptions(){
return exceptions.iterator();
}
regardless of what is typed into the two boxes, stepping through a debug shows the values not getting passed to the parameters. I get the created exceptions printed above the screen for both fields if both have text, if only one has text and when both are empty.
Your request parameter names do not match the input field names. You've assigned the input fields a name of usernameTF and passwordTF. They are then available by exactly those names as request parameter, but you're attempting to get them using the names username and password. So you need either to fix the input field names, or the request parameter names so that they match each other.
By the way, why falling back from a modern MVC framework like JSF to awkward 90's style JSP with mingled business code? Is that really what the homework assignment is asking you? Also the HTML <font> element is deprecated since 1998. Where did you learn about it? Is the quality of the course really good?

Component for "priority table" in JSF?

In a certain page of our JSF application, the user sees a table listing many objects, which we will call "jobs". Let's say each job has a priority, which is nothing but a number, and in this screen the user is able to edit the priorities of the jobs.
However, two jobs can't have the same priority number. For this reason, I'm finding it hard to build an appropriate UI to deal with setting the priorities.
We tried a simple editbox in the beginning, but it soon became clear that it sucked: if the user wanted to lower the priority of a job from 100 to 50, he would have to manually "make room for that job" by adding 1 to jobs 50 to 99.
Now I'm thinking about a table in which the user could drag & drop the rows to visually adjust priority, without ever having to fiddle with priority numbers (in fact never seeing them), but I can't find such component. Does anybody know of such component or have any better UI suggestion?
I don't think you will find a component that will change the priority numbering for you. Instead you should invoke a re-ordering routine that would update the priority numbers accordingly after changing the priority.
Regarding drag-and-drop, I'll suggest that you look at RichFaces or ICEFaces. RichFaces got some very neat functionality for implementing the sort of thing you are talking about. I'll recommend that you have a look at the RichFaces demo page and try out the drag-drop support components. The RichFaces component set also got an Ordering List (under Rich Selects), but it doesn't seem to allow for changing the ordering by entering a number, rather it is done using up and down buttons.
How about take your current idea and let the computer do the job of making room?
If the user enters 50, display a warning that that job exists, ask if they want the new job inserted before or after the current number 50 or if they want to cancel the operation entirely. If their choice is to insert the entry, you reorder the other items in code.
Check out the jQuery table drag'n'drop plugin. Then just tie the resulting javascript calls into your back-end using Ajax (eg. the a4j:jsFunction from Richfaces). Get the back-end Bean to handle the shuffling of the subsequent jobs.
This will definitely look and behave better than any out-of-the-box component that you'll currently find in a JSF library.
I thought I'd have a go and see if you could do this with the out-of-the box components and script.aculo.us. It is possible, though there would be a bit of work in getting it to look nice and provide a slick UI.
The demo view:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<jsp:directive.page language="java"
contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" />
<jsp:text>
<![CDATA[ <?xml version="1.0" encoding="ISO-8859-1" ?> ]]>
</jsp:text>
<jsp:text>
<![CDATA[ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ]]>
</jsp:text>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Sortable</title>
<script src="javascripts/prototype.js" type="text/javascript">/**/</script>
<script src="javascripts/scriptaculous.js" type="text/javascript">/**/</script>
</head>
<body>
<f:view>
<h:form>
<h:dataTable id="table1" binding="#{jobPageBean.table}"
value="#{jobBean.jobs}" var="row" border="1">
<h:column>
<f:facet name="header">
<h:outputText value="jobs" />
</f:facet>
<h:outputText value="#{row.text}" />
<h:inputHidden value="#{row.priority}">
<f:convertNumber integerOnly="true" />
</h:inputHidden>
</h:column>
</h:dataTable>
<h:commandButton id="ucb1" binding="#{jobPageBean.updateCommandButton}"
action="#{jobBean.updatePriorities}" value="Save New Priority Order"
disabled="true" />
</h:form>
<h:form>
<h:inputTextarea value="#{jobBean.newJob}" />
<h:commandButton action="#{jobBean.addNewJob}" value="Add Job" />
</h:form>
</f:view>
<script type="text/javascript"> /* <![CDATA[ */
Sortable.create('${jobPageBean.tableClientId}:tbody_element', {tag: 'tr', onChange: sortElements});
function sortElements() {
var table = document.getElementById('${jobPageBean.tableClientId}');
var inputs = table.getElementsByTagName('input');
for(var i=0; i<inputs.length; i++) {
inputs[i].value = i;
}
var updateCommandButton = document.getElementById('${jobPageBean.updateCommandButtonClientId}');
updateCommandButton.disabled = false;
}
/* ]]> */
</script>
</body>
</html>
</jsp:root>
Beans:
public class JobPageBean {
// Declaration:
// <managed-bean>
// <managed-bean-name>jobPageBean</managed-bean-name>
// <managed-bean-class>job.JobPageBean</managed-bean-class>
// <managed-bean-scope>request</managed-bean-scope>
// </managed-bean>
private UIComponent dataTable;
private UIComponent updateCommandButton;
public void setTable(UIComponent dataTable) {
this.dataTable = dataTable;
}
public UIComponent getTable() {
return dataTable;
}
public String getTableClientId() {
FacesContext context = FacesContext
.getCurrentInstance();
return dataTable.getClientId(context);
}
public void setUpdateCommandButton(
UIComponent updateCommandButton) {
this.updateCommandButton = updateCommandButton;
}
public UIComponent getUpdateCommandButton() {
return updateCommandButton;
}
public String getUpdateCommandButtonClientId() {
FacesContext context = FacesContext
.getCurrentInstance();
return updateCommandButton.getClientId(context);
}
}
public class JobBean {
// Declaration:
// <managed-bean>
// <managed-bean-name>jobBean</managed-bean-name>
// <managed-bean-class>job.JobBean</managed-bean-class>
// <managed-bean-scope>session</managed-bean-scope>
// </managed-bean>
private List<Job> jobs;
private DataModel model;
private String newJob;
public DataModel getJobs() {
if (jobs == null) {
jobs = new ArrayList<Job>();
model = new ListDataModel(jobs);
}
return model;
}
public String updatePriorities() {
if (jobs != null) {
Collections.sort(jobs);
}
return null;
}
public String getNewJob() {
return newJob;
}
public void setNewJob(String newJob) {
this.newJob = newJob;
}
public String addNewJob() {
if (newJob == null || newJob.trim().length() == 0) {
return null;
}
Job job = new Job();
job.setText(newJob);
job.setPriority(jobs.size());
jobs.add(job);
newJob = null;
return null;
}
}
public class Job implements Comparable<Job> {
private String text;
private int priority;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public int compareTo(Job other) {
if (other.priority == priority) {
return 0;
}
return other.priority < priority ? 1 : -1;
}
}
You can reorder the rows by dragging them, but you need to submit the form to save the changes - you won't get good AJAX support without a 3rd party framework that adds it (at least, not until JSF 2.0)
I used MyFaces 1.2.3, the dataTable of which renders its tbody element with an id attribute ending in :tbody_element - I don't know if all implementations do this
I haven't bothered with too much in the way of input validation, or looked at using the keyboard for accessibility support, or all those other things required for a professional UI...

Resources