Fragments

A fragment is a UI building block that can be used as a part of views and other fragments. It’s a composite component with the following additional features:

  • Ability to define the content in XML.

  • The fragment XML element allows you to include fragments in views and other fragments declaratively.

  • The fragment class (AKA controller) supports injection of Spring beans and fragment’s UI components, as well as the use of annotated methods as handlers.

  • The ReadyEvent is sent when a fragment is fully initialized.

  • You can define actions and data components (data containers and loaders) in the fragment’s XML. Data components can be marked as provided to obtain them from the host view.

  • Studio offers the Blank fragment template to generate fragments, shows them in the Jmix tool window and allows you to use the visual designer the same as for views.

Fragments can be used to render items within certain UI components, such as virtualList, or dataGrid columns. This is achieved using a nested fragmentRenderer element. The fragmentRenderer element specifies a Java class that extends io.jmix.flowui.fragmentrenderer.FragmentRenderer. This class is responsible for taking a data item and rendering it within the fragment’s template.

Fragment Content

To create a fragment, create a class that extends io.jmix.flowui.fragment.Fragment. The fragment generic type defines the root component type of the fragment content. The same component must be specified in the XML descriptor as the root component inside the content element.

The @FragmentDescriptor annotation specifies a string value that is a path to an XML file which is used for the fragment initialization. If the value contains a file name only (and doesn’t start with /), it is assumed that the file is located in the package of the fragment class.

Elements available in the XML descriptor:

  • content - required element that contains the fragment layout (similar to the view layout element). Since the root component of a fragment can be any component, content has no attributes and doesn’t represent any component.

  • actions - optional fragment actions element (similar to the view actions element). If an action has a shortcut, it is bound to the root component of the fragment. That is, the shortcut can be triggered only if focus is within the fragment layout.

  • data - optional fragment data element (similar to the view data element). A fragment can define its own data containers and loaders or get them by id from a host view or enclosing fragment as described in the Using Data Components section.

Example of a fragment XML:

customer-list-fragment.xml
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
    <data>
        <collection id="customersDc"
                    class="com.company.onboarding.entity.Customer">
            <fetchPlan extends="_base">
                <property name="city" fetchPlan="_base"/>
            </fetchPlan>
            <loader id="customersDl" readOnly="true">
                <query>
                    <![CDATA[select e from Customer e]]>
                </query>
            </loader>
        </collection>
    </data>
    <content>
        <vbox id="root" padding="false">
            <genericFilter id="genericFilter"
                           dataLoader="customersDl">
                <properties include=".*"/>
            </genericFilter>
            <hbox id="buttonsPanel" classNames="buttons-panel">
                <button id="createBtn" action="customersDataGrid.create"/>
                <button id="editBtn" action="customersDataGrid.edit"/>
                <button id="removeBtn" action="customersDataGrid.remove"/>
                <simplePagination id="pagination" dataLoader="customersDl"/>
            </hbox>
            <dataGrid id="customersDataGrid"
                      width="100%"
                      minHeight="20em"
                      dataContainer="customersDc"
                      columnReorderingAllowed="true">
                <actions>
                    <action id="create" type="list_create"/>
                    <action id="edit" type="list_edit"/>
                    <action id="remove" type="list_remove"/>
                </actions>
                <columns resizable="true">
                    <column property="city"/>
                    <column property="level"/>
                    <column property="age"/>
                    <column property="martialStatus"/>
                    <column property="hobby"/>
                    <column property="firstName"/>
                    <column property="lastName"/>
                    <column property="email"/>
                    <column property="rewardPoints"/>
                </columns>
            </dataGrid>
        </vbox>
    </content>
</fragment>

Fragment API

  • getFragmentData() - returns the FragmentData object defining methods for interacting with the fragment data components.

  • getFragmentActions() - returns the FragmentActions object defining methods for interacting with fragment actions. Similar to ViewActions.

  • getParentController() - returns a parent FragmentOwner object. Currently, it may be View or Fragment.

  • findInnerComponent() / getInnerComponent() - return the inner component with the given id. These methods search among components added via an XML descriptor only.

Fragment inner components have fragment-specific hidden IDs instead of actual Vaadin component IDs. Jmix stores these IDs separately to avoid conflicts with components defined in host views or in multiple instances of the same fragment.

Because of this, use FragmentUtils.getComponentId() to obtain a component ID inside a fragment. Do not use methods that rely on actual component IDs, such as UiComponentUtils, for this purpose.

Use findInnerComponent() / getInnerComponent() to access fragment components by ID from the fragment controller. If you need to work with a component instance directly, FragmentUtils.getComponentId() returns its fragment-specific hidden ID.

Fragment Events

  • ReadyEvent - the event that is fired after the fragment and all its declaratively defined inner components are created and fully initialized. In this event listener, you can make the final configuration of the fragment and its inner components. For example:

    CustomerListFragment.java
    import com.vaadin.flow.component.orderedlayout.VerticalLayout;
    import io.jmix.flowui.fragment.Fragment;
    import io.jmix.flowui.fragment.FragmentDescriptor;
    import io.jmix.flowui.view.Subscribe;
    
    @FragmentDescriptor("customer-list-fragment.xml")
    public class CustomerListFragment extends Fragment<VerticalLayout> {
    
        @Subscribe
        public void onReady(final ReadyEvent event) {
            getFragmentData().loadAll(); (1)
        }
    }
    1 Triggers load() method of all loaders registered in the fragment, including the provided ones.

Autowiring

Similar to views, fragments support injection of components defined in XML and invoking annotated handler methods:

import com.company.onboarding.entity.Customer;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.data.renderer.Renderer;
import io.jmix.core.LoadContext;
import io.jmix.flowui.kit.component.button.JmixButton;
import io.jmix.flowui.model.CollectionContainer;
import io.jmix.flowui.view.*;
import java.util.List;

// class declaration and annotations omitted

    @ViewComponent
    public JmixButton button; (1)

    @ViewComponent
    public CollectionContainer<Customer> collectionDc; (2)

    @Subscribe
    public void onReady(ReadyEvent event) { (3)
        // ...
    }

    @Subscribe(value = "button", subject = "clickListener")
    public void onButtonClick(ClickEvent<JmixButton> event) { (4)
        // ...
    }

    @Install(to = "collectionDl", target = Target.DATA_LOADER)
    public List<Customer> collectionDlLoadDelegate(LoadContext<Customer> loadContext) { (5)
        return loadCustomers(loadContext);
    }

    @Supply(to = "dataGrid.name", subject = "renderer")
    public Renderer<Customer> dataGridNameRenderer() { (6)
        return createRenderer();
    }
1 Injects a UI component.
2 Inject a data container.
3 Subscribes to the fragment’s ReadyEvent.
4 Subscribes to a button’s ClickEvent.
5 Installs a load delegate.
6 Supplies a data grid Renderer.

In addition to that, it’s possible to subscribe to the host view events by defining target = Target.HOST_CONTROLLER in the @Subscribe annotation:

@Subscribe(target = Target.HOST_CONTROLLER)
public void onHostInit(View.InitEvent event) {
    // ...
}

@Subscribe(target = Target.HOST_CONTROLLER)
public void onHostBeforeShow(View.BeforeShowEvent event) {
    // ...
}

@Subscribe(target = Target.HOST_CONTROLLER)
public void onHostReady(View.ReadyEvent event) {
    // ...
}