如何创建动态JSF表单字段 [英] How to create dynamic JSF form fields

查看:119
本文介绍了如何创建动态JSF表单字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have found some similar questions like this one, however there are so many ways this can be done that it made me more confused.

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

We are getting an XML file that we are reading. This XML contains information on some form fields that needs to be presented.

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

So I created this custom DynamicField.java that has all the information we need:

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.
}

所以我们有一个List<DynamicField>.

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

I want to iterate through this list and populate the form fields so it looks something like this:

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

然后<my:someCustomComponent>将返回适当的JSF表单组件(即label,inputText)

The <my:someCustomComponent> would then return the appropriate JSF form components (i.e. label, inputText)

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

Another approach would be to just display the <my:someCustomComponent> and then that would return an HtmlDataTable with form elements. (I think this is maybe easier to do).

哪种方法最好?有人可以向我显示一些链接或代码,以显示如何创建此链接吗?我更喜欢完整的代码示例,而不喜欢您需要javax.faces.component.UIComponent的子类"之类的答案.

Which approach is best? Can someone show me to some links or code where it shows how I can create this? I prefer complete code examples, and not answers like "You need a subclass of javax.faces.component.UIComponent".

推荐答案

由于起源实际上不是XML,而是Javabean,因此另一个答案不值得被编辑成完全不同的形式风味(它可能仍然对其他人将来的参考很有用),我将基于Javabean起源添加另一个答案.

当源是Javabean时,基本上可以看到三个选项.

I see basically three options when the origin is a Javabean.

  1. 使用JSF rendered属性或什至JSTL <c:choose>/<c:if>标签来有条件地渲染或构建所需的组件.下面是使用rendered属性的示例:

  1. Make use of JSF rendered attribute or even JSTL <c:choose>/<c:if> tags to conditionally render or build the desired component(s). Below is an example using rendered attribute:

<ui:repeat value="#{bean.fields}" var="field">
    <div class="field">
        <h:inputText value="#{bean.values[field.name]}" rendered="#{field.type == 'TEXT'}" />
        <h:inputSecret value="#{bean.values[field.name]}" rendered="#{field.type == 'SECRET'}" />
        <h:inputTextarea value="#{bean.values[field.name]}" rendered="#{field.type == 'TEXTAREA'}" />
        <h:selectOneRadio value="#{bean.values[field.name]}" rendered="#{field.type == 'RADIO'}">
            <f:selectItems value="#{field.options}" />
        </h:selectOneRadio>
        <h:selectOneMenu value="#{bean.values[field.name]}" rendered="#{field.type == 'SELECTONE'}">
            <f:selectItems value="#{field.options}" />
        </h:selectOneMenu>
        <h:selectManyMenu value="#{bean.values[field.name]}" rendered="#{field.type == 'SELECTMANY'}">
            <f:selectItems value="#{field.options}" />
        </h:selectManyMenu>
        <h:selectBooleanCheckbox value="#{bean.values[field.name]}" rendered="#{field.type == 'CHECKONE'}" />
        <h:selectManyCheckbox value="#{bean.values[field.name]}" rendered="#{field.type == 'CHECKMANY'}">
            <f:selectItems value="#{field.options}" />
        </h:selectManyCheckbox>
    </div>
</ui:repeat>

可以在>如何使网格成为JSF复合组件?不,JSTL绝对不是坏习惯".这个神话是JSF 1.x时代遗留下来的,并且持续了太长时间,因为初学者并没有清楚地了解JSTL的生命周期和功能.到目前为止,仅当至少在上述JSF视图范围内#{bean.fields}后面的模型(如上述代码片段)没有发生变化时,才可以使用JSTL.另请参见 JSF2 Facelets中的JSTL ...有意义吗?相反,使用binding转换为bean属性仍然是坏习惯".

An example of JSTL approach can be found at How to make a grid of JSF composite component? No, JSTL is absolutely not a "bad practice". This myth is a leftover from JSF 1.x era and continues too long because starters didn't clearly understand the lifecycle and powers of JSTL. To the point, you can use JSTL only when the model behind #{bean.fields} as in above snippet does not ever change during at least the JSF view scope. See also JSTL in JSF2 Facelets... makes sense? Instead, using binding to a bean property is still a "bad practice".

对于<ui:repeat><div>,实际上使用什么迭代组件都没有关系,甚至可以像在最初的问题中那样使用<h:dataTable>,也可以使用特定于组件库的迭代组件,例如<p:dataGrid><p:dataList>. 必要时进行重构代码到包含文件或标记文件.

As to the <ui:repeat><div>, it really doesn't matter which iterating component you use, you can even use <h:dataTable> as in your initial question, or a component library specific iterating component, such as <p:dataGrid> or <p:dataList>. Refactor if necessary the big chunk of code to an include or tagfile.

关于收集提交的值,#{bean.values}应该指向已经预先创建的Map<String, Object>. HashMap就足够了.如果控件可以设置多个值,则可能需要预填充地图.然后,您应该使用List<Object>作为值对其进行预填充.请注意,我希望Field#getType()enum,因为这样可以简化Java代码端的处理.然后,您可以使用switch语句代替讨厌的if/else块.

As to collecting the submitted values, the #{bean.values} should point to a Map<String, Object> which is already precreated. A HashMap suffices. You may want to prepopulate the map in case of controls which can set multiple values. You should then prepopulate it with a List<Object> as value. Note that I expect the Field#getType() to be an enum since that eases the processing in the Java code side. You can then use a switch statement instead of a nasty if/else block.

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

Create the components programmatically in a postAddToView event listener:

<h:form id="form">
    <f:event type="postAddToView" listener="#{bean.populateForm}" />
</h:form>

使用:

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...
        }
    }
}

(注意:不要自己创建HtmlForm!请使用JSF创建的一个,永远不要null)

(note: do NOT create the HtmlForm yourself! use the JSF-created one, this one is never null)

这保证了在正确的时机填充树,并使getter不受业务逻辑的影响,并且避免了当#{bean}的作用域比请求作用域更广时,潜在的重复组件ID"麻烦(因此您可以安全地使用此处的例如范围为视图的Bean),并使Bean不含UIComponent属性,当组件作为可序列化的bean的属性保存时,这又避免了潜在的序列化麻烦和内存泄漏.

This guarantees that the tree is populated at exactly the right moment, and keeps getters free of business logic, and avoids potential "duplicate component ID" trouble when #{bean} is in a broader scope than the request scope (so you can safely use e.g. a view scoped bean here), and keeps the bean free of UIComponent properties which in turn avoids potential serialization trouble and memory leaking when the component is held as a property of a serializable bean.

如果您仍在使用<f:event>不可用的JSF 1.x上,请通过binding

If you're still on JSF 1.x where <f:event> is not available, instead bind the form component to a request (not session!) scoped bean via binding

<h:form id="form" binding="#{bean.form}" />

然后用表格的getter懒惰地填充它:

And then lazily populate it in the getter of the form:

public HtmlForm getForm() {
    if (form == null) {
        form = new HtmlForm();
        // ... (continue with code as above)
    }
    return form;
}

使用binding时,了解UI组件基本上是请求范围的,并且绝对不应在更广泛的范围内被分配为Bean的属性,这一点非常重要.另请参见 JSF的'binding'属性如何工作?何时以及如何使用?

When using binding, it's very important to understand that UI components are basically request scoped and should absolutely not be assigned as a property of a bean in a broader scope. See also How does the 'binding' attribute work in JSF? When and how should it be used?

使用自定义渲染器创建自定义组件.我将不发布完整的示例,因为其中有很多代码,毕竟这将是非常紧密的耦合和特定于应用程序的混乱.

Create a custom component with a custom renderer. I am not going to post complete examples since that's a lot of code which would after all be a very tight-coupled and application-specific mess.

每个选项的利弊都应该清楚.它从最容易和最佳可维护性到最困难和最不可维护性,随后也从最少可重用性变为最佳可重用性.您可以选择最适合您的功能要求和当前状况的东西.

Pros and cons of each option should be clear. It goes from most easy and best maintainable to most hard and least maintainable and subsequently also from least reuseable to best reuseable. It's up to you to pick whatever the best suits your functional requirement and current situation.

请注意,在Java(方法2)中,完全没有什么,这是唯一的,而在XHTML + XML中则是(<方法#1). XHTML + XML和Java一样,一切皆有可能.许多新手在动态创建组件时低估了XHTML + XML(尤其是<ui:repeat>和JSTL),并错误地认为Java是唯一的"方式,而通常只会使代码变得脆弱而混乱.

Noted should be that there is absolutely nothing which is only possible in Java (way #2) and impossible in XHTML+XML (way #1). Everything is possible in XHTML+XML as good as in Java. A lot of starters underestimate XHTML+XML (particularly <ui:repeat> and JSTL) in dynamically creating components and incorrectly think that Java would be the "one and only" way, while that generally only ends up in brittle and confusing code.

这篇关于如何创建动态JSF表单字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆