8

为了根据某些操作有条件地呈现页面片段,我在我的 webapp 的几个地方有以下构造:

<h:panelGroup rendered="#{managedBean.serviceSelected == 'insurance'}">
    <ui:include src="/pages/edocket/include/service1.xhtml" />
</h:panelGroup>

我观察到,<ui:include>即使rendered属性评估,仍然会执行falseservice1.xhtml这会不必要地创建与包含的文件关联的所有支持 bean 。

如何<ui:include>在未呈现父 UI 组件时跳过执行,以免不必要地创建所有这些支持 bean?

4

2 回答 2

9

Unfortunately, this is by design. The <ui:include> runs as being a taghandler during the view build time, while the rendered attribute is evaluated during the view render time. This can be better understood by carefully reading this answer and substituting "JSTL" with "<ui:include>": JSTL in JSF2 Facelets... makes sense?

There are several ways to solve this, depending on the concrete functional requirement:

  1. Use a view build time tag like <c:if> instead of <h:panelGroup>. This however puts implications into the #{managedBean}. It can't be view scoped and should do its job based on HTTP request parameters. Exactly those HTTP request parameters should also be retained in subsequent request (by e.g. <f:param>, includeViewParams, etc) so that it doesn't break when the view is restored.

  2. Replace <ui:include> by a custom UIComponent which invokes FaceletContext#includeFacelet() during the UIComponent#encodechildren() method. So far no such component exist in any of the existing libraries. But I can tell that I've already such one in mind as a future addition for OmniFaces and it works as intuitively expected here at my test environment (with Mojarra). Here's a kickoff example:

    @FacesComponent(Include.COMPONENT_TYPE)
    public class Include extends UIComponentBase {
    
        public static final String COMPONENT_TYPE = "com.example.Include";
        public static final String COMPONENT_FAMILY = "com.example.Output";
    
        private enum PropertyKeys {
            src;
        }
    
        @Override
        public String getFamily() {
            return COMPONENT_FAMILY;
        }
    
        @Override
        public boolean getRendersChildren() {
            return true;
        }
    
        @Override
        public void encodeChildren(FacesContext context) throws IOException {
            getChildren().clear();
            FaceletContext faceletContext = ((FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY));
            faceletContext.includeFacelet(this, getSrc());
            super.encodeChildren(context);
        }
    
        public String getSrc() {
            return (String) getStateHelper().eval(PropertyKeys.src);
        }
    
        public void setSrc(String src) {
            getStateHelper().put(PropertyKeys.src, src);
        }
    
    }
    
于 2012-08-16T16:47:48.880 回答
6

使用条件表达式作为 ui:include src:

<h:panelGroup>
    <ui:include 
        src="#{managedBean.serviceSelected == 'insurance' ?
            '/pages/edocket/include/service1.xhtml'
            :
            '/pages/empty.xhtml'}"
    />
</h:panelGroup>
于 2013-02-21T22:13:38.807 回答