3.5.1.4. Использование фрагментов экранов
В данном разделе рассматриваются примеры определения и использования фрагментов экранов.
- Декларативное использование фрагмента
-
Предположим, имеется фрагмент для ввода адреса:
AddressFragment.java@UiController("demo_AddressFragment") @UiDescriptor("address-fragment.xml") public class AddressFragment extends ScreenFragment { }
address-fragment.xml<fragment xmlns="http://schemas.haulmont.com/cuba/screen/fragment.xsd"> <layout> <textField id="cityField" caption="City"/> <textField id="zipField" caption="Zip"/> </layout> </fragment>
Он может быть включен в некоторый экран с помощью элемента
fragment
с атрибутомscreen
, указывающим на id фрагмента, который задан в аннотации@UiController
:host-screen.xml<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="Some Screen"> <layout> <groupBox id="addressBox" caption="Address"> <fragment screen="demo_AddressFragment"/> </groupBox> </layout> </window>
Элемент
fragment
может быть добавлен в любой UI-контейнер экрана, в том числе в корневой элементlayout
.
- Программное использование фрагмента
-
Тот же самый фрагмент может быть включен в экран программно в обработчике InitEvent или AfterInitEvent как показано ниже:
host-screen.xml<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="Some Screen"> <layout> <groupBox id="addressBox" caption="Address"/> </layout> </window>
HostScreen.java@UiController("demo_HostScreen") @UiDescriptor("host-screen.xml") public class HostScreen extends Screen { @Inject private Fragments fragments; (1) @Inject private GroupBoxLayout addressBox; @Subscribe private void onInit(InitEvent event) { AddressFragment addressFragment = fragments.create(this, AddressFragment.class); (2) addressFragment.init(); (3) addressBox.add(addressFragment.getFragment()); (4) } }
1 - инжекция бина Fragments
, который предназначен для инстанциирования фрагментов2 - создание экземпляра контроллера фрагмента по его классу 3 - инициализация фрагмента 4 - получение визуального компонента Fragment
из контроллера и добавление его в UI-контейнерПосле программного создания, не забудьте инициализировать фрагмент экрана используя метод
init()
. Если фрагменту нужны какие-либо параметры, установите их через публичные сеттеры перед вызовомinit()
. Тогда параметры будут доступны в обработчиках событийInitEvent
иAfterInitEvent
контроллера фрагмента.
- Компоненты данных в фрагментах
-
Фрагмент экрана может иметь свои собственные контейнеры и загрузчики данных, определенные в XML-элементе
data
. В то же время, фреймворк создает единственный экземпляр DataContext для экрана и всех его фрагментов. Поэтому все загруженные сущности помещаются в один контекст и их изменения сохраняются, когда экран выполняет коммит.Далее рассматривается пример использования собственных компонентов данных в фрагменте.
Предположим, имеется сущность
City
, и во фрагменте вместо текстового поля необходимо отобразить выпадающий список с имеющимися городами. Во фрагменте можно определить компоненты данных точно так же, как в обычном экране:address-fragment.xml<fragment xmlns="http://schemas.haulmont.com/cuba/screen/fragment.xsd"> <data> <collection id="citiesDc" class="com.company.demo.entity.City" view="_base"> <loader id="citiesLd"> <query><![CDATA[select e from demo_City e ]]></query> </loader> </collection> </data> <layout> <lookupField id="cityField" caption="City" optionsContainer="citiesDc"/> <textField id="zipField" caption="Zip"/> </layout> </fragment>
Для того, чтобы загрузить данные в момент открытия включающего экрана, необходимо подписаться на событие экрана:
AddressFragment.java@UiController("demo_AddressFragment") @UiDescriptor("address-fragment.xml") public class AddressFragment extends ScreenFragment { @Inject private CollectionLoader<City> citiesLd; @Subscribe(target = Target.PARENT_CONTROLLER) (1) private void onBeforeShowHost(Screen.BeforeShowEvent event) { citiesLd.load(); } }
1 - подписка на BeforeShowEvent включающего экрана Аннотация
@LoadDataBeforeShow
в фрагментах экранов не действует.
- Контейнеры данных, предоставляемые экраном
-
Следующий пример демонстрирует использование контейнеров данных, предоставляемых включающим экраном.
host-screen.xml<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="Some Screen"> <data> <instance id="addressDc" class="com.company.demo.entity.Address"/> (1) </data> <layout> <groupBox id="addressBox" caption="Address"> <fragment screen="demo_AddressFragment"/> </groupBox> </layout> </window>
1 - контейнер данных, который используется фрагментом ниже address-fragment.xml<fragment xmlns="http://schemas.haulmont.com/cuba/screen/fragment.xsd"> <data> <instance id="addressDc" class="com.company.demo.entity.Address" provided="true"/> (1) <collection id="citiesDc" class="com.company.demo.entity.City" view="_base"> <loader id="citiesLd"> <query><![CDATA[select e from demo_City e]]></query> </loader> </collection> </data> <layout> <lookupField id="cityField" caption="City" optionsContainer="citiesDc" dataContainer="addressDc" property="city"/> (2) <textField id="zipField" caption="Zip" dataContainer="addressDc" property="zip"/> </layout> </fragment>
1 - provided="true"
означает, что контейнер с таким же id должен существовать во включающем экране или фрагменте, т.е. должен быть предоставлен извне2 - UI-компоненты соединены с предоставленным контейнером данных В XML-элементе, имеющем
provided="true"
, все атрибуты за исключениемid
игнорируются, но могут присутствовать для обеспечения работы инструментов разработки.
- Подписка на события включающего экрана
-
В контроллере фрагмента можно подписаться на события включающего экрана путем указания значения
PARENT_CONTROLLER
в атрибутеtarget
аннотации, например:@Subscribe(target = Target.PARENT_CONTROLLER) private void onBeforeShowHost(Screen.BeforeShowEvent event) { // }
Таким способом можно обработать любое событие, в том числе InitEntityEvent, посылаемое экранами редактирования.