3.5.1.4. Using Screen Fragments
In this section, we explain how to define and use screen fragments.
- Declarative usage of a fragment
-
Suppose we have a fragment for entering an address:
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>
Then we can include it to another screen using the
fragment
element with thescreen
attribute pointing to the fragment id, specified in its@UiController
annotation: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>
The
fragment
element can be added to any UI-container of the screen, including the top-levellayout
element.
- Programmatic usage of a fragment
-
The same fragment can be included in the screen programmatically in a InitEvent or AfterInitEvent handler as follows:
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 - inject the Fragments
bean which is designed to instantiate screen fragments2 - create the fragment’s controller by its class 3 - initialize the screen fragment 4 - get the Fragment
visual component from the controller and add it to a UI-containerDo not forget to initialize the screen fragment using the
init()
method after programmatic creation. If the fragment has parameters, you can set them via public setters prior to invokinginit()
. Then the parameters will be available inInitEvent
andAfterInitEvent
handlers of the fragment controller.
- Data components in screen fragments
-
A screen fragment can have its own data containers and loaders, defined in the
data
XML element. At the same time, the framework creates a single instance of DataContext for the screen and all its fragments. Therefore all loaded entities are merged to the same context and their changes are saved when the host screen is committed.In the following example, we consider usage of own data containers and loaders in a screen fragment.
Suppose we have a
City
entity and in the fragment, instead of the text field, we want to show a drop-down list with available cities. We can define data components in the fragment descriptor as we would in a regular screen: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>
In order to load data in the fragment when the host screen is opened, we need to subscribe to the screen’s event:
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 - subscribing to BeforeShowEvent of the host screen The
@LoadDataBeforeShow
annotation does not work for screen fragments.
- Provided data containers
-
The next example demonstrates how to use data containers of the host screen in the fragment.
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 - data container which is used in the fragment below 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"
means that the container with the same id must exist in a host screen or enclosing fragment, i.e it must be provided from outside2 - UI-components are linked to the provided data container In the XML element having
provided="true"
, all attributes exceptid
are ignored but can be present to provide information for design time tools.
- Subscribing to host screen events
-
In a fragment controller, you can subscribe to events of the host screen by specifying the
PARENT_CONTROLLER
value in thetarget
attribute of the annotation, for example:@Subscribe(target = Target.PARENT_CONTROLLER) private void onBeforeShowHost(Screen.BeforeShowEvent event) { // }
Any event can be handled this way, including InitEntityEvent sent by entity editors.