JSF 2.0: How to add UIComponents and their contents to view root?

妖精的绣舞 提交于 2019-12-06 10:33:53

I am not sure that getClientId(context) will return myComponent. Indeed, if your component is nested in a NamingContainer component, such as a <h:form>, his ID will be prefixed with the ID of this container.

For example, if you have the following XHTML page:

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

then you will have to retrieve your component using:

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

Regarding the context.getViewRoot().findComponent("myComponent:testDiv"); (or context.getViewRoot().findComponent("myForm:myComponent:testDiv");, it will return null because there is no such element in the JSF components tree on server side. The code:

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

will only set the ID attribute on the HTML generated component, ie you will have a <div id="myForm:myComponent:testDiv"> in your HTML page sent to the browser. This <div> component has no existence in your JSF component tree and thus cannot be retrieved on the Java side.

This was caused by a stupid mistake. As one would suppose, the added UIComponent does get automatically added to ViewRoot. I was, however, calling a custom UIComponent (the one I was talking about in the question) from inside my other custom UIComponent (which I din't mention because I forgot that it was there) like this:

UICodeEditor:

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

Then called this in a template like:

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

Here, the UICodeEditor does get automatically added to the ViewRoot, however, UIEditPanel inside it does not, because I was simply calling encodeAll(context) and so the UIEditPanel has no way of knowing its' parent. A quick fix would be to manually set the parent of the child component:

editor.setParent(this);

Alternative approach (maybe better) would be to extend from UIComponentBase (as we normally do) and not manually call encodeAll(context) but just add the component as a child with getChildren.add(...) in encodeBegin(...) and not in encodeAll(...):

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

The getChildren().add() internally adds the current component as parent of the children.

Considering the location of the child creation, it might be better to build them straight in the constructor and not override the encodeXXX methods at all, if there's no need to use ResponseWriter (if there is, then you need to override those). However, it is more flexible to override and manually call encoding, whatever you need.

Also note that a custom UIComponent cannot directly be an <f:ajax render= target, because it isn't represented by a DOM element in the DOM tree. This this the same case as with composite components, where I tend to wrap the composite component implementation into a JSF managed panelGroup so that the contents can be re-rendered later on.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!