Using Fragments

This section explains how to define and use fragments. See also the Fragment Events section.

Declarative Usage

Suppose you have a fragment for entering an address:

AddressFragment.java
@FragmentDescriptor("address-fragment.xml")
public class AddressFragment extends Fragment<FormLayout> {
}
address-fragment.xml
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
    <content>
        <formLayout>
            <textField id="cityField" label="City"/>
            <textField id="zipcodeField" label="Zipcode"/>
        </formLayout>
    </content>
</fragment>
A fragment can be created using the View Creation Wizard in Studio. Select the Blank fragment template from the view templates list.

Then you can include it in a view using the fragment element with the class attribute specifying the fragment class FQN:

host-view.xml
<view xmlns="http://jmix.io/schema/flowui/view">
    <layout>
        <details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH">
            <fragment class="com.company.onboarding.view.address.var1.AddressFragment"/>
        </details>
    </layout>
</view>

The fragment element can be added to any layout component of the view, including the top-level layout element.

In Studio View Designer, use the Add Component action to open the component palette. Find the Fragment component in it, then drag and drop it into the component structure or into the XML.

Programmatic Usage

The same fragment can be included in a view programmatically in a InitEvent, BeforeShowEvent or ReadyEvent handler as follows:

host-view.xml
<view xmlns="http://jmix.io/schema/flowui/view">
    <layout>
        <details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH"/>
    </layout>
</view>
HostView.java
@Route(value = "HostView", layout = MainView.class)
@ViewController("HostView")
@ViewDescriptor("host-view.xml")
public class HostView extends StandardView {

    @ViewComponent
    private Details addressDetails;

    @Autowired
    private Fragments fragments; (1)

    @Subscribe
    public void onInit(InitEvent event) {
        AddressFragment addressFragment = fragments.create(this, AddressFragment.class); (2)
        addressDetails.add(addressFragment); (3)
    }
}
1 Injects the Fragments bean which is designed to instantiate fragments.
2 Creates the fragment’s instance by its class.
3 Add fragment instance to the Details layout component.

Passing Parameters to Fragments

A fragment controller can have public setters to accept parameters as it is done when opening views, for example:

AddressFragment.java
@FragmentDescriptor("address-fragment.xml")
public class AddressFragment extends Fragment<FormLayout> {

    @ViewComponent
    private EntityComboBox<City> cityField;
    @ViewComponent
    private TypedTextField<String> zipcodeField;

    public void setCitiesContainer(CollectionContainer<City> citiesContainer) { (1)
        cityField.setItems(citiesContainer);
    }

    public void setZipcodePlaceholder(String placeholder) {
        zipcodeField.setPlaceholder(placeholder);
    }
}
1 Setter methods allows you to pass parameters to the fragment.

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

host-view.xml
<view xmlns="http://jmix.io/schema/flowui/view">
    <data>
        <collection id="citiesDc"
                    class="com.company.onboarding.entity.City">
            <fetchPlan extends="_base"/>
            <loader id="citiesDl" readOnly="true">
                <query>
                    <![CDATA[select e from City e]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
    </facets>
    <layout>
        <details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH">
            <fragment class="com.company.onboarding.view.address.var2.AddressFragment">
                <properties>
                    <property name="citiesContainer" value="citiesDc" type="CONTAINER_REF"/> (1)
                    <property name="zipcodePlaceholder" value="Zipcode"/> (2)
                </properties>
            </fragment>
        </details>
    </layout>
</view>
1 Passes a data container to the setCitiesContainer() method.
2 Passes a string parameter to the setZipcodePlaceholder() method.

Use the value attribute to specify values and the optional type attribute to indicate that the string value must be converted by one of the pluggable PropertyParser beans. Setters must have parameters of appropriate types.

To add a parameter in Jmix Studio, select fragment in the view descriptor XML or in the Jmix UI structure panel and click on the Add→Properties→Property button in the Jmix UI inspector panel.

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

HostView.java
@ViewComponent
private CollectionContainer<City> citiesDc;

@Autowired
private Fragments fragments;

@Subscribe
public void onInit(InitEvent event) {
    AddressFragment addressFragment = fragments.create(this, AddressFragment.class);
    addressFragment.setCitiesContainer(citiesDc); (1)
    addressFragment.setZipcodePlaceholder("Zipcode");
    getContent().add(addressFragment);
}
1 Passes a parameter before adding the fragment to the view.

Using Data Components

A 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 view and all its fragments. Therefore, all loaded entities are merged to the same context, and their changes are saved when the host view saves the data.

The following example demonstrates the usage of own data containers and loaders in a fragment.

Suppose you have a City entity, you want to show a drop-down list with available cities. You can define data components in the fragment descriptor as you would in a regular view:

address-fragment.xml
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
    <data>
        <collection id="citiesDc"
                    class="com.company.onboarding.entity.City">
            <fetchPlan extends="_base"/>
            <loader id="citiesDl" readOnly="true">
                <query>
                    <![CDATA[select e from City e]]>
                </query>
            </loader>
        </collection>
    </data>
    <content>
        <formLayout id="addressForm">
            <entityComboBox id="cityField" label="City" itemsContainer="citiesDc"/>
            <textField id="zipcodeField" label="Zipcode"/>
        </formLayout>
    </content>
</fragment>
AddressFragment.java
@FragmentDescriptor("address-fragment.xml")
public class AddressFragment extends Fragment<FormLayout> {

    @ViewComponent
    private CollectionLoader<City> citiesDl;

    @Subscribe(target = Target.HOST_CONTROLLER)
    protected void onHostBeforeShow(View.BeforeShowEvent event) { (1)
        citiesDl.load();
    }
}
1 The fragment subscribes to the host view BeforeShowEvent, so fragment data will be loaded when the host view is opened.

Provided Data Components

The next example demonstrates how to use data containers of the host view in a fragment.

host-view.xml
<view xmlns="http://jmix.io/schema/flowui/view">
    <data>
        <instance id="addressDc"
                  class="com.company.onboarding.entity.Address"> (1)
            <fetchPlan extends="_base"/>
            <loader/>
        </instance>

        <collection id="citiesDc"
                    class="com.company.onboarding.entity.City">
            <fetchPlan extends="_base"/>
            <loader id="citiesDl" readOnly="true">
                <query>
                    <![CDATA[select e from City e]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
    </facets>
    <layout>
        <details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH">
            <fragment class="com.company.onboarding.view.address.var4.AddressFragment"/>
        </details>
    </layout>
</view>
1 Data container that is used in the fragment below.
address-fragment.xml
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
    <data>
        <instance id="addressDc"
                  class="com.company.onboarding.entity.Address"
                  provided="true"/> (1)

        <collection id="citiesDc"
                    class="com.company.onboarding.entity.City"
                    provided="true"/>
    </data>
    <content>
        <formLayout id="addressForm" dataContainer="addressDc">
            <entityComboBox id="cityField" itemsContainer="citiesDc" property="city"/> (2)
            <textField id="zipcodeField" property="zipcode"/>
        </formLayout>
    </content>
</fragment>
1 provided="true" means that the container with the same id must exist in a host view 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 view. A provided loader must have the same id as a loader of the host view, and provided="true" attribute. For example:

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