动态表单

动态表单


一. 需求

  1. 目前的实现方案
    目前为实现faces页面的动态表现,普遍采用的办法是在beforeRender方法中动态地添加子组件,如下面代码所示:
    @Bind
    private UIPanel panel;
    
    @BeforeRender
    public void beforeRender(boolean isPostback){
      if(!isPostback){
        FacesContext context = FacesContext.getCurrentInstance();
        UITextField txt = (UITextField)context.getApplication().createComponent(UITextField.COMPONENT_TYPE);
        txt.setId("txt");
        //下面这一段是给添加的组件设置一大堆属性
        txt.set..
        txt.set..
        UIButton btn = (UIButton)context.getApplication().createComponent(UIButton.COMPONENT_TYPE);
        btn.setActionExpression(context.getApplication().getExpressionFactory().createMethodExpression(
    context.getELContext(), #{testBean.action}", void.class, new Class[]{}));
        panel.getChildren().add(txt);
        panel.getChildren().add(btn);
      }
    }
    
  2. 目前实现方案的缺点
    1. 用程序动态添加组件过于繁琐,如需要生成较复杂的页面,代码量较多
    2. 动态添加的组件必须设置id属性,如果组件较多,设置起来较麻烦
    3. 当设置某些ValueExpression,MethodExpression属性时,创建这些对象十分麻烦
  3. 达到的目标
    1. 能够简单自由地给faces页面添加组件
    2. 添加的组件与页面上本身的组件没有区别

二. 实现

  • 设计方案
  1. 方案一 使用嵌入页面的elite脚本动态地向facet树插入facet片断
    如下面的代码
    <w:page>
    <!-- 下面的代码相当于动态在这个位置添加textfield组件 -->
    <om:elite>
    //<![CDATA[
    out.print("<w:textField id=\"txt\" value=\"#{testBean.txtValue}\"/>");
    //]]>
    </om:elite>
    <w:page>
    
  2. 方案二 使用模板系统动态生成xhtml页面,然后将xhtml页面交由facelet处理之后走jsf生命周期
    如下面代码
    <!-- helloWorld.dxhtml -->
    <!-- 下面的代码根据name是否为空的条件来决定是否添加一个outputLabel到页面中 -->
    <w:page>
    ${if not empty name}
    Hello, <h:outputLabel value="#{name}"/>
    ${/if}
    <w:page>
    
  • 两种方案的优缺点
  1. 优点
    第一种方案使用起来比较直观,使用过jsp的人可以很快上手,elite也可以自由地引用facesContext中的变量(使用jsf引擎自带的elresolover)
    第二种方案由于在交由facelet处理之前即生成好动态页面,不会影响facelet的正常处理逻辑,对facelet及之后的jsf运行的基本上没有影响
  2. 缺点
    第一种方案输出双引号过于麻烦,要求用户能比较熟练地使用elite脚本,同时因为它是在eliteHandler中进行向facet树插入facet片断的操作的,可能会影响facelet的正常处理逻辑,造成某些不可知的严重后果
    第二种方案要求用户掌握一种模板语言,模板语言中的参数需要用户动手放置到templateContext中(也许可以将rquestMap,sessionMap中一些常用对象预先放置在templateContext中,如用户还有其它需要,可以定义自己的TemplateHandler来设置模板上下文参数)
  • 方案选择
    比较起两种方案的优缺点,目前倾向于选择第二种实现方案
  • 方案实现关键点
    实现一个DynamicFacesServlet,使用模板引擎对dxhtml文件进行处理,生成xhtml文件,并将请求转向到*.faces
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String reqPath = request.getServletPath();
            int periodPos = reqPath.lastIndexOf(".");
            if (periodPos >= 0) {
                String dFacesPagePath = reqPath.substring(0, periodPos) + getDfacesDefaultSuffix();
                String facesPagePath = reqPath.substring(0, periodPos) + getFacesDefaultSuffix();
                //generate faces page
                generateFacesPage(facesPagePath, dFacesPagePath, request);
                //redirect to generated faces page
                redirectReq(response, facesPagePath);
            }
        }
    

    这里生成faces页面的策略可分为三种,具体可在dfaces-config.xml中配置

    <dfaces-config>
      <view-mapping>
        <url-pattern>*</url-pattern>
        <!--
             模板所对应的template context handler, 用于向template context中设置模板上下文参数
        -->
        <template-context-handler>test.tplContextHandler.#{~View}TemplateContextHandler</template-context-handler>
        <!-- 
             scope可以有三种选择, application,session,request 
             当设置为application时,第一次访问*.dfaces,将会生成应用唯一的faces页面,以后访问则不生成直接跳转
             当设置为session时,第一次访问*.dfaces,将会生成session唯一的faces页面,以后访问则不生成直接跳转
             当设置为application时,每次访问*.dfaces,都会faces页面
        -->
        <scope>application</scope>
      </view-mapping>
    </dfaces-config>
    

    当请求被转发至faces页面后,通过注册一个ResourceResolver,使facelets可以找到生成的faces页面
    附上工程示例

      Name Size Creator (Last Modifier) Creation Date Last Mod Date Comment  
    ZIP Archive dynamicTable.zip 391 kB 徐新杰 2010年01月11日 2010年01月11日  

输入标签加入到本页面中
Please wait 
寻找标签吗?请输入搜索信息.