Find component by ID in JSF
Asked Answered
L

5

53

I want to find some UIComponent from managed bean by the id that I have provided.

I have written the following code:

private UIComponent getUIComponent(String id) {  
      return FacesContext.getCurrentInstance().getViewRoot().findComponent(id) ;  
}

I have defined a p:inputTextarea as:

<p:inputTextarea id="activityDescription" value="#{adminController.activityDTO.activityDescription}" required="true" maxlength="120"
    autoResize="true" counter="counter" counterTemplate="{0} characters remaining." cols="80" rows="2" />

Now if a call to the method as getUIComponent("activityDescription") it is returning null, but if I call it as getUIComponent("adminTabView:activityForm:activityDescription") then I can get the org.primefaces.component.inputtextarea.InputTextarea instance.

Is there any way to get the component with only the id i.e., "activityDescription" not the absolute id i.e., "adminTabView:activityForm:activityDescription"?

Lytton answered 17/1, 2013 at 11:49 Comment(4)
May I know why -1? Isn't it a valid question?Lytton
Perhaps it's just some naive user wandering in [java] tag who has no idea what you're talking about.Sanmicheli
I notice you are using primefaces. Have you checked ComponentUtils.java ? grepcode.com/file/repository.primefaces.org/org.primefaces/…Subside
@Tapas Bose: A brilliant question that actually cleared my visualisation of UIComponent class. +1 from my side. These kind of questions deserved something more than that.Sargent
M
50

You can use the following code:

public UIComponent findComponent(final String id) {

    FacesContext context = FacesContext.getCurrentInstance(); 
    UIViewRoot root = context.getViewRoot();
    final UIComponent[] found = new UIComponent[1];

    root.visitTree(new FullVisitContext(context), new VisitCallback() {     
        @Override
        public VisitResult visit(VisitContext context, UIComponent component) {
            if (component != null 
                && id.equals(component.getId())) {
                found[0] = component;
                return VisitResult.COMPLETE;
            }
            return VisitResult.ACCEPT;              
        }
    });

    return found[0];

}

This code will find only the first component in the tree with the id you pass. You will have to do something custom if there are 2 components with the same name in the tree (this is possible if they are under 2 different naming containers).

Modifier answered 17/1, 2013 at 12:40 Comment(4)
if (component != null && component.getId() != null && component.getId().equals(id)) // get those null checks inNorthing
When compiling against the JSF API, it's not possible to use FullVisitContext. It is, however possible, to create a visit context using VisitContext.createVisitContext(FacesContext.getCurrentInstance()).Olli
even with the null checks, this one got itself into an infinite loop for me - so it's not a great solution.Ackerley
You check that component is not null. While in general is a good practise, is it possible that visitTree can return a component null?Abm
P
2

I try this code, and it's help:

private static UIComponent getUIComponentOfId(UIComponent root, String id){
    if(root.getId().equals(id)){
        return root;
    }
    if(root.getChildCount() > 0){
        for(UIComponent subUiComponent : root.getChildren()){
                UIComponent returnComponent = getUIComponentOfId(subUiComponent, id);
                if(returnComponent != null){
                    return returnComponent;
            }
        }
    }
    return null;
}

Thanks

Pung answered 3/6, 2015 at 3:53 Comment(0)
C
1

Maybe it's not possible. The FacesContext.getCurrentInstance().getViewRoot().findComponent(id) method returns only one UIComponent. The ViewRoot is constructed as a tree so if you have two forms in the view, each one with a component with id="text", they will have it's parent components added to the id so they won't conflict. If you put the two id="text" components within the same form, you will have java.lang.IllegalStateException thrown.

If you want to find all components with the searched id, you could write a method that implements:

List<UIComponent> foundComponents = new ArrayList();
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
    if(component.getId().contains("activityDescription")){
        foundComponents.add(component);
    }
}

Or if you want to find the first occurrence:

UIComponent foundComponent;
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) {
    if(component.getId().contains("activityDescription")){
        foundComponent = component;
        break;
    }
}
Crunode answered 17/1, 2013 at 12:14 Comment(4)
There's one problem because if you get children from view root there can be another component with chilren and then next inside it and more... You have to use recursion here - no other option I thinkLodged
Of course, you're right, it's a tree. I've neglected the data structure.Crunode
Note that the docs on UIComponent.findComponent hint to a way to search a component by its clientId using invokeOnComponent with a callback.Outpost
@elias I think you answer fits exactly OP's needs.Crunode
A
0

Yes, in all parent components which are NamingContainers you have to add attribute prependId="false" - it will works in <h:form> for sure and should work in others.
If it is not possible to set it via attribute in .xhtml file you have to set such value programaticaly.

Suggestion after question's author comment:

If there is not such attribute in components that you are using try write find method like this:

private UIComponent findComponent(String id, UIComponent where) {
if (where == null) {
   return null;
}
else if (where.getId().equals(id)) {
   return where;
}
else {
   List<UIComponent> childrenList = where.getChildren();
   if (childrenList == null || childrenList.isEmpty()) {
      return null;
   }
   for (UIComponent child : childrenList) {
      UIComponent result = null;
      result = findComponent(id, child);
      if(result != null) {
         return result;
   }
   return null;
}   

Next just invoke

UIComponent iamLookingFor = findComponent(myId, FacesContext.getCurrentInstance().getViewRoot());

That will help?

Acuate answered 17/1, 2013 at 11:54 Comment(1)
Thanks for the response. In <h:form/> there is an attribute prependId="false", but the <p:tabView/> doesn't have it.Lytton
R
0

Just put prependId="false" to your form in which this textarea is.

Reflectance answered 17/1, 2013 at 13:5 Comment(1)
Terrible advice. Don't do that. Never use prependId="false". Related: #7415730 It was only (temporarily) introduced to get non-ajax based j_security_check forms to work with JSF 1.x anyway. Don't ever use it on JSF 2.x.Sanmicheli

© 2022 - 2024 — McMap. All rights reserved.