ICEfaces
  1. ICEfaces
  2. ICE-7955

Proxy tabset doesn't work as a portlet

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 3.0.1, EE-3.0.0.GA
    • Fix Version/s: 3.1.0.BETA2, 3.1
    • Component/s: ACE-Components, Bridge
    • Labels:
      None
    • Environment:
      ICEfaces 3 ACE Portlet Portal
    • Assignee Priority:
      P1

      Description

      When running the Proxy version of the tabset as a portlet, the content of the other tabs does not display when clicking on them. There are no obvious client or server errors logged.

        Activity

        Hide
        Deryk Sinotte added a comment -

        This is still not working but now at least logs a warning which may provide a clue:

        1-Jun-2012 10:59:44 PM org.apache.myfaces.renderkit.html.HtmlLabelRenderer encodeBegin
        WARNING: Attribute 'for' of label component with id jpfcpncuivr_A6150_j_id0:proxyForm:_7_d is not defined

        Show
        Deryk Sinotte added a comment - This is still not working but now at least logs a warning which may provide a clue: 1-Jun-2012 10:59:44 PM org.apache.myfaces.renderkit.html.HtmlLabelRenderer encodeBegin WARNING: Attribute 'for' of label component with id jpfcpncuivr_A6150_j_id0:proxyForm: _7_d is not defined
        Hide
        Deryk Sinotte added a comment -

        In the non-portlet version of this component example, when entering the text and hitting Enter, a partial-response is sent back and contains multiple updates for fixing the ViewState in the forms:

        <update id="proxyForm_fixviewstate"><![CDATA[<span id="proxyForm_fixviewstate"><script type="text/javascript">ice.fixViewState('proxyForm', '-2634178208448956548:1181024006892679813');</script></span>]]></update>
        <update id="frm_fixviewstate"><![CDATA[<span id="frm_fixviewstate"><script type="text/javascript">ice.fixViewState('frm', '-2634178208448956548:1181024006892679813');</script></span>]]></update>
        <update id="sourceCodeForm_fixviewstate"><![CDATA[<span id="sourceCodeForm_fixviewstate"><script type="text/javascript">ice.fixViewState('sourceCodeForm', '-2634178208448956548:1181024006892679813');</script></span>]]></update>

        In the portlet example, the updates look like this:

        <update id="_jpfcpncuivr_A6150_j_id1:proxyForm_fixviewstate"><![CDATA[<span id="_jpfcpncuivr_A6150_j_id1:proxyForm_fixviewstate"><script type="text/javascript">ice.fixViewState('proxyForm', '9034984880897130778:5409151781173733796');</script></span>]]></update>
        <update id="dynamic-code-compat"><![CDATA[<span id="dynamic-code-compat"></span>]]></update>
        <update id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-retrieve-update_fixviewstate"><![CDATA[<span id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-retrieve-update_fixviewstate"><script type="text/javascript">ice.fixViewState('v2zvetu1-retrieve-update', '9034984880897130778:5409151781173733796');</script></span>]]></update>
        <update id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-single-submit_fixviewstate"><![CDATA[<span id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-single-submit_fixviewstate"><script type="text/javascript">ice.fixViewState('v2zvetu1-single-submit', '9034984880897130778:5409151781173733796');</script></span>]]></update>

        The main difference being that while the id of the forms is namespaced for portlets:

        <span id="_jpfcpncuivr_A6150_j_id1:proxyForm_fixviewstate">

        the actual id used in the update script is not:

        ice.fixViewState('proxyForm', '9034984880897130778:5409151781173733796');

        This seems like the most reasonable culprit at this point so I'll look to see how the fixViewState update is constructed.

        Show
        Deryk Sinotte added a comment - In the non-portlet version of this component example, when entering the text and hitting Enter, a partial-response is sent back and contains multiple updates for fixing the ViewState in the forms: <update id="proxyForm_fixviewstate"><![CDATA [<span id="proxyForm_fixviewstate"><script type="text/javascript">ice.fixViewState('proxyForm', '-2634178208448956548:1181024006892679813');</script></span>] ]></update> <update id="frm_fixviewstate"><![CDATA [<span id="frm_fixviewstate"><script type="text/javascript">ice.fixViewState('frm', '-2634178208448956548:1181024006892679813');</script></span>] ]></update> <update id="sourceCodeForm_fixviewstate"><![CDATA [<span id="sourceCodeForm_fixviewstate"><script type="text/javascript">ice.fixViewState('sourceCodeForm', '-2634178208448956548:1181024006892679813');</script></span>] ]></update> In the portlet example, the updates look like this: <update id="_jpfcpncuivr_A6150_j_id1:proxyForm_fixviewstate"><![CDATA [<span id="_jpfcpncuivr_A6150_j_id1:proxyForm_fixviewstate"><script type="text/javascript">ice.fixViewState('proxyForm', '9034984880897130778:5409151781173733796');</script></span>] ]></update> <update id="dynamic-code-compat"><![CDATA [<span id="dynamic-code-compat"></span>] ]></update> <update id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-retrieve-update_fixviewstate"><![CDATA [<span id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-retrieve-update_fixviewstate"><script type="text/javascript">ice.fixViewState('v2zvetu1-retrieve-update', '9034984880897130778:5409151781173733796');</script></span>] ]></update> <update id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-single-submit_fixviewstate"><![CDATA [<span id="_jpfcpncuivr_A6150_j_id1:v2zvetu1-single-submit_fixviewstate"><script type="text/javascript">ice.fixViewState('v2zvetu1-single-submit', '9034984880897130778:5409151781173733796');</script></span>] ]></update> The main difference being that while the id of the forms is namespaced for portlets: <span id="_jpfcpncuivr_A6150_j_id1:proxyForm_fixviewstate"> the actual id used in the update script is not: ice.fixViewState('proxyForm', '9034984880897130778:5409151781173733796'); This seems like the most reasonable culprit at this point so I'll look to see how the fixViewState update is constructed.
        Hide
        Mircea Toma added a comment -

        Pass the client ID (instead of just the ID) into the ice.fixViewState function, this way the portlet namespace will be prefixing the form ID.

        Show
        Mircea Toma added a comment - Pass the client ID (instead of just the ID) into the ice.fixViewState function, this way the portlet namespace will be prefixing the form ID.
        Hide
        Deryk Sinotte added a comment -

        Now that the changes to FixViewState have been applied, there are no more errors in the client console. However the Proxy TabSet component still does not work as a portlet.

        Show
        Deryk Sinotte added a comment - Now that the changes to FixViewState have been applied, there are no more errors in the client console. However the Proxy TabSet component still does not work as a portlet.
        Hide
        Deryk Sinotte added a comment -

        The issue is on the client side with trying to find the form. There is no form around the whole content. Instead, the forms are inside the tab content. So the normal strategy of climbing the tree to find the form does not work and it falls back to a secondary strategy. This secondary strategy works in the non-portlet example:

        },getTabIndexField: function(g) {
        var a = null;
        try

        { //This fails because there is no parent form a = formOf(g); }

        catch (d) {
        if (!a) {
        //This works in non-portlet mode but not portlet mode ("myTabSet" + "_tsc" exists)
        //but in non-portlet mode ("_jpfcpncuivr_A6150_j_id1:myTabSet" + "_tsc" does not exist).
        var c = document.getElementById(g.id + "_tsc");
        if (c) {
        try

        { a = formOf(c); }

        catch (d) {
        }
        } else {
        }
        }
        }
        if (a) {
        var b = document.getElementById(a.id + "yti");
        if (!b)

        { b = ice.ace.tabset.createHiddenField(a, a.id + "yti"); }

        return b;
        } else

        { return null; }

        Looking at the rendered source, the hidden input field in the portlet is actually named without the portlet namespace which is why it can't be found:

        <input id="myTabSet_tsc" name="myTabSet_tsc" type="hidden" style="null">

        So it appears that some of the rendering for the component is not rendering out the correct ids that will work in a portlet environment.

        Show
        Deryk Sinotte added a comment - The issue is on the client side with trying to find the form. There is no form around the whole content. Instead, the forms are inside the tab content. So the normal strategy of climbing the tree to find the form does not work and it falls back to a secondary strategy. This secondary strategy works in the non-portlet example: },getTabIndexField: function(g) { var a = null; try { //This fails because there is no parent form a = formOf(g); } catch (d) { if (!a) { //This works in non-portlet mode but not portlet mode ("myTabSet" + "_tsc" exists) //but in non-portlet mode ("_jpfcpncuivr_A6150_j_id1:myTabSet" + "_tsc" does not exist). var c = document.getElementById(g.id + "_tsc"); if (c) { try { a = formOf(c); } catch (d) { } } else { } } } if (a) { var b = document.getElementById(a.id + "yti"); if (!b) { b = ice.ace.tabset.createHiddenField(a, a.id + "yti"); } return b; } else { return null; } Looking at the rendered source, the hidden input field in the portlet is actually named without the portlet namespace which is why it can't be found: <input id="myTabSet_tsc" name="myTabSet_tsc" type="hidden" style="null"> So it appears that some of the rendering for the component is not rendering out the correct ids that will work in a portlet environment.
        Hide
        Deryk Sinotte added a comment -

        From the encode method in TabSetProxy.java:

        public void encodeBegin(FacesContext context) throws IOException

        { super.encodeBegin(context); ResponseWriter writer = context.getResponseWriter(); String id = getFor() + "_tsc"; writer.startElement(HTML.INPUT_ELEM, this); writer.writeAttribute(HTML.ID_ATTR, id, HTML.ID_ATTR); writer.writeAttribute(HTML.NAME_ATTR, id, HTML.NAME_ATTR); writer.writeAttribute(HTML.TYPE_ATTR, "hidden", HTML.TYPE_ATTR); writer.endElement(HTML.INPUT_ELEM); }

        I would guess that the getFor method is not returning the proper value.

        Show
        Deryk Sinotte added a comment - From the encode method in TabSetProxy.java: public void encodeBegin(FacesContext context) throws IOException { super.encodeBegin(context); ResponseWriter writer = context.getResponseWriter(); String id = getFor() + "_tsc"; writer.startElement(HTML.INPUT_ELEM, this); writer.writeAttribute(HTML.ID_ATTR, id, HTML.ID_ATTR); writer.writeAttribute(HTML.NAME_ATTR, id, HTML.NAME_ATTR); writer.writeAttribute(HTML.TYPE_ATTR, "hidden", HTML.TYPE_ATTR); writer.endElement(HTML.INPUT_ELEM); } I would guess that the getFor method is not returning the proper value.
        Hide
        Deryk Sinotte added a comment - - edited

        The logic in the markup for the proxy tabset looks like this:

        <ace:tabSet id="myTabSet" cancelOnInvalid="#

        {not tabProxy.invalidSwitch}

        " clientSide="false">
        <ace:tabPane id="txtEntry">
        <f:facet name="label">Text Entry</f:facet>
        <h:form id="proxyForm">
        <ace:tabSetProxy for="myTabSet"/>

        Looking at the logic we use to identify the "for" component id it seems that the code is not geared to:

        • find the component that the "for" attribute is associated with so that it can get it's id
        • determine the id programmatically in a portlet-safe way

        The reason that finding the "for" component is important is that, as noted above, it may not be in the same naming container and if it's a portlet, the raw value "myTabSet" is not going to be the final client id that's rendered out. It'll look more like:
        "A6150:myTabSet" (PortletFaces Bridge) or
        "_jpfcpncuivr_A6150_j_id1:myTabSet" (Liferay Faces Bridge)

        It also appears that we have "getFor()" methods in a number (17) of compat (10) and ACE (7) component classes that looks like it could be factored out into a common mechanism or two.

        Show
        Deryk Sinotte added a comment - - edited The logic in the markup for the proxy tabset looks like this: <ace:tabSet id="myTabSet" cancelOnInvalid="# {not tabProxy.invalidSwitch} " clientSide="false"> <ace:tabPane id="txtEntry"> <f:facet name="label">Text Entry</f:facet> <h:form id="proxyForm"> <ace:tabSetProxy for="myTabSet"/> Looking at the logic we use to identify the "for" component id it seems that the code is not geared to: find the component that the "for" attribute is associated with so that it can get it's id determine the id programmatically in a portlet-safe way The reason that finding the "for" component is important is that, as noted above, it may not be in the same naming container and if it's a portlet, the raw value "myTabSet" is not going to be the final client id that's rendered out. It'll look more like: "A6150:myTabSet" (PortletFaces Bridge) or "_jpfcpncuivr_A6150_j_id1:myTabSet" (Liferay Faces Bridge) It also appears that we have "getFor()" methods in a number (17) of compat (10) and ACE (7) component classes that looks like it could be factored out into a common mechanism or two.
        Hide
        Deryk Sinotte added a comment -

        I was able to get the proxy to finally work but it will likely need the component team to have a look and potentially come up with the correct fix.

        Currently the TabSetProxy extends TabSetProxyBase which is a generated class. It generates code to handle the getFor and setFor methods. In this case it using fairly boilerplate code to store the value of the for attribute and look it up again. Unfortunately, this doesn't take into consideration that the base id in the markup won't necessarily be the same as the client id that ends up getting rendered. This is especially true for portlets which are namespaced within a UIViewRoot. This is discussed in previous comments in the case.

        What I ended up doing was overriding the getFor method in TabSetProxy to the following:

        @Override
        public String getFor() {
        //Get the originally stored value
        String forComponentId = super.getFor();

        //Proceed to find the component that it belongs to using one of our existing find methods
        UIViewRoot root = getFacesContext().getViewRoot();
        UIComponent forComponent = CoreComponentUtils.findComponentInView(root, forComponentId);
        if( forComponent != null )

        { //Get the client id of the found component and use that forComponentId = forComponent.getClientId(); }

        return forComponentId;
        }

        Assuming the component can be reliably found this way, the resulting hidden inputText value now matches the actual id of the tabset:

        <input id="A6150:myTabSet_tsc" name="A6150:myTabSet_tsc" type="hidden">

        Clicking on the tab actually results in a script being executed:

        <em id="A6150:confirmLbl" onclick="if(ice.ace.util.isEventSourceInputElement(event)) event.cancelBubble = true;">Confirmation</em>

        which can now match up the id of the hidden input with the id of the tabset (which it couldn't do before and was causing the problem).

        I imagine that we have a similar issue with any of our components that use the same boilerplate code for their "for" attribute in that if they don't actually do a search and return the client id, something may not work correctly.

        Show
        Deryk Sinotte added a comment - I was able to get the proxy to finally work but it will likely need the component team to have a look and potentially come up with the correct fix. Currently the TabSetProxy extends TabSetProxyBase which is a generated class. It generates code to handle the getFor and setFor methods. In this case it using fairly boilerplate code to store the value of the for attribute and look it up again. Unfortunately, this doesn't take into consideration that the base id in the markup won't necessarily be the same as the client id that ends up getting rendered. This is especially true for portlets which are namespaced within a UIViewRoot. This is discussed in previous comments in the case. What I ended up doing was overriding the getFor method in TabSetProxy to the following: @Override public String getFor() { //Get the originally stored value String forComponentId = super.getFor(); //Proceed to find the component that it belongs to using one of our existing find methods UIViewRoot root = getFacesContext().getViewRoot(); UIComponent forComponent = CoreComponentUtils.findComponentInView(root, forComponentId); if( forComponent != null ) { //Get the client id of the found component and use that forComponentId = forComponent.getClientId(); } return forComponentId; } Assuming the component can be reliably found this way, the resulting hidden inputText value now matches the actual id of the tabset: <input id="A6150:myTabSet_tsc" name="A6150:myTabSet_tsc" type="hidden"> Clicking on the tab actually results in a script being executed: <em id="A6150:confirmLbl" onclick="if(ice.ace.util.isEventSourceInputElement(event)) event.cancelBubble = true;">Confirmation</em> which can now match up the id of the hidden input with the id of the tabset (which it couldn't do before and was causing the problem). I imagine that we have a similar issue with any of our components that use the same boilerplate code for their "for" attribute in that if they don't actually do a search and return the client id, something may not work correctly.
        Hide
        Deryk Sinotte added a comment -

        I've resolved this issue by overriding the getFor() method in TabSetProxy.java as noted in the previous comments. I tested in both portlet and non-portlet scenarios. While this fixes it for the particular case, I still believe we may need to review how the getFor/setFor attribute code is generated and used for the other components as well.

        Show
        Deryk Sinotte added a comment - I've resolved this issue by overriding the getFor() method in TabSetProxy.java as noted in the previous comments. I tested in both portlet and non-portlet scenarios. While this fixes it for the particular case, I still believe we may need to review how the getFor/setFor attribute code is generated and used for the other components as well.

          People

          • Assignee:
            Deryk Sinotte
            Reporter:
            Deryk Sinotte
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: