[
https://issues.apache.org/jira/browse/TOMAHAWK-1579?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Leonardo Uribe resolved TOMAHAWK-1579.
--------------------------------------
Resolution: Duplicate
Assignee: Leonardo Uribe
> Wrong source parameter for JSF AJAX onchange event code since there is no DOM
> element for HtmlSelectOneRadio
> --------------------------------------------------------------------------------------------------------------
>
> Key: TOMAHAWK-1579
> URL: https://issues.apache.org/jira/browse/TOMAHAWK-1579
> Project: MyFaces Tomahawk
> Issue Type: Bug
> Components: selectOneRadio / radio
> Affects Versions: 1.1.10
> Environment: Tomahawk 1.1.10 for JSF 2, Mojarra 2.1
> Reporter: Lutz Ulruch
> Assignee: Leonardo Uribe
>
> Hello,
> I'm using Tomahawk's HtmlSelectOneRadio (of Java package
> org.apache.myfaces.component.html.ext) with 'spread' layout
> and an attached AjaxBehavior for the default event ('valueChange').
> The script code rendered for the event does not work. No AJAX request is
> submitted.
>
> In
> org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRadioRendererBase.renderRadio(FacesContext,UIInput,String,boolean,boolean,boolean,Integer)
> the passed UIInput component is the HtmlSelectOneRadio.
> HtmlRenderUtils.renderBehaviorizedxxx() is used to render the event handler
> code.
> The generated JavaScript code passes the clientId of the HtmlSelectOneRadio
> as the value of the JS function parameter 'source', for example:
> <input type="radio"
> onchange="mojarra.ab('j_id13:j_id51',event,'valueChange','@form',0,{'com.os.bellevue.faces.ajax.single':'true'})"
> value="0" name="j_id13:j_id51" id="j_id13:j_id51:0">
> where 'j_id13:j_id51' is the clientId of HtmlSelectOneRadio. But, of course,
> there is no HTML DOM element with that ID.
> The renderer renders HTML elements for the HtmlRadio components attached to
> HtmlSelectOneRadio, but not for the HtmlSelectOneRadio.
> Now, when a radio button is selected and the value changes, the JSF 2.0
> JavaScript stuff cannot find an element whose ID matches the clientId of
> HtmlSelectOneRadio. As a result, no AJAX request is submitted.
> I fixed that bug locally by re-implementing
> renderRadio(FacesContext,UIInput,String,boolean,boolean,boolean,Integer) in a
> way so the
> HtmlSelectOneRadio's clientId is replaced by 'this' (the this-reference to
> the <input type="radio">.
> Of course, I just tested that fix in my application, where I do not use any
> other events, but 'valueChange'.
> Also, the fix is probably not optimal in terms of performance and it is an
> ugly workaround:
> protected String renderRadio(FacesContext facesContext,
> UIInput uiComponent,
> String value,
> boolean disabled,
> boolean checked,
> boolean renderId,
> Integer itemNum) throws IOException
> {
> String clientId = uiComponent.getClientId(facesContext);
> String itemId = (itemNum == null)? null : clientId +
> UINamingContainer.getSeparatorChar(facesContext) + itemNum;
> ResponseWriter writer = facesContext.getResponseWriter();
> writer.startElement(HTML.INPUT_ELEM, uiComponent);
> if (itemId != null)
> {
> writer.writeAttribute(HTML.ID_ATTR, itemId, null);
> }
> else if (renderId) {
> writer.writeAttribute(HTML.ID_ATTR, clientId, null);
> }
> writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_RADIO, null);
> writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
> if (disabled) {
> writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR,
> null);
> }
> if (checked)
> {
> writer.writeAttribute(HTML.CHECKED_ATTR, HTML.CHECKED_ATTR, null);
> }
> if (value != null)
> {
> writer.writeAttribute(HTML.VALUE_ATTR, value, null);
> }
>
> Map<String, List<ClientBehavior>> behaviors = null;
> if (uiComponent instanceof ClientBehaviorHolder &&
> JavascriptUtils.isJavascriptAllowed(facesContext.getExternalContext()))
> {
> behaviors = ((ClientBehaviorHolder)
> uiComponent).getClientBehaviors();
> // L. Ulrich:
> // original code:
>
> //HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(facesContext,
> writer, uiComponent, behaviors);
> //HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext,
> writer, uiComponent, behaviors);
>
> //HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(facesContext,
> writer, uiComponent, behaviors);
> //HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
> HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE_AND_EVENTS);
>
> // end of original code
> //
> // replaced by the following code.
> // It replaces the client ID in the script code rendered for
> ClientBehaviors.
> // Note that the script code rendered by
> // HtmlRendererUtils.renderBehaviorizedxxx()
> // uses the clientId of the passed uiComponent (which is
> // a HtmlSelectOneRadio, not a HtmlRadio) as value for the
> // source parameter of AJAX scripts
> // (like for jsf.ajax.request(source, event, options) ).
> // But since no HTML element is rendered for HtmlSelectOneRadio,
> // the HTML DOM does not contain an element with that ID.
> // As a result, script code fails to find an element with that ID
> // (JavaScript: Document.getElementById(source)) )
> // which in turn results in no AJAX request being send.
> //
> // I fixed that problem by using a temporary ResponseWriter
> // to write the event attributes.
> // After the attributes have been written,
> // the generated HTML fragment is parsed and
> // the HtmlSelectOneRadio's clientId is replaced by 'this',
> // that is: the DOM element of the HTML <input type="radio">.
> //
> // The fix also switches the value of CURRENT_COMPONENT
> // attribute of facesContext to the
> // org.apache.myfaces.component.html.ext.HtmlSelectOneRadio
> // which is the master for the
> org.apache.myfaces.custom.radio.HtmlRadio component
> // whose HTML is rendered by this method.
> // The switch was needed so RichFaces'
> // org.ajax4jsf.component.EventValueExpression.getComponent()
> // can find the AjaxComponent for HtmlSelectOneRadio.
> // Since we do no longer combine HtmlSelectOneRadio with
> // RichFaces, the fix for that problem might be obsolete.
> Object originalCurrentComp =
> facesContext.getAttributes().get(UIComponent.CURRENT_COMPONENT);
>
> try
> {
>
> facesContext.getAttributes().put(UIComponent.CURRENT_COMPONENT, uiComponent);
>
> StringWriter buff = new StringWriter();
> ResponseWriter originalWriter =
> facesContext.getResponseWriter();
> ResponseWriter myWriter =
> originalWriter.cloneWithWriter(buff);
> // L. Ulrich, 27.04.2011:
> // enclose the attributes in a dummy element.
> // Otherwise, the ResponseWriter may not write the
> // attributes in buff (at least, the implementation of
> // ResponseWriter in Mojarra 2.1.0 does not write the
> // attribute text to buff as long as there is no open+closed
> element).
> myWriter.startElement("dummy", uiComponent);
>
>
> HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(facesContext,
> myWriter, uiComponent, behaviors);
>
> HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, myWriter,
> uiComponent, behaviors);
>
> HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(facesContext,
> myWriter, uiComponent, behaviors);
> myWriter.endElement("dummy");
>
> myWriter.flush();
>
> String allBehaviorHtml = buff.toString();
> if (allBehaviorHtml.length() > 14)
> {
> int end = allBehaviorHtml.lastIndexOf('"') +1;
> String withoutDummy = allBehaviorHtml.substring(7, end);
> String modifiedHtml = withoutDummy.replaceAll("'" +
> clientId + "'", "this");
> for (int attrStart = modifiedHtml.indexOf('=');
> attrStart > 0; attrStart = modifiedHtml.indexOf('='))
> {
> String attrName = modifiedHtml.substring(0,
> attrStart).trim();
> modifiedHtml = modifiedHtml.substring(attrStart
> +1);
> int valueStart = modifiedHtml.indexOf('"');
> modifiedHtml =
> modifiedHtml.substring(valueStart +1);
> int valueEnd = modifiedHtml.indexOf('"');
> String attrValue = modifiedHtml.substring(0,
> valueEnd);
> modifiedHtml = modifiedHtml.substring(valueEnd
> +1);
>
> originalWriter.writeAttribute(attrName,
> attrValue, null);
> }
>
> }
>
> HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
> HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE_AND_EVENTS);
> }
> finally
> {
>
> facesContext.getAttributes().put(UIComponent.CURRENT_COMPONENT,
> originalCurrentComp);
> }
> // end of code replacement
> }
> else
> {
> HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
> HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE);
> }
> if (isDisabled(facesContext, uiComponent))
> {
>
> writer.writeAttribute(org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.DISABLED_ATTR,
> Boolean.TRUE, null);
> }
> writer.endElement(HTML.INPUT_ELEM);
> return itemId;
> }
--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira