Использование фрагментов экранов

В данном разделе рассматриваются примеры определения и использования фрагментов экранов. См. также раздел События ScreenFragment для получения информации о событиях жизненного цикла фрагментов.

Декларативное использование

Предположим, имеется фрагмент для ввода адреса:

AddressFragment.java
@UiController("sample_AddressFragment")
@UiDescriptor("address-fragment.xml")
public class AddressFragment extends ScreenFragment {
}
address-fragment.xml
<fragment xmlns="http://jmix.io/schema/ui/fragment">
    <layout>
        <textField id="cityField" caption="City"/>
        <textField id="zipField" caption="Zip"/>
    </layout>
</fragment>

Он может быть включен в некоторый экран с помощью элемента fragment с атрибутом screen, указывающим на id фрагмента, который задан в аннотации @UiController:

host-screen.xml
<window xmlns="http://jmix.io/schema/ui/window"
        caption="msg://hostScreen.caption">
    <layout>
        <groupBox id="addressBox" caption="Address">
            <fragment screen="sample_AddressFragment"/>
        </groupBox>
    </layout>
</window>

Элемент fragment может быть добавлен в любой UI-контейнер экрана, в том числе в корневой элемент layout.

Программное использование

Тот же самый фрагмент может быть включен в экран программно в обработчике InitEvent или AfterInitEvent как показано ниже:

host-screen.xml
<window xmlns="http://jmix.io/schema/ui/window"
        caption="msg://hostScreen.caption">
    <layout>
        <groupBox id="addressBox" caption="Address"/>
    </layout>
</window>
HostScreen.java
@UiController("sample_HostScreen")
@UiDescriptor("host-screen.xml")
public class HostScreen extends Screen {

    @Autowired
    private Fragments fragments; (1)

    @Autowired
    private GroupBoxLayout addressBox;

    @Subscribe
    private void onInit(InitEvent event) {
        AddressFragment addressFragment = fragments.create(this, AddressFragment.class); (2)
        addressBox.add(addressFragment.getFragment()); (3)
    }
}
1 Инжектирование бина Fragments, который предназначен для инстанциирования фрагментов.
2 Создание экземпляра контроллера фрагмента по его классу.
3 Получение визуального компонента Fragment из контроллера и добавление его в UI-контейнер.
Если фрагменту нужны какие-либо параметры, установите их через публичные сеттеры перед добавлением фрагмента в экран. Тогда параметры будут доступны в обработчиках событий InitEvent и AfterInitEvent контроллера фрагмента.

Передача параметров в фрагменты

Контроллер фрагмента может иметь публичные сеттеры для получения параметров, как это делается при открытии экранов. Например:

AddressFragment.java
@UiController("sample_AddressFragment")
@UiDescriptor("address-fragment.xml")
public class AddressFragment extends ScreenFragment {

    @Autowired
    private TextField<String> zipcodeField;

    private String zipcode;

    public void setZipcode(String zipcode) { (1)
        this.zipcode = zipcode;
    }

    public void setAddressContainer(InstanceContainer<Address> dataContainer) { (1)
        //
    }

    @Subscribe
    public void onInit(InitEvent event) {
        zipcodeField.setInputPrompt(zipcode); (2)
    }

}
1 Setter methods allows you to pass parameters to the screen fragment.
2 An example of parameters usage.

Если фрагмент открывается программно, то сеттеры можно вызвать явно:

HostScreen.java
@Autowired
private Fragments fragments;

@Autowired
private GroupBoxLayout addressBox;

@Subscribe
private void onInit(InitEvent event) {
    AddressFragment addressFragment = fragments.create(this, AddressFragment.class);
    addressFragment.setZipcode("2779001"); (1)
    addressBox.add(addressFragment.getFragment());
}
1 Передача параметра перед добавлением фрагмента в экран.

Если фрагмент добавляется в экран декларативно в XML, для передачи параметров можно использовать элемент properties, например:

host-screen.xml
<fragment id="addressFragment" screen="sample_AddressFragment">
    <properties>
        <property name="zipcode" value="2779001"/> (1)
        <property name="addressContainer" ref="addressDc"/> (2)
    </properties>
</fragment>
1 Передача строкового параметра в метод setZipcode().
2 Передача контейнера данных в метод setAddressContainer().

Атрибут value используется для указания значений, атрибут ref – для указания идентификаторов компонентов экрана. Сеттеры должны иметь параметры подходящего типа.

Компоненты данных в фрагментах

Фрагмент экрана может иметь свои собственные контейнеры и загрузчики данных, определенные в XML-элементе data. В то же время, фреймворк создает единственный экземпляр DataContext для экрана и всех его фрагментов. Поэтому все загруженные сущности помещаются в один контекст и их изменения сохраняются, когда экран выполняет коммит.

Далее рассматривается пример использования собственных контейнеров и загрузчиков в фрагменте.

Предположим, имеется сущность Country, и во фрагменте вместо текстового поля необходимо отобразить выпадающий список с имеющимися городами. Во фрагменте можно определить компоненты данных точно так же, как в обычном экране:

address-fragment.xml
<fragment xmlns="http://jmix.io/schema/ui/fragment">
    <data>
        <collection id="countriesDc" class="ui.ex1.entity.Country">
            <fetchPlan extends="_base"/>
            <loader id="countriesDl">
                <query>
                    <![CDATA[select e from uiex1_Country e]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
    </facets>
    <layout>
        <entityComboBox id="countryField" caption="Country" optionsContainer="countriesDc"/>
        <textField id="zipField" caption="Zip"/>
    </layout>
</fragment>

Когда откроется включающий экран, данные будут загружены в фрагмент.

Контейнеры данных, предоставляемые экраном

Следующий пример демонстрирует использование контейнеров данных, предоставляемых включающим экраном.

host-screen.xml
<window xmlns="http://jmix.io/schema/ui/window"
        caption="msg://hostScreen.caption">
    <data>
        <instance id="addressDc"
                  class="ui.ex1.entity.Address"> (1)
            <fetchPlan extends="_base"/>
            <loader/>
        </instance>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
    </facets>
    <layout>
        <groupBox id="addressBox" caption="Address">
            <fragment screen="sample_AddressFragment"/>
        </groupBox>
    </layout>
</window>
1 Контейнер данных, который используется фрагментом ниже.
addreess-fragment.xml
<fragment xmlns="http://jmix.io/schema/ui/fragment">
    <data>
        <instance id="addressDc" class="ui.ex1.entity.Address"
                  provided="true"/> (1)
        <collection id="countriesDc" class="ui.ex1.entity.Country" provided="true"/>
    </data>
    <layout>
        <entityComboBox id="countryField" caption="Country" optionsContainer="countriesDc"
                        dataContainer="addressDc" property="country"/> (2)
        <textField id="streetField"
                   property="street"/>
        <textField id="zipcodeField"
                   property="zipcode"/>
    </layout>
</fragment>
1 provided="true" означает, что контейнер с таким же id должен существовать во включающем экране или фрагменте, т.е. должен быть предоставлен извне.
2 UI-компоненты соединены с предоставленным контейнером данных.

В XML-элементе, имеющем provided="true", все атрибуты за исключением id игнорируются, но могут присутствовать для обеспечения работы инструментов разработки.

Во фрагменте могут быть также использованы загрузчики данных, предоставляемый включающим экраном. Такой загрузчик должен иметь атрибут id, соответствующий загрузчику включающего экрана, и атрибут provided="true". Например:

<loader id="addressDl" provided="true"/>