JSF 2.0:如何添加UIComponents及其内容以查看root?

时间:2010-11-24 15:36:12

标签: jsf jsf-2 uicomponents

我正在构建一个自定义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作为我的自定义组件的子项时,同样的问题也适用 - 它们成功呈现,但是无法从组件树中找到,因为我的自定义组件本身并不存在。

将组件放入组件树的诀窍是什么?

修改:调整标题以更好地反映问题。

2 个答案:

答案 0 :(得分:4)

我不确定getClientId(context)是否会返回myComponent。实际上,如果您的组件嵌套在NamingContainer组件中,例如<h:form>,则其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页面中会有<div id="myForm:myComponent:testDiv">发送到浏览器。此<div>组件在您的JSF组件树中不存在,因此无法在Java端检索。

答案 1 :(得分:1)

这是由一个愚蠢的错误引起的。可以假设,添加的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(...)的子组件{ {1}}而不是encodeBegin(...)

encodeAll(...)

@Override public void encodeBegin(FacesContext context) throws IOException { UIEditPanel editor = new UIEditPanel(); getChildren().add(editor); } 在内部将当前组件添加为子组件的父组件。

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

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