使用 Fragments
本节介绍如何定义并使用 fragment。以及 Fragment 事件 部分。
声明式使用
假设有一个 fragment 用于输入地址:
@FragmentDescriptor("address-fragment.xml")
public class AddressFragment extends Fragment<FormLayout> {
}
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
<content>
<formLayout>
<textField id="cityField" label="City"/>
<textField id="zipcodeField" label="Zipcode"/>
</formLayout>
</content>
</fragment>
Studio 中可以通过 视图创建向导 创建 fragment,创建时,需选择 Blank fragment 模板。
|
然后可以在视图中使用 fragment
元素引入 fragment,其中 class
属性需要指定为 fragment 类的完全限定名(FQN):
<view xmlns="http://jmix.io/schema/flowui/view">
<layout>
<details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH">
<fragment class="com.company.onboarding.view.address.var1.AddressFragment"/>
</details>
</layout>
</view>
fragment
元素可以添加到视图中的任意布局组件中,包括根元素 layout
。
在 Studio 的 视图设计器 中,通过 Add Component 操作打开组件工具箱。找到 Fragment 组件,拖放至组件结构或 XML 中。
|
编程式使用
Fragment 可以使用 Fragments
bean 通过编程的方式添加到视图中,示例:
<view xmlns="http://jmix.io/schema/flowui/view">
<layout>
<details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH"/>
</layout>
</view>
@Route(value = "HostView", layout = MainView.class)
@ViewController("HostView")
@ViewDescriptor("host-view.xml")
public class HostView extends StandardView {
@ViewComponent
private Details addressDetails;
@Autowired
private Fragments fragments; (1)
@Subscribe
public void onInit(InitEvent event) {
AddressFragment addressFragment = fragments.create(this, AddressFragment.class); (2)
addressDetails.add(addressFragment); (3)
}
}
1 | 注入 Fragments bean 用于初始化 fragment。 |
2 | 使用 fragment 的类进行创建。 |
3 | 将 fragment 实例添加至 `Details`布局组件。 |
如果一个 fragment 订阅了父视图的 事件,则该 fragment 需要在相应的事件触发之前添加到视图中。 |
传递参数
Fragment 控制器可以有 public 的 setter,在打开视图时可以用来接收参数。示例:
@FragmentDescriptor("address-fragment.xml")
public class AddressFragment extends Fragment<FormLayout> {
@ViewComponent
private EntityComboBox<City> cityField;
@ViewComponent
private TypedTextField<String> zipcodeField;
public void setCitiesContainer(CollectionContainer<City> citiesContainer) { (1)
cityField.setItems(citiesContainer);
}
public void setZipcodePlaceholder(String placeholder) {
zipcodeField.setPlaceholder(placeholder);
}
}
1 | setter 方法可以为 fragment 设置参数。 |
如果 fragment 是通过声明式的方式在 XML 中添加的,可以用 properties
元素传参,示例:
<view xmlns="http://jmix.io/schema/flowui/view">
<data>
<collection id="citiesDc"
class="com.company.onboarding.entity.City">
<fetchPlan extends="_base"/>
<loader id="citiesDl" readOnly="true">
<query>
<![CDATA[select e from City e]]>
</query>
</loader>
</collection>
</data>
<facets>
<dataLoadCoordinator auto="true"/>
</facets>
<layout>
<details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH">
<fragment class="com.company.onboarding.view.address.var2.AddressFragment">
<properties>
<property name="citiesContainer" value="citiesDc" type="CONTAINER_REF"/> (1)
<property name="zipcodePlaceholder" value="Zipcode"/> (2)
</properties>
</fragment>
</details>
</layout>
</view>
1 | 传递一个数据容器给 setCitiesContainer() 方法。 |
2 | 传递一个字符串参数给 setZipcodePlaceholder() 方法。 |
通过 value
属性设置值,可选的 type
属性表示值需要通过一个可插拔的 PropertyParser
bean 进行转换。Setter 的参数类型必须匹配。
如需通过 Jmix Studio 添加参数,在视图 XML 或 Jmix UI 层级结构面板中选择 |
如果 fragment 是以编程的方式创建的,那么可以直接调用 setter:
@ViewComponent
private CollectionContainer<City> citiesDc;
@Autowired
private Fragments fragments;
@Subscribe
public void onInit(InitEvent event) {
AddressFragment addressFragment = fragments.create(this, AddressFragment.class);
addressFragment.setCitiesContainer(citiesDc); (1)
addressFragment.setZipcodePlaceholder("Zipcode");
getContent().add(addressFragment);
}
1 | 在添加到视图之前传参。 |
使用数据组件
一个 fragment 可以有自己的数据容器和加载器,在 XML 的 data
元素定义。同时,框架会为视图及其所有的 fragments 创建单一的 DataContext 实例。因此,所有加载出来的实体都合并在一个 context 中,父视图保存数据时一同保存。
下面的示例展示了如何在 fragment 中使用自有的数据容器和加载器。
假设有 City
实体,需要展示包含所有城市的下拉列表。与普通视图一样,可以在 fragment 的 XML 中定义数据组件:
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
<data>
<collection id="citiesDc"
class="com.company.onboarding.entity.City">
<fetchPlan extends="_base"/>
<loader id="citiesDl" readOnly="true">
<query>
<![CDATA[select e from City e]]>
</query>
</loader>
</collection>
</data>
<content>
<formLayout id="addressForm">
<entityComboBox id="cityField" label="City" itemsContainer="citiesDc"/>
<textField id="zipcodeField" label="Zipcode"/>
</formLayout>
</content>
</fragment>
@FragmentDescriptor("address-fragment.xml")
public class AddressFragment extends Fragment<FormLayout> {
@ViewComponent
private CollectionLoader<City> citiesDl;
@Subscribe(target = Target.HOST_CONTROLLER)
protected void onHostBeforeShow(View.BeforeShowEvent event) { (1)
citiesDl.load();
}
}
1 | fragment 订阅父视图的 BeforeShowEvent 事件,当父视图打开时加载 fragment 的数据。 |
使用父数据组件
下面的示例展示如何在 fragment 中使用父视图的数据容器。
<view xmlns="http://jmix.io/schema/flowui/view">
<data>
<instance id="addressDc"
class="com.company.onboarding.entity.Address"> (1)
<fetchPlan extends="_base"/>
<loader/>
</instance>
<collection id="citiesDc"
class="com.company.onboarding.entity.City">
<fetchPlan extends="_base"/>
<loader id="citiesDl" readOnly="true">
<query>
<![CDATA[select e from City e]]>
</query>
</loader>
</collection>
</data>
<facets>
<dataLoadCoordinator auto="true"/>
</facets>
<layout>
<details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH">
<fragment class="com.company.onboarding.view.address.var4.AddressFragment"/>
</details>
</layout>
</view>
1 | 将在下面 fragment 中使用的数据容器。 |
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
<data>
<instance id="addressDc"
class="com.company.onboarding.entity.Address"
provided="true"/> (1)
<collection id="citiesDc"
class="com.company.onboarding.entity.City"
provided="true"/>
</data>
<content>
<formLayout id="addressForm" dataContainer="addressDc">
<entityComboBox id="cityField" itemsContainer="citiesDc" property="city"/> (2)
<textField id="zipcodeField" property="zipcode"/>
</formLayout>
</content>
</fragment>
1 | provided="true" 表示这个 id 的数据容器必须在父视图或 fragment 中存在。 |
2 | UI 与父数据容器关联。 |
在具有 provided="true"
的 XML 元素中,除了 id,其他的属性都会被忽略,但是也可以保留为设计时工具提供信息。
Fragment 也可以定义由父视图提供的数据加载器。其 id 必须与父视图的加载器 id 一致,并使用 provided="true"
属性。示例:
<loader id="addressDl" provided="true"/>