3.5.1.4. Using Screen Fragments
In this section, we explain how to define and use screen fragments. See also ScreenFragment Events for how to handle fragment lifecycle events.
- 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) 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 - get the Fragment
visual component from the controller and add it to a UI-containerIf the fragment has parameters, you can set them via public setters prior to adding the fragment to the screen. Then the parameters will be available in
InitEvent
andAfterInitEvent
handlers of the fragment controller.
- Passing parameters to fragments
-
A fragment controller can have public setters to accept parameters as it is done when opening screens. If the fragment is opened programmatically, the setters can be invoked explicitly:
@UiController("demo_HostScreen") @UiDescriptor("host-screen.xml") public class HostScreen extends Screen { @Inject private Fragments fragments; @Inject private GroupBoxLayout addressBox; @Subscribe private void onInit(InitEvent event) { AddressFragment addressFragment = fragments.create(this, AddressFragment.class); addressFragment.setStrParam("some value"); (1) addressBox.add(addressFragment.getFragment()); } }
1 - pass a parameter before adding the fragment to the screen. If the fragment is added to the screen declaratively in XML, use
properties
element to pass the parameters, for example:<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="Some Screen"> <data> <instance id="someDc" class="com.company.demo.entity.Demo"/> </data> <layout> <textField id="someField"/> <fragment screen="demo_AddressFragment"> <properties> <property name="strParam" value="some value"/> (1) <property name="dataContainerParam" ref="someDc"/> (2) <property name="componentParam" ref="someField"/> (3) </properties> </fragment> </layout> </window>
1 - pass a string parameter to setStrParam()
method.2 - pass a data container to setDataContainerParam()
method.3 - pass the TextField
component tosetComponentParam()
method.Use the
value
attribute to specify values and theref
attribute to specify identifiers of the screen components. Setters must have parameters of appropriate types.
- 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.