2

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

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

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

4

2 回答 2

4

我不确定那getClientId(context)会回来myComponent。事实上,如果你的组件嵌套在一个NamingContainer组件中,比如 a <h:form>,他的 ID 就会以这个容器的 ID 为前缀。

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

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

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

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

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

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

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

于 2010-11-24T15:47:29.387 回答
1

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

UI代码编辑器:

@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(...)inencodeBegin(...)而不是 in的子组件encodeAll(...)

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

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

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

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

于 2010-11-25T09:53:30.637 回答