gridLayout

The gridLayout component arranges child components in a responsive, two-dimensional grid system based on the CSS Grid Layout.

XML Element

gridLayout

Java Class

GridLayout

Attributes

id - alignSelf - classNames - columnMinWidth - css - enabled - gap - height - itemsContainer - itemsEnum - maxHeight - maxWidth - minHeight - minWidth - visible - width

Handlers

AttachEvent - itemLabelGenerator - renderer - DetachEvent

Elements

fragmentRenderer

Basics

You can populate gridLayout with components either declaratively in the XML descriptor or programmatically in a controller using the gridLayout.add() method.

Example of declarative addition:

<gridLayout id="gridLayout" width="100%">
    <textField placeholder="City" label="Where from?"/>
    <textField placeholder="City" label="Where to?"/>
    <datePicker label="Depart"/>
    <datePicker label="Return"/>
    <button text="Search tickets" height="AUTO"/>
</gridLayout>

Example of adding in a view controller:

@ViewComponent
private GridLayout<Object> gridLayout;

@Autowired
private UiComponents uiComponents;

@Subscribe
public void onInit(final InitEvent event) {
    Checkbox checkbox = uiComponents.create(Checkbox.class);
    checkbox.setLabel("I verify that all information is accurate");
    checkbox.setValue(false);
    gridLayout.add(checkbox);
}
grid layout basic

Data Binding

Data binding refers to linking a visual component to a data container.

In the gridLayout component, this is achieved by setting the itemsContainer attribute to reference a data container.

<data>
    <collection id="usersDc" class="com.company.onboarding.entity.User"> (1)
        <fetchPlan extends="_base">
            <property name="department" fetchPlan="_base"/>
        </fetchPlan>
        <loader id="usersDl" readOnly="true">
            <query>
                <![CDATA[select e from User e]]>
            </query>
        </loader>
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/> (2)
</facets>
<layout>
    <gridLayout width="100%"
                itemsContainer="usersDc"/> (3)
</layout>
1 Data container to hold a collection of all existing User instances.
2 Data load coordinator to automatically supply the component with instances to display from.
3 Specifying the container that holds the list of items to be displayed.

By default, the component displays the instance name of the entity.

grid layout items
You can override the default display by configuring a custom renderer.

The component also supports displaying enumeration values using the itemsEnum property.

<gridLayout itemsEnum="com.company.onboarding.entity.DayOfWeek"
            width="100%"/>

Rendering Items

It is possible to customize the render of items. The renderer is applied to each item to create a component which represents the item.

This customization can be achieved using two distinct methods.

Programmatic Approach

In the view controller, use either:

  • The setRenderer() method;

  • The @Supply annotation.

Building on the previous example, we can generate a renderer to display each user as a custom Card component.

Show code
@Supply(to = "gridLtUsers", subject = "renderer")
private ComponentRenderer<Card, User> gridLtUsersRenderer() { (1)
    return new ComponentRenderer<>(this::createCard, this::initCard);
}

private Card createCard() { (2)
    Card card = uiComponents.create(Card.class);
    card.setWidthFull();
    card.addThemeVariants(CardVariant.LUMO_OUTLINED, CardVariant.LUMO_ELEVATED);
    return card;
}

private void initCard(Card card, User user) { (3)
    card.setHeaderPrefix(createAvatar(user));
    card.setTitle(user.getFirstName() + " " + user.getLastName());
    card.setSubtitle(createSubtitle(user));
    card.setHeaderSuffix(createHeaderSuffix(user));
}

private Image createAvatar(User user) { (4)
    Image image = uiComponents.create(Image.class);
    FileRef fileRef = user.getPicture();
    if (fileRef != null) {
        image.setWidth("50px");
        image.setHeight("50px");
        InputStreamDownloadHandler handler = DownloadHandler.fromInputStream(event -> {
            InputStream inputStream = fileStorageLocator.getByName(fileRef.getStorageName()).openStream(fileRef);
            return new DownloadResponse(inputStream, fileRef.getFileName(), fileRef.getContentType(), -1);
        });
        image.setSrc(handler);
    }
    return image;
}

private Span createSubtitle(User user) {
    Span span = uiComponents.create(Span.class);
    span.setText("%s: %s".formatted(
            getPropertyCaption(user, "department"),
            (user.getDepartment() != null ?
                    user.getDepartment().getName() :
                    "Not assigned")));
    return span;
}

private String getPropertyCaption(User user, String property) {
    MetaClass metaClass = metadata.getClass(user);
    return messageTools.getPropertyCaption(metaClass, property);
}

private Span createHeaderSuffix(User user) {
    Span span = uiComponents.create(Span.class);
    if (user.getActive()) {
        span.setText("Active");
        span.getElement().getThemeList().add("badge success");
    }
    else {
        span.setText("Inactive");
        span.getElement().getThemeList().add("badge error");
    }
    return span;
}
1 Supplies a custom renderer to display each User as a Card.
2 Creates a base Card component with common styling.
3 Initializes card content with user-specific data. Called for each card instance with the corresponding User entity.
4 Creates an avatar image component from the user’s picture.
grid layout renderer

Declarative Approach

Alternatively, you can render items using a nested fragmentRenderer element.

  1. Create FragmentRenderer XML-descriptor:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <fragment xmlns="http://jmix.io/schema/flowui/fragment">
        <data>
            <instance id="userDc" class="com.company.onboarding.entity.User">
                <loader id="userDl"/>
                <fetchPlan extends="_base"/>
            </instance>
        </data>
        <content>
            <vbox id="root" padding="false"/>
        </content>
    </fragment>
  2. Create FragmentRenderer Java Controller

    The fragment renderer class should extend the FragmentRenderer base class with type parameters indicating the root component and rendered entity, for example:

    Show code
    @FragmentDescriptor("card-fragment.xml")
    @RendererItemContainer("userDc")
    public class CardFragment extends FragmentRenderer<VerticalLayout, User> {
    
        @Autowired
        private FileStorageLocator fileStorageLocator;
        @Autowired
        private Metadata metadata;
        @Autowired
        private MessageTools messageTools;
    
        @Override
        protected void onAttach(AttachEvent attachEvent) {
            super.onAttach(attachEvent);
            initLayout();
        }
    
        private void initLayout() {
            Card card = uiComponents.create(Card.class);
            card.setWidthFull();
            card.addThemeVariants(CardVariant.LUMO_OUTLINED, CardVariant.LUMO_ELEVATED);
    
            card.setHeaderPrefix(createAvatar(getItem()));
            card.setTitle(getItem().getFirstName() + " " + getItem().getLastName());
            card.setSubtitle(createSubtitle(getItem()));
            card.setHeaderSuffix(createHeaderSuffix(getItem()));
            getContent().add(card);
        }
    
        private Image createAvatar(User user) {
            Image image = uiComponents.create(Image.class);
            FileRef fileRef = user.getPicture();
            if (fileRef != null) {
                image.setWidth("50px");
                image.setHeight("50px");
                InputStreamDownloadHandler handler = DownloadHandler.fromInputStream(event -> {
                    InputStream inputStream = fileStorageLocator.getByName(fileRef.getStorageName()).openStream(fileRef);
                    return new DownloadResponse(inputStream, fileRef.getFileName(), fileRef.getContentType(), -1);
                });
                image.setSrc(handler);
            }
            return image;
        }
    
        private Span createSubtitle(User user) {
            Span span = uiComponents.create(Span.class);
            span.setText("%s: %s".formatted(
                    getPropertyCaption(user, "department"),
                    (user.getDepartment() != null ?
                            user.getDepartment().getName() :
                            "Not assigned")));
            return span;
        }
    
        private String getPropertyCaption(User user, String property) {
            MetaClass metaClass = metadata.getClass(user);
            return messageTools.getPropertyCaption(metaClass, property);
        }
    
        private Span createHeaderSuffix(User user) {
            Span span = uiComponents.create(Span.class);
            if (user.getActive()) {
                span.setText("Active");
                span.getElement().getThemeList().add("badge success");
            }
            else {
                span.setText("Inactive");
                span.getElement().getThemeList().add("badge error");
            }
            return span;
        }
    }
  3. Use fragmentRenderer for a gridLayout component:

    <gridLayout id="gridUsers"
                width="100%"
                itemsContainer="usersDc"
                gap="var(--lumo-space-m)">
        <fragmentRenderer class="com.company.onboarding.view.layout.gridlayout.CardFragment"/>
    </gridLayout>

Attributes

Common attributes serve the same purpose for all components.

The following attributes are specific to gridLayout:

Name

Description

Default

columnMinWidth

Sets the minimum width for the grid columns in gridLayout. This ensures columns don’t become too narrow when the container resizes. This value represented as a CSS length value (for example, "50px", "10rem")

19rem

gap

Controls the spacing between grid cells (both row and column gaps). This creates visual separation between components. The value represented as a CSS length value (for example, "10px", "1rem")

var(--lumo-space-s)

Handlers

Common handlers are configured in the same way for all components.

The following handlers are specific to gridLayout.

To generate a handler stub in Jmix Studio, use the Handlers tab of the Jmix UI inspector panel or the Generate Handler action available in the top panel of the view class and through the CodeGenerate menu (Alt+Insert / Cmd+N).

Name

Description

itemLabelGenerator

Allows you to customize the labels displayed for each item. See Customizing Item Labels.

renderer

Sets the item renderer for gridLayout. The renderer is applied to each item to create a component which represents the item option. See Rendering Items.