【问题标题】:Find component by ID in JSF在 JSF 中按 ID 查找组件
【发布时间】: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。这类问题应该得到更多的东西。

标签: jsf uicomponents


【解决方案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"。相关:stackoverflow.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 的需求。
    猜你喜欢
    • 2015-07-15
    • 2015-04-18
    • 1970-01-01
    • 2021-03-19
    • 2015-12-06
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    相关资源
    最近更新 更多