【问题标题】:JSF 2.0: How to add UIComponents and their contents to view root?JSF 2.0:如何将 UIComponents 及其内容添加到查看根目录?
【发布时间】:2011-05-15 04:33:12
【问题描述】:

我正在构建一个自定义 UIComponent 并在其中添加元素(和其他库存 UIComponents)。组件渲染正常,但在 ViewRoot 中找不到。

假设我有:

ResponseWriter writer;

@Override
public void encodeBegin(FacesContext context) throws IOException {
    writer = context.getResponseWriter();
    writer.startElement("div", this);
    writer.writeText("testing", null);
    writer.writeAttribute("id", getClientId(context) + ":testDiv", null);
}

@Override
public void encodeEnd(FacesContext context) throws IOException {
    writer.endElement("div");        
}

添加为:

<x:myUiComponent id="myComponent" />

这渲染正常,但是我无法从 ViewRoot 中找到组件或其子 div:

context.getViewRoot().findComponent("myComponent");  // returns null
context.getViewRoot().findComponent("myComponent:testDiv");  // returns null
findComponent("myComponent:testDiv");  // called within the custom component, throws java.lang.IllegalArgumentException?

当我尝试将其他 UIComponents 添加为我的自定义组件的子组件时,同样的问题也存在 - 它们成功呈现,但是由于我的自定义组件本身并不存在,因此无法从组件树中找到。

将组件放入组件树的技巧是什么?

编辑:调整标题以更好地反映问题。

【问题讨论】:

    标签: jsf jsf-2 uicomponents


    【解决方案1】:

    我不确定getClientId(context) 是否会返回myComponent。确实,如果你的组件嵌套在NamingContainer 组件中,比如&lt;h:form&gt;,那么他的ID 就会以这个容器的ID 为前缀。

    例如,如果您有以下 XHTML 页面:

    <h:form id="myForm">
        <x:myUiComponent id="myComponent" />
    

    那么您将不得不使用以下方法检索您的组件:

    context.getViewRoot().findComponent("myForm:myComponent");
    

    关于context.getViewRoot().findComponent("myComponent:testDiv");(或context.getViewRoot().findComponent("myForm:myComponent:testDiv");,它将返回null,因为服务器端的JSF组件树中没有这样的元素。代码:

    writer.writeAttribute("id", getClientId(context) + ":testDiv", null);
    

    只会在HTML 生成的组件 上设置ID 属性,即您的HTML 页面中将有一个&lt;div id="myForm:myComponent:testDiv"&gt; 发送到浏览器。这个&lt;div&gt; 组件在您的 JSF 组件树中不存在,因此无法在 Java 端检索。

    【讨论】:

    • 嗯,你是对的,getClientId(context) 实际上返回的是 JSF 生成的 ID(本例中为“j_id11”)。我想必须用 UIComponents 手动覆盖自动生成的 ID(通过 setId())。在这种情况下,生成的 ID 没有前缀,因为我直接从 h:body 调用我的组件。无论如何,我怎样才能添加我的自定义 UIComponent 以便它本身被添加到组件树中(例如,我可以通过 AJAX 调用重新渲染它)?如果这不可能,我如何在我的 UIComponent 中添加子元素,以便将它们添加到组件树中?
    • 好的,感谢您的意见,我找到了问题(见我的回答)。我想这是从问题中甚至看不到的东西。下次我会尽量小心!
    【解决方案2】:

    这是由一个愚蠢的错误造成的。正如人们所想的那样,添加的UIComponent确实会自动添加到ViewRoot。但是,我是从我的另一个自定义 UIComponent(我没有提到因为我忘记它在那里)中调用自定义 UIComponent(我在问题中谈论的那个),如下所示:

    UICodeEditor:

    @Override
    public void encodeAll(FacesContext context) throws IOException {
        UIEditPanel editor = new UIEditPanel();
        editor.encodeAll(context);
    }
    

    然后在这样的模板中调用它:

    <!-- codeEditor is taghandler for UICodeEditor -->
    <x:codeEditor />
    

    这里,UICodeEditor确实会自动添加到ViewRoot,但是,它里面的 UIEditPanel 没有,因为我只是调用了encodeAll(context),所以 UIEditPanel 无法知道它的'家长。一个快速的解决方法是手动设置子组件的父组件:

    editor.setParent(this);
    

    另一种方法(也许更好)是从UIComponentBase 扩展(就像我们通常做的那样),而不是手动调用encodeAll(context),而只是将组件添加为getChildren.add(...)encodeBegin(...) 中而不是@ 中的子组件987654334@:

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        UIEditPanel editor = new UIEditPanel();
        getChildren().add(editor);
    }
    

    getChildren().add() 在内部将当前组件添加为子组件的父组件。

    考虑到子创建的位置,最好直接在构造函数中构建它们并且根本不覆盖encodeXXX 方法,如果不需要使用ResponseWriter(如果有,那么你需要覆盖那些)。但是,无论您需要什么,都可以更灵活地覆盖和手动调用编码。

    另外请注意,自定义 UIComponent 不能直接作为 &lt;f:ajax render= 目标,因为它不是由 DOM 树中的 DOM 元素表示的。这与复合组件的情况相同,我倾向于将复合组件实现包装到 JSF 管理的 panelGroup 中,以便以后可以重新呈现内容。

    【讨论】:

      猜你喜欢
      • 2014-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多