Here my simple JSF application to show JavaScript API bug with jsf.util.chain method.
Using:
Here the view:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Test JSF 2.0 Javascript API</title>
<script type="text/javascript">
function returnTrue(){
debug('return true');
return true;
}
function returnFalse(){
debug('return false');
return false;
}
function debug(obj) {
if (console) {
if(console.debug){
//for firebug
//http://getfirebug.com/wiki/index.php/Console_API
console.debug(obj);
}
}
else if (console) {
if(console.log){
//for IE
//http://msdn.microsoft.com/en-us/library/dd565625(v=vs.85).aspx
console.log(obj);
}
}
}
</script>
</h:head>
<h:body>
<h:form>
<br />
<br />
<h1 align="center">Test jsf.util.chain</h1>
<br />
<table align="center">
<tbody>
<tr>
<td>Combo A</td>
<td><h:selectOneMenu id="comboa" value="#{comboTest1.combo1Value}" onchange="returnTrue()">
<f:selectItems value="#{comboTest1.values1}" />
<f:ajax listener="#{comboTest1.changeCombo1}" render="combob" />
</h:selectOneMenu></td>
</tr>
<tr>
<td><label>Combo B</label></td>
<td><h:selectOneMenu id="combob" value="#{comboTest1.combo2Value}" title="Depend of combo 1" onchange="returnFalse()">
<f:selectItems value="#{comboTest1.values2}" />
<f:ajax listener="#{comboTest1.changeCombo2}" render="@this labela" />
</h:selectOneMenu></td>
</tr>
<tr>
<td>Label A</td>
<td><h:outputLabel id="labela" value="#{comboTest1.labelValue}" title="Depend of combo 2" /></td>
</tr>
</tbody>
</table>
<br />
<br />
<br />
</h:form>
</h:body>
</f:view>
Here the managed bean:
package com.ms.test.jsf.bug;
/**
*
*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.model.SelectItem;
/**
* @author soaint
*
*/
@ManagedBean(name = "comboTest1")
@ViewScoped
public class ComboTest1 implements Serializable {
private Map<String, List<String>> combinado = null;
private Object combo1Value = null;
private Object combo2Value = null;
private Object labelValue = null;
/**
*
*/
private static final long serialVersionUID = -4737479400003511726L;
public ComboTest1() {
System.out.println("CREATE ComboTest1");
combinado = new HashMap<String, List<String>>();
List<String> list = null;
for (int i = 0; i < 10; i++) {
list = new ArrayList<String>();
for (int k = 0; k < 10; k++) {
list.add(i + " - " + k);
}
combinado.put(i + "", list);
}
}
public void changeCombo1(AjaxBehaviorEvent abe) {
System.out.println("CHANGE MENU A === " + combo1Value);
}
public void changeCombo2(AjaxBehaviorEvent abe) {
System.out.println("CHANGE MENU B === " + combo2Value);
labelValue = combo2Value;
}
public List<SelectItem> getValues1() {
List<SelectItem> list = new ArrayList<SelectItem>();
Set<String> keys = combinado.keySet();
List<String> listi = new ArrayList<String>(keys);
Collections.sort(listi);
for (Object object : listi) {
list.add(new SelectItem(object, " -- " + object + " -- "));
}
list.add(0, new SelectItem(null, " ------- "));
return list;
}
public List<SelectItem> getValues2() {
List<SelectItem> list = new ArrayList<SelectItem>();
List<? extends Object> keys = combinado.get(combo1Value);
if (keys != null) {
for (Object object : keys) {
list.add(new SelectItem(object, " -- " + object + " -- "));
}
}
return list;
}
public Object getCombo1Value() {
return combo1Value;
}
public void setCombo1Value(Object combo1Value) {
this.combo1Value = combo1Value;
}
public Object getCombo2Value() {
return combo2Value;
}
public void setCombo2Value(Object combo2Value) {
this.combo2Value = combo2Value;
}
public Object getLabelValue() {
return labelValue;
}
}
Is simple, the view have two selecOne, the first select have in onchange event a call to returnTrue and second select event call to returFalse. Both have ajax call to managedbean attached to change (default). Internally JSF join the custom onchange attribute and ajax mojarra call into jsf.util.chain call:
<select id="j_idt5:comboa" name="j_idt5:comboa" size="1" onchange="jsf.util.chain(this,event,'returnTrue()','mojarra.ab(this,event,\'valueChange\',0,\'j_idt5:combob\')')">
...
<select id="j_idt5:combob" name="j_idt5:combob" size="1" title="Depend of combo 1" onchange="jsf.util.chain(this,event,'returnFalse()','mojarra.ab(this,event,\'valueChange\',0,\'@this j_idt5:labela\')')">
...
The especification of jsf.util.chain on oracle site advertisement:
<static> jsf.util.chain(source, event)
A varargs function that invokes an arbitrary number of scripts. If any script in the chain returns false, the chain is short-circuited and subsequent scripts are not invoked. Any number of scripts may specified after the event argument.
This is not true, the second select have call to selectFalse but this fire the ajax.
Cause:
I have my JSF configured in Development mode in web.xml context-param and can debug the jsf.js uncompressed version. On my JSF version used the jsf.util.chain is declared on line 2247. I insert breakpoint on line 2260 on code:
var returnValue = f.call(thisArg, event);
Full method:
jsf.util.chain = function(source, event) {
if (arguments.length < 3) {
return true;
}
// RELEASE_PENDING rogerk - shouldn't this be getElementById instead of null
var thisArg = (typeof source === 'object') ? source : null;
// Call back any scripts that were passed in
for (var i = 2; i < arguments.length; i++) {
var f = new Function("event", arguments[i]);
var returnValue = f.call(thisArg, event);
if (returnValue === false) {
return false;
}
}
return true;
};
after the first method call (returnTrue or returnFalse) the returnValue have always undefined value.
Full code of my test here
Download, unzip, open console to unzip path and run
mvn clean jetty:run
Open web browser at http://localhost:8080/testBugJSF/
My questions are: Did this was a valid implementation and core JavaScript suffered changes that affected the results of this implementation? As to whom to report this to be corrected? What is JSF report path? As I can correct this problem directly in my application?
each method must have return.
<tr>
<td>Combo A</td>
<td><h:selectOneMenu id="comboa" value="#{comboTest1.combo1Value}" onchange="return returnTrue()">
<f:selectItems value="#{comboTest1.values1}" />
<f:ajax listener="#{comboTest1.changeCombo1}" render="combob" />
</h:selectOneMenu></td>
</tr>