Details
-
Type: Improvement
-
Status: Closed
-
Priority: Major
-
Resolution: Fixed
-
Affects Version/s: 1.6
-
Component/s: ICE-Components
-
Labels:None
-
Environment:All
-
Support Case References:
Description
I'm proposing a little improvement for the MenuRenderer of ICEFaces, which it's
very important for our application.
We need using complex objects associated to a UISelectItem.
Usually, the value of an UISelectItem is a simple object (String, Integer,etc);
using a complex object is possible if there is a converter associated to the
UISelectOne component, which generates a "key" which identifies the complex
object in the getAsString() method of the Converter. In fact, in the generated
HTML page, the complex object has to be represented as a simple string in the
VALUE attribute of OPTION element.
But there's a problem during the rendering of the UISelectOne component, in fact
no option is selected, even if the model of the managed-bean is set to a value
which should match one of values contained in the list of the UISelectItems
component.
This happens when the current model and the matching element contained by the
UISelectItems component have the same "key", but they are two different
instances. The "key" is the string returned by the getAsString() method of the
associated Converter.
The class com.icesoft.faces.renderkit.dom_html_basic.MenuRenderer is responsible
of writing the SELECTED attribute for the OPTION element which corresponds to
the model actually selected.
Looking at the
com.icesoft.faces.renderkit.dom_html_basic.MenuRenderer.renderOption(FacesContext,
UIComponent, SelectItem, Element) method, when the submittedValue is not null,
the following code is executed:
Object selectedValues = getCurrentSelectedValues(uiComponent);
isSelected = isSelected(selectItem.getValue(), selectedValues);
The logic which determines if an option is selected does not use converted
values (it is not based on string comparison, but on generic Object comparison),
but the value obtained by the UIComponent is used.
I send you a patched version of the MenuRenderer class (version 1.5.3 of
ICEFaces, but the situation is identical in 1.6.0), which always uses the
converted value for calculating the selected item.
In fact, the formatComponentValue() method is called not only for the
submittedValue, but also for the value of SelectItem and UISelectOne: they are
treated in the same way.
If you don't like this solution and you prefer making comparison between the raw
value of components, I ask you if in the next releases it's possible to isolate
the code which contains the logic of determining the selected item, putting it
in a protected method; so I can easily change the logic in a sub-class of the
MenuRenderer.
For example, the
com.icesoft.faces.renderkit.dom_html_basic.MenuRenderer.renderOption(FacesContext,
UIComponent, SelectItem, Element) method can be modified as in the following
code, adding the new isValueSelected method, which is protected, but your
original logic is completely unchanged:
protected void renderOption(FacesContext facesContext,
UIComponent uiComponent,
SelectItem selectItem, Element optionGroup)
throws IOException {
DOMContext domContext =
DOMContext.attachDOMContext(facesContext, uiComponent);
Element select = (Element) domContext.getRootNode();
Element option = domContext.createElement("option");
if (optionGroup == null) {
select.appendChild(option);
} else {
optionGroup.appendChild(option);
}
String valueString = formatComponentValue(facesContext, uiComponent,
selectItem.getValue());
option.setAttribute("value", valueString);
/* Now the logic is here!!!! */
boolean isSelected = isValueSelected(facesContext,selectItem,uiComponent);
if (isSelected) {
option.setAttribute("selected", "selected");
}
if (selectItem.isDisabled()) {
option.setAttribute("disabled", "disabled");
}
Document doc = domContext.getDocument();
Text labelNode = doc.createTextNode(selectItem.getLabel());
option.appendChild(labelNode);
}
protected boolean isValueSelected(FacesContext facesContext, SelectItem
selectItem,UIComponent uiComponent){
boolean isSelected;
String valueString = formatComponentValue(facesContext, uiComponent,
selectItem.getValue());
Object submittedValues[] =
getSubmittedSelectedValues(uiComponent);
if (submittedValues != null) {
isSelected = isSelected(valueString, submittedValues);
} else {
Object selectedValues =
getCurrentSelectedValues(uiComponent);
isSelected = isSelected(selectItem.getValue(), selectedValues);
}
return isSelected;
}
very important for our application.
We need using complex objects associated to a UISelectItem.
Usually, the value of an UISelectItem is a simple object (String, Integer,etc);
using a complex object is possible if there is a converter associated to the
UISelectOne component, which generates a "key" which identifies the complex
object in the getAsString() method of the Converter. In fact, in the generated
HTML page, the complex object has to be represented as a simple string in the
VALUE attribute of OPTION element.
But there's a problem during the rendering of the UISelectOne component, in fact
no option is selected, even if the model of the managed-bean is set to a value
which should match one of values contained in the list of the UISelectItems
component.
This happens when the current model and the matching element contained by the
UISelectItems component have the same "key", but they are two different
instances. The "key" is the string returned by the getAsString() method of the
associated Converter.
The class com.icesoft.faces.renderkit.dom_html_basic.MenuRenderer is responsible
of writing the SELECTED attribute for the OPTION element which corresponds to
the model actually selected.
Looking at the
com.icesoft.faces.renderkit.dom_html_basic.MenuRenderer.renderOption(FacesContext,
UIComponent, SelectItem, Element) method, when the submittedValue is not null,
the following code is executed:
Object selectedValues = getCurrentSelectedValues(uiComponent);
isSelected = isSelected(selectItem.getValue(), selectedValues);
The logic which determines if an option is selected does not use converted
values (it is not based on string comparison, but on generic Object comparison),
but the value obtained by the UIComponent is used.
I send you a patched version of the MenuRenderer class (version 1.5.3 of
ICEFaces, but the situation is identical in 1.6.0), which always uses the
converted value for calculating the selected item.
In fact, the formatComponentValue() method is called not only for the
submittedValue, but also for the value of SelectItem and UISelectOne: they are
treated in the same way.
If you don't like this solution and you prefer making comparison between the raw
value of components, I ask you if in the next releases it's possible to isolate
the code which contains the logic of determining the selected item, putting it
in a protected method; so I can easily change the logic in a sub-class of the
MenuRenderer.
For example, the
com.icesoft.faces.renderkit.dom_html_basic.MenuRenderer.renderOption(FacesContext,
UIComponent, SelectItem, Element) method can be modified as in the following
code, adding the new isValueSelected method, which is protected, but your
original logic is completely unchanged:
protected void renderOption(FacesContext facesContext,
UIComponent uiComponent,
SelectItem selectItem, Element optionGroup)
throws IOException {
DOMContext domContext =
DOMContext.attachDOMContext(facesContext, uiComponent);
Element select = (Element) domContext.getRootNode();
Element option = domContext.createElement("option");
if (optionGroup == null) {
select.appendChild(option);
} else {
optionGroup.appendChild(option);
}
String valueString = formatComponentValue(facesContext, uiComponent,
selectItem.getValue());
option.setAttribute("value", valueString);
/* Now the logic is here!!!! */
boolean isSelected = isValueSelected(facesContext,selectItem,uiComponent);
if (isSelected) {
option.setAttribute("selected", "selected");
}
if (selectItem.isDisabled()) {
option.setAttribute("disabled", "disabled");
}
Document doc = domContext.getDocument();
Text labelNode = doc.createTextNode(selectItem.getLabel());
option.appendChild(labelNode);
}
protected boolean isValueSelected(FacesContext facesContext, SelectItem
selectItem,UIComponent uiComponent){
boolean isSelected;
String valueString = formatComponentValue(facesContext, uiComponent,
selectItem.getValue());
Object submittedValues[] =
getSubmittedSelectedValues(uiComponent);
if (submittedValues != null) {
isSelected = isSelected(valueString, submittedValues);
} else {
Object selectedValues =
getCurrentSelectedValues(uiComponent);
isSelected = isSelected(selectItem.getValue(), selectedValues);
}
return isSelected;
}
Activity
Tyler Johnson
created issue -
Ken Fyten
made changes -
Field | Original Value | New Value |
---|---|---|
Fix Version/s | 1.7DR#1 [ 10100 ] | |
Assignee | Mark Collette [ mark.collette ] |
Ken Fyten
made changes -
Fix Version/s | 1.7 [ 10080 ] | |
Fix Version/s | 1.7DR#1 [ 10100 ] |
Philip Breau
made changes -
Support Case References | https://www.icesoft.ca:4443/supportilla/show_bug.cgi?id=4239 |
Philip Breau
made changes -
ICEfaces Forum Reference | https://www.icesoft.ca:4443/supportilla/show_bug.cgi?id=4239 |
Ken Fyten
made changes -
Assignee | Mark Collette [ mark.collette ] | Yip Ng [ yip.ng ] |
Ken Fyten
made changes -
Fix Version/s | 1.7DR#3 [ 10112 ] | |
Fix Version/s | 1.7 [ 10080 ] | |
Assignee Priority | P1 |
Repository | Revision | Date | User | Message |
ICEsoft Public SVN Repository | #15237 | Thu Nov 22 16:39:40 MST 2007 | yip.ng | Changed to allow more flexibility in the conversion and matching of values between the SelectOne and SelectMany components and their SelectItem's |
Files Changed | ||||
MODIFY
/icefaces/trunk/icefaces/core/src/com/icesoft/faces/renderkit/dom_html_basic/SelectManyCheckboxListRenderer.java
MODIFY /icefaces/trunk/icefaces/core/src/com/icesoft/faces/renderkit/dom_html_basic/RadioRenderer.java MODIFY /icefaces/trunk/icefaces/core/src/com/icesoft/faces/renderkit/dom_html_basic/MenuRenderer.java |
yip.ng
made changes -
Attachment | Car.java [ 10745 ] | |
Attachment | CarConverter.java [ 10746 ] | |
Attachment | SelectionTagsBean.java [ 10747 ] |
yip.ng
made changes -
Status | Open [ 1 ] | Resolved [ 5 ] |
Resolution | Fixed [ 1 ] |
Ken Fyten
made changes -
Fix Version/s | 1.7 [ 10080 ] |
Ken Fyten
made changes -
Status | Resolved [ 5 ] | Closed [ 6 ] |
Assignee Priority | P1 | |
Assignee | Yip Ng [ yip.ng ] |
That's a very good explanation. I have the same problem. My Problem is the following:
<ice:selectOneMenu value="$
{myBean.prop123}">
<f:selectItem itemValue="123" itemLabel="foo" />
</ice:selectOneMenu>
the property "prop123" is from type "MyClass" and implements the "equal" method. Now the item will never be selected because you always convert the value of the selectItem to an String and execute the equals method of the string. In the case to create via java code the select items with value of type "MyClass" to force the icefaces implementation to call the equals method of the MyClass Object is also wrong, because the formatComponentValue convert it to an String.
If I can override the selection code I will be happy or you change the code to executes the equals from the value binding object.