如何创建动态JSF表单字段

如何创建动态JSF表单字段,jsf,components,facelets,dynamic-forms,Jsf,Components,Facelets,Dynamic Forms,我发现了一些类似的问题,比如一个,但是有很多方法可以做到这一点,这让我更加困惑 我们正在读取一个XML文件。此XML包含一些需要显示的表单字段的信息 因此,我创建了这个定制的DynamicField.java,它包含了我们需要的所有信息: public class DynamicField { private String label; // label of the field private String fieldKey; // some key to identify the f

我发现了一些类似的问题,比如一个,但是有很多方法可以做到这一点,这让我更加困惑

我们正在读取一个
XML
文件。此
XML
包含一些需要显示的表单字段的信息

因此,我创建了这个定制的
DynamicField.java
,它包含了我们需要的所有信息:

public class DynamicField {
  private String label; // label of the field
  private String fieldKey; // some key to identify the field
  private String fieldValue; // the value of field
  private String type; // can be input,radio,selectbox etc

  // Getters + setters.
}
所以我们有一个
列表

我希望遍历此列表并填充表单字段,使其看起来像这样:

<h:dataTable value="#{dynamicFields}" var="field">
    <my:someCustomComponent value="#{field}" />
</h:dataTable>

然后将返回相应的JSF表单组件(即标签、inputText)

另一种方法是只显示
,然后返回带有表单元素的
HtmlDataTable
。(我认为这可能更容易做到)


哪种方法最好?有人能告诉我一些链接或代码,它显示了我如何创建这个?我更喜欢完整的代码示例,而不是回答“您需要一个子类
javax.faces.component.UIComponent

如果源代码是XML,我建议采用完全不同的方法:。Facelets是基于XHTML的。您可以轻松地使用XSL从XML转换为XHTML。这是可以做到的,在JSF开始工作之前,有一点像样的
过滤器

这里有一个启动示例

persons.xml


一
这个过滤器将使用
persons.xsl
persons.xml
转换为
persons.xhtml
,最后将
persons.xhtml
放在JSF期望的位置

诚然,XSL有一点学习曲线,但它是这项工作的正确工具,因为源是XML,目标也是基于XML的

要在表单和托管bean之间进行映射,只需使用
Map
。如果您这样命名输入字段


...
提交的值将通过
Map
field1
field2
field3
等方式提供。

因为源代码实际上不是XML,而是Javabean,而另一个答案不值得编辑成完全不同的风格(对于其他人将来的参考可能仍然有用),我将添加另一个基于Javabean来源的答案


当源代码是Javabean时,我基本上看到了三个选项

  • 使用JSF
    rendered
    属性或甚至JSTL
    /
    标记有条件地呈现或构建所需的组件。下面是使用
    渲染的
    属性的示例:

    
    
    JSTL方法的一个例子可以在No中找到,JSTL绝对不是一个“坏习惯”。这个神话是JSF1.x时代遗留下来的,并且持续太久,因为初学者没有清楚地理解JSTL的生命周期和功能。在这一点上,只有当上述代码片段中的
    {bean.fields}
    后面的模型至少在JSF视图范围内没有改变时,才能使用JSTL。另请参见,使用
    绑定
    到bean属性仍然是一种“糟糕的做法”

    至于
    ,您使用哪种迭代组件并不重要,您甚至可以像最初的问题一样使用
    ,或者使用特定于组件库的迭代组件,例如

    至于收集提交的值,
    #{bean.values}
    应该指向已经预创建的
    映射。一个
    HashMap
    就足够了。如果控件可以设置多个值,则可能需要预填充贴图。然后,您应该使用
    列表
    作为值预填充它。请注意,我希望
    字段#getType()
    枚举
    ,因为这样可以简化Java代码端的处理。然后可以使用
    switch
    语句,而不是讨厌的
    if/else


  • postAddToView
    事件侦听器中以编程方式创建组件:

    
    
    与:

    (注意:不要自己创建
    HtmlForm
    !使用JSF创建的表单,这个表单永远不会
    null

    这保证了在正确的时刻填充树,并使getter不受业务逻辑的影响,并且避免了当
    {bean}
    在比请求范围更广的范围内时出现潜在的“重复组件ID”问题(因此您可以在此处安全地使用视图范围的bean),并且保持bean没有
    UIComponent
    属性,这反过来避免了组件作为可序列化bean的属性保存时可能出现的序列化问题和内存泄漏

    如果您仍然在JSF 1.x上,
    不可用,请通过
    绑定将表单组件绑定到请求(而不是会话!)范围的bean

    
    
    然后懒洋洋地将其填充到表单的getter中:

    public HtmlForm getForm() {
        if (form == null) {
            form = new HtmlForm();
            // ... (continue with code as above)
        }
        return form;
    }
    
    当使用
    绑定时
    ,理解UI组件基本上是请求范围的,绝对不应该被分配为更大范围内bean的属性,这一点非常重要。另见


  • 使用自定义渲染器创建自定义组件。我不打算发布完整的示例,因为这是一个非常紧密耦合的、特定于应用程序的混乱的大量代码


  • 每种选择的利弊都应该清楚。它从最容易维护到最难维护,再从最不可重用到最可重用。由您选择最适合您的功能需求和当前情况的产品

    应该注意的是,只有在Java中才有可能实现的东西是绝对的
    public void populateForm(ComponentSystemEvent event) {
        HtmlForm form = (HtmlForm) event.getComponent();
        for (Field field : fields) {
            switch (field.getType()) { // It's easiest if it's an enum.
                case TEXT:
                    UIInput input = new HtmlInputText();
                    input.setId(field.getName()); // Must be unique!
                    input.setValueExpression("value", createValueExpression("#{bean.values['" + field.getName() + "']}", String.class));
                    form.getChildren().add(input);
                    break;
                case SECRET:
                    UIInput input = new HtmlInputSecret();
                    // etc...
            }
        }
    }
    
    public HtmlForm getForm() {
        if (form == null) {
            form = new HtmlForm();
            // ... (continue with code as above)
        }
        return form;
    }