+ Start a Discussion
jhartjhart 

ComponentID changes (oddness) in Winter '08 - docs issue?

I have a page with the following structure:

<apex: page>
<apex:form>
<apex: pageBlock>
<apex:inputText id="mytextinput">
.....


(forgive the spaces between "apex:" and "page" - necessary to avoid emoticons)

Prior to Winter '08, using {!$Component.mytextinput} gave me the ID of inputText component.

As of Winter '08, this no longer works (it just returns a blank string).

To access that component, I have to do two things:

(1) give an ID to the pageBlock, and then
(2) Use the hierarchical Component notation: {!$Component.mypageblock.mytextinput}

The Winter '08 docs are ambiguous with regards to the necessity of these extra steps.  They state:

For example, suppose you want to access a data table component that is contained in an <apex: pageBlock> tag. You only need to specify the ID for the <apex:dataTable> tag.This way, if the page hierarchy is ever changed (for example, if an <apex:detail> tag is wrapped around the table), you do not have to change your code.

Component IDs are accessed hierarchically on a page. For example, to access a data table with the ID tableID contained in a page block with the ID blockID, use the following expression: $Component.blockID.tableID.


These two paragraphs almost contradict each other; to me they make sense only if they are read as "if you have specified IDs for items higher in your hierarchy, then you need to use those IDs with the $Component accessor ($Component.mypageblock.mytextinput).  If you don't have IDs higher in your hierarchy, then you may use the direct accessor ($Component.mytextinput)."

However, my test case shows that the full hierarchy is required both on the page and in the accessor.

dchasmandchasman
There were definitely changes around $Component and also a number of components that were no correctly contributing to the component Id heirarchy have been corrected (e.g. pageBlock was one). Can you post you entire page's markup?
jhartjhart
Here's the markup up to & including the targeted inputText element.  Note the javascript that uses the {!$Component.globalblock.pendinglifespan} notation - it is necessary to strip a trailing decimal place of a value that - as defined - doesn't allow decimal places.  (This is a separate bug, posted here).

Code:
<apex:page controller="CtlAdmin">
<apex:stylesheet value="{!$Resource.aastyles}"/>
<apex:outputText value="{!aaTabSet}" escape="false"/>
<div style="text-align:right;margin-top:-40px;"><img src="{!$Resource.aalogo}"/></div><br/><br/>
<script type="text/javascript" src="{!$Resource.prototype}"></script>

<apex:form rendered="{!isAdmin}">

<script type="text/javascript">
var aaAdmin_onload_cached = window.onload; window.onload = function() { aaAdminLoad(); if (aaAdmin_onload_cached != null) aaAdmin_onload_cached(); }

function aaAdminLoad() {
 rmPendingDecimalPlace(); 
 }

function rmPendingDecimalPlace() {
 var pl = $('{!$Component.globalblock.pendinglifespan}');
 if (pl != null) pl.value = pl.value.replace('.0', '');
 }
</script>

<apex:pageBlock id="globalblock">
<apex:facet name="header"><div style="padding:4px;font-weight:bold;">Global Preferences</div></apex:facet>
<apex:facet name="footer"><apex:commandButton action="{!saveAAConfig}" id="saveConfig" value="Save" styleClass="btn" style="margin:8px;"/></apex:facet>
<br/>Delete pending after <apex:inputText id="pendinglifespan" value="{!aaconfig.PendingLifespan__c}" maxlength="4" style="margin:0px 4px 0px 4px;width:24px;"/> months.

 My original markup - which worked in Summer '07 - did not have an ID on the pageBlock, and accessed the "pendingLifespan" ID directly via {!$Component.pendinglifespan}

jwetzlerjwetzler
Hi jhart.  You'll find if you move your script tag into your pageBlock, it will work the way it used to.

Whenever you have a tag that refers to $Component, the id it's referring to has to be either in your direct hierarchy, or it has to be a sibling of your tag (which can be considered to be a part of your hierarchy).  If you're trying to refer to something outside of that, you will need to start adding in ids from the top down, hence the reason you now have to add in the pageBlock id as well.

The reason this worked before and did not work now is because, as Doug mentioned, pageBlock erroneously was not contributing to the hierarchy, when all components that can be seen as "containers" probably should be.  I think pageBlock is the only pre-existing component from Summer '07 that had this change made to it, but I'm not 100% on that.

There is just a little more discussion about this in a previous thread:
http://community.salesforce.com/sforce/board/message?board.id=Visualforce&message.id=170

Hopefully this clears things up a bit.

Jill
jhartjhart
Thanks Jill - that does fix it.

It makes more sense now that I think of the $Component accessor as supporting relative paths based on where in the DOM the $Component accessor is used.
thedgethedge

I too found the documentation contradictory.  After reading your post I was able to get my own page working correctly.....thanx!  However, there were a couple of things that didn't sit well with me.

1)  The placement of the <script> tag in the middle of the markup.  Perhaps I'm just being picky but I do like the physical separation of markup and script on a page.

2)  Having to traverse the hierarchy removes the benefits of being able to reference tags by their ID's with the getElementById browser function.  If you know the hierarchy then you don't need to use the getElementById reference.

I used a slightly different approach and created a function which receives the field ID as a parameter (called bindToFieldId) and  passed the {!$Component.fieldID} as a parameter into the function and subsequently used the document.getElementById(bindToFieldName) reference to set this fields value.  By placing the {!$Component.fieldID} in the function call I was able to forget about figuring out the hierarchy because the full hierarchy is automatically retrieved.

Code:
<script language="javascript">
function fieldTest(bindToFieldId){                
   var retval = 'My First Name';
   window.document.getElementById(bindToFieldId).value=retval;         
}
</script>


<apex:inputField id="aFirstName" value="{!a.First_Name__c}"/>
<input type="button" value="Button Test" onclick="fieldTest('{!$Component.aFirstName}')"/>

I figured I would post this example here since it was your post (and a nudge from 'aballard') which helped me.