我正在构建一个自定义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作为我的自定义组件的子项时,同样的问题也适用 - 它们成功呈现,但是无法从组件树中找到,因为我的自定义组件本身并不存在。
将组件放入组件树的诀窍是什么?
修改:调整标题以更好地反映问题。
答案 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=
中,以便稍后可以重新呈现内容。