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:
@FragmentDescriptor("address-fragment.xml")
public class AddressFragment extends Fragment<FormLayout> {
}
<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:
<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 using the Fragments
bean, for example:
<view xmlns="http://jmix.io/schema/flowui/view">
<layout>
<details id="addressDetails" summaryText="Address" opened="true" alignSelf="STRETCH"/>
</layout>
</view>
@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. |
If a fragment subscribes to a host view event, the fragment must be created and added to the view before this event is triggered. |
Passing Parameters to Fragments
A fragment controller can have public setters to accept parameters as it is done when opening views, for example:
@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:
<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 |
If the fragment is created programmatically, setters can be invoked explicitly:
@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:
<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>
@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.
<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. |
<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"/>