7 Replies Latest reply on Jan 28, 2010 11:29 AM by Mykhaylo Kozyaryk

    Obtaining newly generated clientId of JSF components

    Mykhaylo Kozyaryk Newbie

      Hi
      I have next code on my page:


      <a4j:commandLink
          onclick="clickButton('#{facesClientUtil.getClientId('someBtn')}');return false;">
      </a4j:commandLink>



      Click on this command link performs click on another button with id = someBtn and with generated clientId like: j_id67:j_id112:someBtn, but after some AJAX-requests which rerenders form with that button, its clientId is changed to j_id67:j_id183:someBtn, but


      #{facesClientUtil.getClientId('someBtn')}



      still returns old clientId j_id67:j_id112:someBtn. and it causes JavaScript error, since component with that id doesn't exist and I'm trying to invoke click on null element.


      getClientid implemented in next way:


      public String getClientId(String componentId) {
          FacesContext context = FacesContext.getCurrentInstance();
          UIViewRoot root = context.getViewRoot();
      
          UIComponent c = findComponent(root, componentId);
          return c.getClientId(context);
        }
      
        /**
         * Finds component with the given id
         */
        private UIComponent findComponent(UIComponent c, String id) {
          if (id.equals(c.getId())) {
            return c;
          }
          Iterator<UIComponent> kids = c.getFacetsAndChildren();
          while (kids.hasNext()) {
            UIComponent found = findComponent(kids.next(), id);
            if (found != null) {
              return found;
            }
          }
          return null;
        }



      Is there any way how to get newly generated clientId?


        • 1. Re: Obtaining newly generated clientId of JSF components
          Nikos Paraskevopoulos Novice

          Hello,


          The client id of the targeted button indicates that it is nested within two NamingContainers (one should be the <x:form>). The NamingContainers have no id of their own. Did you try specifying the id for them? Does it still behave the same?


          I see that you are trying to immitate a button click. Depending on your use case, the <a:jsFunction> might be more appropriate, have a look at it.

          • 2. Re: Obtaining newly generated clientId of JSF components
            Mykhaylo Kozyaryk Newbie

            Hi,
            I tried to specify id attribute for containing h:form, but in this case I got duplicated Id exception, because that form is defined on xhtml page that is included two times on page (I have rich:tabPanel control and on each tab I include page which contains that form)


            Also I tried using aj4:jsFunction:


            <a4j:jsFunction name="getClientId" data="#{resultData}" action="#{facesClientUtil.getClientId('addConditionBtn')}" oncomplete="alert(data);">
            </a4j:jsFunction>
            <a4j:commandLink onclick="clickButton(getClientId());return false;">
            Test
            </a4j:commandLink>



            But every time I click on 'Test' button oncomplete="alert(data);" results in alert with message: 'undefined'.
            I toggled breakpoint in facesClientUtil.getClientId method, but it still returns wrong clientId.

            • 3. Re: Obtaining newly generated clientId of JSF components
              Mykhaylo Kozyaryk Newbie

              Hi,
              I moved h:form tag from page which is included to containing page. So I get two forms on that page and I set different IDs for them :



              <rich:tabPanel id="myTabPanel" switchType="ajax" >
                  <rich:tab label="In Progress" name="inProgress" >
                   <h:form id="activeFiltersForm">
                       <ui:include src="filters.xhtml">
                        <ui:param name="tableState"
                            value="#{activeState}" />
                       </ui:include>
                   </h:form>
                  </rich:tab>
                  <rich:tab label="Complete" name="complete">
                   <h:form id="inactiveFiltersForm">
                       <ui:include src="filters.xhtml" >
                        <ui:param name="tableState"
                            value="#{inactiveState}" />
                       </ui:include>
                   </h:form>
                  </rich:tab>
              </rich:tabPanel>


              It resolves duplicated id for component exception and allowed me to define id for h:form by hands, instead of leaving it for JSF.


              And instead of one button which performs click on button in filters.xhtml I created two buttons, one of which clicks on activeFiltersForm:addFilterBtn and another on inactiveFiltersForm:addFilterBtn :



              <a4j:commandLink rendered="#{processMonitorController.selectedTab eq 'active'}"
                  onclick="clickButton('#{rich:clientId('activeFiltersForm:addFilterBtn')}');
                      return false;">
                  Add filter
              </a4j:commandLink>
              <a4j:commandLink rendered="#{processMonitorController.selectedTab eq 'inactive'}"
                  onclick="clickButton('#{rich:clientId('inactiveFiltersForm:addFilterBtn')}');
                      return false;">
                  Add filter
              </a4j:commandLink>


              And using rendered attribute, appropriate button for each tab will be rendered.


              But, this isn't nice solution, since for each tab we should create its own set of controls (here I posted only one button, but really there are more than one control),


              It would be great if I define only one set of controls and RichFaces or Seam will menage that on tab change I want to click on new button, but not old.

              • 4. Re: Obtaining newly generated clientId of JSF components
                Tim Evers Master

                Have you tried using the built in functions that come with richfaces.


                Read http://mkblog.exadel.com/ria/richfaces-ria/richfaces-built-in-client-functions/


                My guess is that this will probably work for you.

                • 5. Re: Obtaining newly generated clientId of JSF components
                  Mykhaylo Kozyaryk Newbie

                  Tim, thanks for answer.


                  Yes, that functions work for me, for example, rich:clientId which I'm using now in temporary solution, described in previous message, but it works only if I specify button id with form id, like activeFiltersForm:addFilterBtn, but when I have three forms, each on separate tab of rich:tabPanel, I should create three buttons, for each form, but desired behavior is, that I create one button, which clicks on appropriate button addFilterBtn, depending on tab, which currently is active. So I want to specify only button id - addFilterBtn, without form id, because in one moment of time, only one button with id = addFilterBtn rendered on page, only form id, where this button is placed on is changed.

                  • 6. Re: Obtaining newly generated clientId of JSF components
                    Tim Evers Master

                    I'd like to try understand your problem a bit better, but I really don't have the time to set up a test page for it at the moment.


                    So, something that you could do to avoid multiple buttons is just use jQuery to get you the client id. jQuery is already part of richfaces so you should be able to do something like...


                    <a4j:commandLink
                        onclick="clickButton("jQuery("a[id$='someBtn']").id()));return false;">
                    </a4j:commandLink>
                    



                    This is NOT tested, but the idea is.


                    jQuery select the <a> tag where the id attribute ends with 'someBtn' and get it's Id. This is then passed into your clickButton method.


                    I think this should work for you. :)

                    • 7. Re: Obtaining newly generated clientId of JSF components
                      Mykhaylo Kozyaryk Newbie

                      Hi, Tim


                      Thanks for pointing me to using jQuery. This is what I wanted. Since jQuery is JavaScript library and is invoked on client-side, it returns me actual id of currently displayed on page button, but not id of first component in RichFaces components tree, as returns RichFaces functions.


                      Right syntax will be next:
                                          



                      <a4j:commandLink 
                           onclick="clickButton(jQuery('input[id$=\'addConditionBtn\']').attr('id'));return false;">
                           Add filter
                      </a4j:commandLink>




                      Thanks a lot.