Using Screen Fragments

This section explains how to define and use screen fragments. See also ScreenFragment Events for how to handle fragment lifecycle events.

Declarative Usage

Suppose we have a fragment for entering an address.

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>

Then we can include it to another screen using the fragment element with the screen attribute pointing to the fragment id, specified in its @UiController annotation:

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>

The fragment element can be added to any UI container of the screen, including the top-level layout element.

Programmatic Usage

The same fragment can be included in the screen programmatically in a InitEvent or AfterInitEvent handler as follows:

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 Injects the Fragments bean which is designed to instantiate screen fragments.
2 Creates the fragment’s controller by its class.
3 Gets the Fragment visual component from the controller and adds it to a UI container.
If a 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 and AfterInitEvent 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, for example:

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.

If the fragment is opened programmatically, the setters can be invoked explicitly:

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 Passes a parameter before adding the fragment to the screen.

If the fragment is added to the screen declaratively in XML, use the properties element to pass the parameters, for example:

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 Passes a string parameter to the setZipcode() method.
2 Passes a data container to the setAddressContainer() method.

Use the value attribute to specify values and the ref attribute to specify identifiers of the screen components. Setters must have parameters of appropriate types.

Using Data Components

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 Country 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://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>

When the host screen is opened, data will be loaded in the fragment.

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://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 Data container that is used in the fragment below.
address-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" means that the container with the same id must exist in a host screen or enclosing fragment.
2 UI component is linked to the provided data container.

In the XML element having provided="true", all attributes except id are ignored but can be present to provide information for design time tools.

A fragment can also define loaders, provided by a host screen. A provided loader must have an id equal to the loader of the host screen, and provided="true" attribute. For example:

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