3.5.2.1.15. 字段组

FieldGroup 用来集中显示和编辑实体的多个属性。

gui fieldGroup

该组件对应的 XML 名称: fieldGroup

FieldGroup 只能在基于历史 API 的界面中使用。当前 API 中通过 Form 组件提供类似功能。

下面这个例子展示了在 XML 中定义一组字段的情况:

<dsContext>
    <datasource id="orderDs"
                class="com.sample.sales.entity.Order"
                view="order-with-customer">
    </datasource>
</dsContext>
<layout>
    <fieldGroup id="orderFieldGroup" datasource="orderDs" width="250px">
        <field property="date"/>
        <field property="customer"/>
        <field property="amount"/>
    </fieldGroup>
</layout>

在上面这个例子中,dsContext 定义了一个包含单个实体 Order数据源 orderDs。这里用 fieldGroup 组件的 datasource 属性来定义这个数据源。XML 元素 field 定义了那些需要显示在界面上的实体属性。

fieldGroup 的 XML 元素:

  • column – 可选元素,用来把字段放到多个列显示。为了达到这样的效果,field 元素不能直接放在 fieldGroup 元素里面,而需要放在一个 column 元素里,比如:

    <fieldGroup id="orderFieldGroup" datasource="orderDs" width="100%">
        <column width="250px">
            <field property="num"/>
            <field property="date"/>
            <field property="amount"/>
        </column>
        <column width="400px">
            <field property="customer"/>
            <field property="info"/>
        </column>
    </fieldGroup>

    这样的话,字段会被排成两列;第一列包含的几个字段的宽度会是 250px,第二列几个字段的宽度会是 400px

    column 元素的属性:

    • width – 定义列中的字段宽度。默认的字段宽度是 200px。这里可以采用像素或者整个列宽的百分比来定义。

    • flex – 伸缩率,定义当 fieldGroup 整体的宽度发生变化时,此列相对于其它列水平伸缩的程度。比如,可以定义一列的 flex=1,另一列的 flex=3

    • id – 列 id,可选,在做界面扩展的时候会用到。

  • field – 主要的组件元素,定义组件的一个字段。

    自定义的字段也可以放在 field 元素里:

    <fieldGroup>
        <field id="demo">
            <lookupField id="demoField" datasource="userDs" property="group"/>
        </field>
    </fieldGroup>

    field 元素的 XML 属性:

    • id – 如果 property 没设置,那么必须设置 id;如果 property 设置了,那么 id 默认跟 property 取一样的值。id 属性需要使用唯一的标识符,要么是 property 定义的字段名,要么是通过编程的方式定义的一个字段。如果是采取编程方式定义,那么 field 也需要有 custom="true" (参阅下面 custom 的说明)

    • property - 如果 id 没设置,那么必须设置此属性;这个属性的值必须是一个实体属性的名称,用来显示这个绑定的字段。

    • caption − 定义字段的显示名称。如果没设置的话,则会显示实体的属性本地化名称

    • inputPrompt - 如果这个字段使用的组件支持 inputPrompt 属性的话,这里可以直接设置这个属性的值。

    • visible − 通过这个属性控制是否显示这个字段及其名称(caption)。

    • datasource − 可以设置该字段单独的数据源,而不用整个 fieldGroup 组件的数据源。这样的话,一个 fieldGroup 就可以显示来自不同实体的属性了。

    • optionsDatasource 定义了一个用来做选项列表的数据源名称。可以给实体属性关联的字段定义选项数据源。默认情况下,选择关联实体的时候是通过一个查找界面来操作。但是如果 optionsDatasource 属性设置了,则可以通过下拉列表来选择。也就是说,其实设置这个属性会导致原本默认的 LookupPickerField 会被 PickerField 替换掉。

    • width − 设置字段宽度,不包括显示名称。默认是 200px。宽度值可以是像素值或者整个列宽的百分比。需要同时设置一列中所有的字段统一宽度,可以通过设置上面提到过的 columnwidth 属性。

    • custom – 如果设置成 true,表示这个字段不关联实体的属性,也不关联一个对应的组件。然后这个字段需要通过 FieldGroupsetComponent() 方法以编程方式实现,具体可以参考下面对于 setComponent() 的解释。

    • generator 属性用来以声明的方式创建自定义字段,需要设置这个属性的值为一个可以返回自定义组件的方法名:

      <fieldGroup datasource="productDs">
          <column width="250px">
              <field property="description" generator="generateDescriptionField"/>
          </column>
      </fieldGroup>
      public Component generateDescriptionField(Datasource datasource, String fieldId) {
          TextArea textArea = uiComponents.create(TextArea.NAME);
          textArea.setRows(5);
          textArea.setDatasource(datasource, fieldId);
          return textArea;
      }
    • linkScreen - 当 link 属性设置成 true,用来定义点击链接时需要打开的界面的标识符。

    • linkScreenOpenType - 定义界面打开的类型(THIS_TABNEW_TAB 或者 DIALOG)。

    • linkInvoke - 定义一个控制器方法,在点击链接时调用这个方法,而不是打开界面。

    根据需要显示的实体属性类型的不同,可以使用下面这些 field 的属性:

    • mask 如果给一个文本型的实体属性设置这个 mask 属性,那么界面组件会用 MaskedField 替换 TextField,并使用适当的掩码,此时也可以设置 valueMode 属性。

    • rows 如果给一个文本型的实体属性设置这个 rows 属性,那么界面组件会用 TextArea 替换 TextField,并将文字重组成适当的行数,此时也可以设置 cols 属性。

    • maxLength 对于文本型实体属性,可以定义 maxLength 属性,跟 TextField 中描述的一致。

    • dateFormat 对于 date 或者 dateTime 类型的实体属性,可以设置 dateFormatresolution 参数,使用的组件是 DateField

    • showSeconds 如果实体属性是 time 类型,可以设置界面组件 TimeFieldshowSeconds 属性。

fieldGroup 的 XML 属性:

  • border 属性可以设置成 hidden 或者 visible。默认值是 hidden。如果设置成 visiblefieldGroup 组件会有边框(border)而且会被高亮。在 web 的实现中,通过添加 cuba-fieldgroup-border 这个 CSS 类显示边框。

  • captionAlignment 属性定义 FieldGroup 内字段的名称与字段的相对位置。可选项:LEFTTOP

  • fieldFactoryBean 在 XML 描述里面,声明式的字段默认是通过 FieldGroupFieldFactory 接口创建的。可以使用这个属性,覆盖默认工厂,将此属性设置成自定义 FieldGroupFieldFactory 实现的名称。

    通过编程的方式创建 FieldGroup 的话,可以使用 setFieldFactory() 方法。

FieldGroup 接口的方法:

  • addField 在运行时将字段添加到 FieldGroup。接受 FieldConfig 类型实例作为参数,也可以通过 colIndexrowIndex 参数定义字段的位置。

  • bind()setDatasource() 之后触发的方法,用来为添加的字段绑定相应的 UI 组件。

  • createField() 用来创建新的实现了 FieldConfig 接口的 FieldGroup 元素:

    fieldGroup.addField(fieldGroup.createField("newField"));
  • getComponent() 返回一个跟字段绑定的可视化组件。这个也许在需要设置额外的组件参数时,通过这个方法得到这个可视化组件。因为上面提到的 field 提供的 XML 配置的参数有限。

    在界面控制器中,如果想获取对可视化组件的引用,可以采用注入的方式,而不是通过显式地调用 getFieldNN("id").getComponentNN() 的方式。具体做法是,使用 @Named 注解,提供的注解参数是 fieldGroup 的标识符加 . 再加上字段标识符。

    比如下面例子中,在用一个字段选择关联的实体的时候,可以添加一个 Open 操作,然后删掉这个字段的 Clear 操作:

    <fieldGroup id="orderFieldGroup" datasource="orderDs">
        <field property="date"/>
        <field property="customer"/>
        <field property="amount"/>
    </fieldGroup>
    @Named("orderFieldGroup.customer")
    protected PickerField customerField;
    
    @Override
    public void init(Map<String, Object> params) {
        customerField.addOpenAction();
        customerField.removeAction(customerField.getAction(PickerField.ClearAction.NAME));
    }

    要使用 getComponent() 获取或者注入字段组件,需要知道字段中使用的组件的类型。下面这个表列举了实体的属性类型和组件的对应关系:

    实体属性类型 附加条件 字段可视化组件类型

    关联实体

    指定了 optionsDatasource

    LookupPickerField

    PickerField

    枚举类型 (enum)

    LookupField

    string

    指定了 mask

    MaskedField

    指定了 rows

    TextArea

    TextField

    boolean

    CheckBox

    date, dateTime

    DateField

    time

    TimeField

    int, long, double, decimal

    指定了 mask

    MaskedField

    TextField

    UUID

    MaskedField 16 进制掩码

  • removeField() 支持在运行时根据 id 移除字段.

  • setComponent() 为字段设置自定义的可视化组件。可以在 XML 元素 field 的属性 custom="true" 或者使用 createField() 方法创建字段时使用。当与 custom="true" 一起使用的时候,数据源(datasource)和对应的属性(property)需要手动设置。

    FieldConfig 接口类的实例可以通过 getField() 或者 getFieldNN() 方法获取,然后就可以调用它的 setComponent() 方法:

    @Inject
    protected FieldGroup fieldGroup;
    @Inject
    protected UiComponents uiComponents;
    @Inject
    private Datasource<User> userDs;
    
    @Override
    public void init(Map<String, Object> params) {
        PasswordField passwordField = uiComponents.create(PasswordField.NAME);
        passwordField.setDatasource(userDs, "password");
        fieldGroup.getFieldNN("password").setComponent(passwordField);
    }