Использование фрагментов
Этот раздел объясняет, как определять и использовать фрагменты. См. также раздел События фрагмента.
Декларативное использование
Предположим, у вас есть фрагмент для ввода адреса:
@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>
Фрагмент можно создать с помощью мастера создания экранов в Студии. Выберите шаблон Blank fragment из списка шаблонов экранов.
|
Фрагмент можно включить в экран, используя элемент fragment
с атрибутом class
, в котором указан 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
может быть добавлен к любому UI-контейнеру экрана, включая элемент layout
верхнего уровня.
В визуальном дизайнере Studio используйте действие Add Component, чтобы открыть палитру компонентов. Найдите в ней компонент Fragment и перетащите его в структуру компонентов или в XML.
|
Программное использование
Этот же фрагмент может быть включен в экран программно используя бин Fragments
, например:
<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 , который предназначен для создания фрагментов. |
2 | Создание экземпляра фрагмента по его классу. |
3 | Добавление экземпляра фрагмента в компонент Details . |
Если фрагмент подписан на событие хост-экрана, то фрагмент должен быть создан и добавлен в экран перед тем, как это событие будет вызвано. |
Передача параметров в фрагменты
Контроллер фрагмента может иметь публичные сеттеры для принятия параметров, как это делается при открытии экранов, например:
@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 | Сеттеры позволяют передавать параметры во фрагмент. |
Если фрагмент добавляется в экран декларативно в 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
. Сеттеры должны иметь параметры соответствующих типов.
Для добавления параметра в Jmix Studio выберите |
Если фрагмент создан программным способом, сеттеры могут быть вызваны явно:
@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 | Перед добавлением в экран в созданный фрагмент передается параметр. |
Использование компонентов данных
Фрагмент может иметь собственные контейнеры и загрузчики данных, определенные в элементе data
XML. В то же время фреймворк создает единственный экземпляр DataContext для экрана и всех его фрагментов. Следовательно, все загруженные сущности объединяются в один контекст, и их изменения сохраняются при сохранении данных включающего экрана.
Следующий пример демонстрирует использование собственных контейнеров данных и загрузчиков во фрагменте.
Предположим, у вас есть сущность City
, и вы хотите показать выпадающий список с доступными городами. Вы можете определить компоненты данных в дескрипторе фрагмента так же, как и в обычном экране:
<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 | Фрагмент подписывается на BeforeShowEvent включающего экрана, поэтому данные фрагмента будут загружены при открытии экрана. |
Предоставляемые компоненты данных
Следующий пример демонстрирует, как использовать контейнеры данных включающего экрана во фрагменте.
<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 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" означает, что контейнер с тем же идентификатором должен существовать во включающем экране или фрагменте. |
2 | Компонент UI связан с предоставленным контейнером данных. |
В XML-элементе с атрибутом provided="true"
игнорируются все атрибуты, кроме id, однако они могут присутствовать для предоставления информации IDE.
Фрагмент может определять и загрузчики, предоставляемые включающим экраном. Предоставленный загрузчик должен иметь идентификатор, равный идентификатору загрузчика экрана, и атрибут provided="true"
. Например:
<loader id="addressDl" provided="true"/>