[ 
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

Reply via email to