在 JSF 中按 ID 查找组件

Posted

技术标签:

【中文标题】在 JSF 中按 ID 查找组件【英文标题】:Find component by ID in JSF 【发布时间】:2012-12-31 22:53:57 【问题描述】:

我想通过我提供的 id 从托管 bean 中找到一些 UIComponent

我写了以下代码:

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

我已将p:inputTextarea 定义为:

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

现在如果以getUIComponent("activityDescription") 调用该方法,它将返回null,但如果我将其调用为getUIComponent("adminTabView:activityForm:activityDescription"),那么我可以获得org.primefaces.component.inputtextarea.InputTextarea 实例。

有什么方法可以获取只有 id 的组件,即“activityDescription”而不是绝对 id,即“adminTabView:activityForm:activityDescription”?

【问题讨论】:

我可以知道为什么-1吗?这不是一个有效的问题吗? 也许只是一些在[java]标签中徘徊的天真用户不知道你在说什么。 我注意到您使用的是 primefaces。你检查过 ComponentUtils.java 吗? grepcode.com/file/repository.primefaces.org/org.primefaces/… @Tapas Bose:一个绝妙的问题实际上清除了我对 UIComponent 类的可视化。从我这边+1。这类问题应该得到更多的东西。 【参考方案1】:

您可以使用以下代码:

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];


此代码将仅查找树中带有您传递的id 的第一个组件。如果树中有 2 个具有相同名称的组件(如果它们位于 2 个不同的命名容器下,这是可能的),您将不得不做一些自定义。

【讨论】:

if (component != null && component.getId() != null && component.getId().equals(id)) // 获取那些 null 检查 针对 JSF API 进行编译时,不能使用 FullVisitContext。但是,可以使用VisitContext.createVisitContext(FacesContext.getCurrentInstance()) 创建访问上下文。 即使有空检查,这对我来说也陷入了无限循环 - 所以这不是一个很好的解决方案。 您检查component 不是null。虽然一般来说是一个很好的做法,但visitTree 是否有可能返回组件null【参考方案2】:

只需将prependId="false" 输入到此文本区域所在的表单中即可。

【讨论】:

糟糕的建议。不要那样做。永远不要使用prependId="false"。相关:***.com/questions/7415230/… 它只是(暂时)引入以使基于非 ajax 的j_security_check 表单无论如何都可以与 JSF 1.x 一起使用。永远不要在 JSF 2.x 上使用它。【参考方案3】:

我试试这段代码,它很有帮助:

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;

谢谢

【讨论】:

【参考方案4】:

是的,在 NamingContainers 的所有父组件中,您必须添加属性 prependId="false" - 它肯定会在 &lt;h:form&gt; 中工作,并且应该在其他组件中工作。 如果无法通过 .xhtml 文件中的属性设置它,则必须以编程方式设置此类值。

问题作者评论后的建议:

如果您正在使用的组件中没有这样的属性,请尝试编写这样的查找方法:

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;
   

接下来调用

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

这会有帮助吗?

【讨论】:

感谢您的回复。在&lt;h:form/&gt; 中有一个属性prependId="false",但&lt;p:tabView/&gt; 没有它。【参考方案5】:

也许这是不可能的。 FacesContext.getCurrentInstance().getViewRoot().findComponent(id) 方法只返回一个UIComponent。 ViewRoot 被构造为一棵树,因此如果您在视图中有两个表单,每个表单都有一个带有id="text" 的组件,它们会将其父组件添加到 id 中,这样它们就不会发生冲突。如果将两个id="text" 组件放在同一个表单中,则会抛出java.lang.IllegalStateException

如果你想找到所有具有搜索到的 id 的组件,你可以编写一个方法来实现:

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

或者如果你想找到第一个匹配项:

UIComponent foundComponent;
for(UIComponent component: FacesContext.getCurrentInstance().getViewRoot().getChildren()) 
    if(component.getId().contains("activityDescription"))
        foundComponent = component;
        break;
    

【讨论】:

有一个问题,因为如果您从视图根目录获取子项,则可能会有另一个带有孩子的组件,然后在其中的下一个组件等等......您必须在这里使用递归 - 我认为没有其他选择跨度> 当然,你是对的,它是一棵树。我忽略了数据结构。 请注意,UIComponent.findComponent 上的文档暗示了一种使用带有回调的 invokeOnComponent 通过其 clientId 搜索组件的方法。 @elias 我认为您的回答完全符合 OP 的需求。

以上是关于在 JSF 中按 ID 查找组件的主要内容,如果未能解决你的问题,请参考以下文章

在 JSF 中按类型查找组件

在 JSF 2.0 中检索其他组件的客户端 ID

猫鼬查询:在数组中按 id 查找对象

在 JavaScript 对象数组中按 id 查找对象

在深层嵌套对象中按特定键查找所有值

在 RESTful Web 服务中按 ID 和用户名查找