You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by Simon Kitching <sk...@obsidium.com> on 2005/11/29 01:55:04 UTC

Re: behavior of findComponent [or why forceId is a bad idea]

Dennis Byrne wrote:
> Have I misunderstood findComponent() this whole time?
> 
> In JSP:
> 
> <t:inputHidden  
>    forceId="true" 
>    id="id" 
>    value="#{siteCrudBacker.unit.id}" />
> 
> In Java (finds the component w/ for loop, but can't find it 
> w/ findComponent method):
> 
> private void displayChildren(UIComponent ui){
>   Iterator iterator = ui.getChildren().iterator();
>   for(;iterator.hasNext();){
> 	UIComponent uiChild = (UIComponent) iterator.next();
> 	if("id".equals(uiChild.getId())){
> 	  log.info(" child w/ id of ->" + uiChild.getId());
> 	  UIComponent sameChild = FacesContext
> 			.getCurrentInstance()
> 			.getViewRoot().findComponent("id");
> 	  if(sameChild == null)
> 		log.info("how can this be null?");
> 	}
> 	displayChildren(uiChild);
>   } // end of for
> } // end of method
> 
> In console:
> 
> INFO  SiteNumberValidator -  child w/ id of ->id
> INFO  SiteNumberValidator - how can this be null?

FindComponent works just like finding a file in a filesystem.

Given:
   /home/someuser/foo/file.txt
   /home/someuser/bar/file.txt

When current working dir = "/home/someuser/foo", the command "ls 
file.txt" will show the first file.

When current working dir = "/home/someuser/bar", the command "ls 
file.txt" will show the second file.

When current working dir is "/", the command will show no files. But "ls 
foo/file.txt" will show it.

In your debugging, you're at some arbitrary base component and find that 
there is a child with some particular id. Trying to then find that same 
id by asking the viewroot for it won't work if there is any intervening 
NamingContainer.

That's a fundamental flaw with "forceId"; it's a Tomahawk concept, not a 
JSF concept. Character NamingContainer.SEPARATOR_CHAR (ie ":") functions 
in exactly the same way as "/" in a file path, and NamingContainer 
components are like directories.

Setting forceId will cause the renderer to output a particular id into 
the generated page. And because "decoding" is done by asking each 
component to inspect the input parameters it works for submitted data. 
But because UIComponent.findComponent is part of the JSF spec, and the 
JSF spec mandates that clientId values contain absolute paths from the 
view root to the component it can quite reasonably look to see whether 
the id provided has NamingContainer.SEPARATOR_CHAR in it, and if so 
assume that there is no point in descending into any NamingContainer in 
the search for that component.

The forceId concept is like a filesystem that does away with 
directories. Imagine if "file.txt" was the same file, no matter where in 
the directory path you are. "rm file.txt", "rm foo/file.txt", "rm 
bar/baz/file.txt" would all affect the same file. Such a system would be 
unmanageable because name conflicts would occur; you'd never know 
whether a particular name was safe for use or not. And forceId is the 
same; it makes it impossible to compose pages from fragments because the 
namespacing feature of NamingContainer is ignored.

Of course for small pages where there is no attempt to "compose" the 
page from parts the forceId approach can work. But bring in things like 
Tiles or portlets and....hmm.


Corrections/rebuttals very welcome; if my argument is flawed I'm happy 
to hear about it.


Regards,

Simon