3 Replies Latest reply on Jan 29, 2009 2:17 PM by Stan Silvert

    Issue with JSFClientSession.getElement(id)

    Tim Evers Master

      There is an annoying little issue with the getElement method on JSFClientSession and the problem lies in the code

      public Element getElement(String componentID)
       String xpathQuery = buildXPathQuery(componentID);

      The buildXPathQuery does this
      private String buildXPathQuery(String componentID)
       return "//*[" + endsWith("ID", componentID) + " or " + endsWith("id", componentID) + "]";

      The big problem with this logic is this.

      Say I have to components on my page like this
      <h:inputText id="authorisedPrice" value="..."/>
      <h:inputText id="unauthorisedPrice" value="..."/>

      If I then try to access the inputText with the id of "authorisedPrice"

      I get a DuplicateClientIDException (By the way should ID be capitilised? or should it be DuplicateClientIdException).

      Is endsWith the correct logic? Should it end with ':{the id i provided}' so in my example the real endsWith logic would use ':authorisedPrice' as the matching pattern?

        • 1. Re: Issue with JSFClientSession.getElement(id)
          Stan Silvert Master

          Concerning the naming of DuplicateClientIDException, I don't know of any standard or consensus on the ID vs. Id issue, but if you know of a reason to change it then I'm open to it. I'd certainly like to get the naming right before GA if possible.

          The philosophy behind the JSFSession API is that, as much as possible, you should be able to create a test from looking at your markup. When you create that markup you only have the component ID attribute and you can't be certain what the rendered client ID will be. See http://www.jboss.org/community/docs/DOC-12515.

          So all the methods that take componentID as a parameter specify that what you are actually passing is a client ID suffix. The vast majority of the time, you can pass in the value of the ID attribute from your markup and everything will work.

          In your case though, you need to pass in ":authorizedPrice" instead of "authorizedPrice".

          If getElement() automatically prepended the ":" in all cases then that would cause other things to break. For instance, sometimes you want to pass in the entire client ID. Consider the case of two forms where each form has a button with id="mybutton". The client ID's would be form1:mybutton and form2:mybutton. If getElement() prepended a ":" then you would have no possible way to reference either element.

          Perhaps we should teach users to get in the habit of prepending the ":" themselves? The problem there is that it looks a little odd. I'm afraid that new developers would be put off by it.

          I certainly agree that it would be good to reduce the instances of DuplicateClientIDException. It's annoying when it happens, but at least the exception tells you all the matching client ID's and your problem is pretty easy to fix. So far, this is the best I have come up with. But further ideas on it are most welcome!


          • 2. Re: Issue with JSFClientSession.getElement(id)
            Wolfgang Knauf Master

            Hi Stan,

            about the ID querying: currently you test only for "endsWith 'componentID' ". It would solve the problem if the test was: "ID matches exactly the componentID parameter OR ID endswith ':componentID' ". Is this possible with your XPath query?

            I would also prefer "DuplicateClientIdException" ;-). See e.g. the "@Id" annotation. But I don't have more samples in the moment.


            • 3. Re: Issue with JSFClientSession.getElement(id)
              Stan Silvert Master

              I was just about to make that change when I realized a possible problem. Sometimes a single component renders several elements with similar client IDs. So say you have this custom component:

              <custom:myinput id="foo"/>

              It renders elements like
              <div id="div:foo">
               <input type="text" id="userInput:foo" value="default"/>
               <input type="hidden" id="foo" value="hiddencomponentvalue"/>

              Now say I want to set the value of the custom component. I had no way of knowing that the component rendered all those client ID's. So when I say client.setValue("foo", "bar"), it sets the hidden element instead. I'd have a very hard time figuring out what happened.