5 Replies Latest reply on Sep 13, 2007 11:30 AM by hbarca

    Ajax Button rendered programatically in a custom component n

    hbarca

      Hi,

      I render an ajax command button programatically from java code inside a jsf custom component. This custom component is inside a a4j:form.
      The ajax request works fine, but the response doesn't contain the ids of elements to be rerendered, nor the values of these elements. That's why the page doesn't get refreshed.
      I had already set the rerender value of the ajax command button in the java code with the id of the whole cusotom component, so normally the entire component should be rerendered.

      The component gets rerendered ok if I click an ajax command button outside the custom component, having the same properties as the button inside the component. It seems that having the ajax button inside the custom component breakes the ajax server response (as the ajax requests seem identical in both cases).

      Have you any idea of what I am doing wrong?
      Thank you,
      Horia.

        • 1. Re: Ajax Button rendered programatically in a custom compone

          code snippets will be helpful

          • 2. Re: Ajax Button rendered programatically in a custom compone
            hbarca

            Ok, my jsf page looks like this:

            <a4j:form id="myForm">
            <cust:helloworld id="myComp"
             value="#{backBean.input}" >
            </cust:helloworld>
            <p/>
            <a4j:commandButton id="another_ajax" value="Another AJAX" reRender="myComp"/>
            <p/>
            <a4j:log popup="false" />
            </a4j:form>
            


            The custom component helloworld contains a ajax command button that doesn't work.
            The command Button with id "another_ajax" is working fine.

            In the component class of the hellowrold component I encode the ajax button like tihs:

            
            encodeSubmitButtonAjax(context, "submit_ajax", "Submit with AJAX", new String[]{this.getId()});
            


            and the encodeSubmitButtonAjax looks like this (myButton is a class variable of the helloworld component):

            
            HtmlAjaxCommandButton myButton = new HtmlAjaxCommandButton();
            
            private void encodeSubmitButtonAjax(FacesContext context, String pClientId, String pValue, String[] pReRender) throws IOException {
            
             myButton.setValue(pValue);
             myButton.setParent(this);
             myButton.setId(pClientId);
            
             String reRender = "";
             for(String s : pReRender){
             if(!reRender.equals("")) reRender+=",";
             reRender+=s;
             }
             System.out.println("Setting reRender atttribute to " + reRender);
             myButton.setReRender(reRender);
             myButton.setType("button");
            
             myButton.encodeBegin(context);
             myButton.encodeChildren(context);
             myButton.encodeEnd(context);
             }
            


            And the ajax4jsf log has following output for an ajax request made with the working ajax button (the one outside the component):

            Full response content: <?xml version="1.0"?>
            <html xmlns="http://www.w3.org/1999/xhtml">
            <head><title></title>
            <script type="text/javascript" src="/JSFCustomCompPortlet/faces/a4j_3_1_0-SNAPSHOTorg.ajax4jsf.javascript.AjaxScript">
             </script>
            <script type="text/javascript" src="/JSFCustomCompPortlet/faces/a4j_3_1_0-SNAPSHOTorg/ajax4jsf/javascript/scripts/form.js"> </script>
            </head>
            <body>
            <span id="jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:myComp">
            <input type="text" name="jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:myComp.inputField" value="start_valueddd"
            size="20" /><p>
            </p>
            <input type="submit" name="jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:myComp.submit_local" value="Submit locally"
            size="6" />
            <input type="submit" name="jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:myComp.submit_global" value="Submit globally" size="6" />
            <p>You entered: start_valueddd</p>
            </span><input id="jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:submit_ajax"
             name="jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:submit_ajax"
             onclick="A4J.AJAX.Submit('_viewRoot','jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm',
            event,{'parameters':{'org.ajax4jsf.portlet.ACTION_URL':'/portal/portal/default/MyIPC/JSFCustomComponentAWindow?action=1&
            org.ajax4jsf.portlet.NAMESPACE=jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj&org.ajax4jsf.portlet.VIEWID=%2Fmain.jsp',
            'jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:submit_ajax':
            'jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:submit_ajax',
            'org.ajax4jsf.portlet.NAMESPACE':'jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj'} ,'actionUrl':'/JSFCustomCompPortlet/faces/main.jsp'} );
            return false;"
            value="Submit with AJAX" type="button" />
            <meta name="Ajax-Update-Ids" content="jbpns_2fdefault_2fMyIPC_2fJSFCustomComponentAWindowsnpbj:myForm:myComp" />
            <span id="ajax-view-state">
            <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState"
            
            value="rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAN0AAkvbWFpbi5qc3BwcQB+AAI=" />
            </span>
            <meta id="Ajax-Response" name="Ajax-Response" content="true" />
            </body>
            </html>
            


            ... but it has following output with the not working button:

            Response with content-type: text/xml;charset=UTF-8
            debug[10:34:14,175]: Full response content:
            <?xml version="1.0"?>
            <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
            <script type="text/javascript" src="/JSFCustomCompPortlet/faces/a4j_3_1_0-SNAPSHOTorg.ajax4jsf.javascript.AjaxScript"> </script>
            <script type="text/javascript" src="/JSFCustomCompPortlet/faces/a4j_3_1_0-SNAPSHOTorg/ajax4jsf/javascript/scripts/form.js"> </script>
            <meta name="Ajax-Update-Ids" content="" />
            <title></title>
            </head>
            <body>
            <span id="ajax-view-state">
            <input
            type="hidden"
            name="javax.faces.ViewState" id="javax.faces.ViewState" value="rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAN0AAkvbWFpbi5qc3BwcQB+AAI="
            />
            </span>
            <meta id="Ajax-Response" name="Ajax-Response" content="true"/>
            </body>
            </html>
            


            and I also get the following warning:

            warn[10:34:14,182]: No information in response about elements to replace
            


            Thank you for any advice.
            Horia.

            • 3. Re: Ajax Button rendered programatically in a custom compone
              alexsmirnov

              For a properly JSF component behavior, you need add support for a all JSF phases:
              1) Create instance of a included commandButton on a restore_view phase ( or save/restore it in the saveState/restoreState methods ).
              2) Call processDecodes method of the child HtmlAjaxButton from a decode() method of a custom component.

              • 4. Re: Ajax Button rendered programatically in a custom compone
                hbarca

                Thnak you Alex.
                I think they are two ways to embed a button in a custom component.

                The first way is to declare it as a child of the custom component, and in this case the save/restore and the decode methods of the child are called automatically during corresponding phases of the parent life cycle (parent's processSave, processRestore and processDecode methods call corresonding methods in children).

                Another way is not to declare it as a child, and then call all methods manually during the custom component's life cycle. For an ajaxButton it is necessary to call the decode method (unlike a non ajax UICommandButton), because in this method the component ids to be rerendered are read and passed to the ajaxContainer. This is how I tried in the first place. Unfortunately I got a NullPointerException in the queueEvent method of the AjaxActionComponent class during the decode phase of the ajax button. This happens because the ajax component assumes the button always has a parent (it calls getParent without checking if it has a parent).

                So I tried to make the button a child of my custom component. In this case, child's save/restore and processDecode calls are no more necessary. Now I get a IndexOutOfBoundsException in the processRestoreState of the UICommandBase class. During the restoreState, some component (I assume it is my custom component) has more children than children saved states. It tries to acces the second element of the array containing saved states of children, but the array has only one element (that is the AjaxCommandButton's state). This means, the custom component has for some strange reason two children in that moment.

                I am adding the Ajaxbutton as a child in the custom component's constructor. Is this correct? I assume that I'm adding the button somehow twice, and that's why I get this indexOutOfBounds exception, but I can't figure it out where this happens.

                Thank you for any support.

                • 5. Re: Ajax Button rendered programatically in a custom compone
                  hbarca

                  I found a solution to my problem, which was not an ajax4jsf problem, but a JSF one. I post it though, because I think it's interesting to see how JSF really works.

                  Actually I got the IndexOutOfBoundException because children were added twice in my custom component: once in the constructor of the component, and second during the restore tree view phase of flife cycle. That's why during the restore state phase the number of saved states did not correspond to the number of children and hence the exception.

                  The solution I'm using now is to add the children later in the life cycle of the component, that is in the encodeBegin method. I check there if the component already has chidlren and if not I add them. On the first instatiation of the component there is no saved tree view, so the children get added in the encode phase. On later instantiation, the children get added during the restore tree phase, but no second time in the encode phase.

                  Actually children are not thought to be used like components rendering themselves inside a custom component, but rather as nested tags inside another tag. In my case though, I don't want nested tags, but only one custom tag defining the whole component and I also want to make use of the automatic encoding, decoding, save/restore state of children (which can be complicated components like the Ajax button). So I had to use the hack to add the children during the encode phase.

                  If anyone has a better design solution of custom components being made of other (complicated) UI components, it would be nice to share it here.