gridLayout

gridLayout arranges components or data items in a responsive, two-dimensional CSS grid.

XML Element

gridLayout

Java Class

GridLayout

Basics

Add child components declaratively and use gap and columnMinWidth to control spacing and responsive column sizing. Both attributes accept CSS length values and variables.

<gridLayout id="gridLayout" gap="var(--lumo-space-m)" columnMinWidth="18em" width="100%" height="100%">
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
    <image width="100%" height="100%" resource="icons/jmix-icon.png"/>
</gridLayout>

Data-aware gridLayout

Set itemsContainer to connect gridLayout to a collection container. The layout creates one rendered component for each item in the container.

<data readOnly="true">
    <collection id="customerDc" class="io.jmix.uisamples.entity.Customer"
                fetchPlan="_base">
        <loader id="customerDl" readOnly="true">
            <query>
                <![CDATA[select e from Customer e]]>
            </query>
        </loader>
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/>
</facets>
<layout>
    <split width="100%" height="100%">
        <gridLayout id="gridLayout" itemsContainer="customerDc"
                    width="100%" height="100%"/>
        <div/>
    </split>
</layout>

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

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

Enumeration Items

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

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

Custom Item Renderer

Use a renderer to replace the default item label with a component. The example connects gridLayout to a collection container in XML and supplies a ComponentRenderer from the controller with @Supply.

XML
<data readOnly="true">
    <collection id="customerDc" class="io.jmix.uisamples.entity.Customer"
                fetchPlan="_base">
        <loader id="customerDl" readOnly="true">
            <query>
                <![CDATA[select e from Customer e]]>
            </query>
        </loader>
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/>
</facets>
<layout>
    <split width="100%" height="100%">
        <vbox>
            <gridLayout id="gridLayout" itemsContainer="customerDc" gap="var(--lumo-space-m)"
                        width="100%" height="100%"/>
        </vbox>
        <div/>
    </split>
</layout>
Java
@Autowired
private UiComponents uiComponents;
@Autowired
private MetadataTools metadataTools;
@Autowired
private MessageTools messageTools;
@Autowired
private Metadata metadata;

private int colorIndex;

@Supply(to = "gridLayout", subject = "renderer")
public ComponentRenderer<JmixCard, Customer> customerRenderer() {
    return new ComponentRenderer<>(this::createCard, this::initCard);
}

private JmixCard createCard() {
    JmixCard card = uiComponents.create(JmixCard.class);
    card.setWidthFull();
    card.addThemeVariants(CardVariant.OUTLINED, CardVariant.ELEVATED);
    return card;
}

private void initCard(JmixCard card, Customer customer) {
    card.setHeaderPrefix(createAvatar(customer));
    card.setTitle(customer.getInstanceName());
    card.setSubtitle(createSubtitle(customer));
    card.add(createAdditionalInfo(customer));
}

private Avatar createAvatar(Customer customer) {
    Avatar avatar = new Avatar(customer.getInstanceName());
    avatar.setColorIndex(colorIndex++ % 7);
    return avatar;
}

private Component createSubtitle(Customer customer) {
    Span span = uiComponents.create(Span.class);
    String ageCaption = getPropertyCaption(customer, "age");
    span.setText("%s: %s".formatted(ageCaption, customer.getAge()));
    return span;
}

private Component createAdditionalInfo(Customer customer) {
    HorizontalLayout emailLayout = uiComponents.create(HorizontalLayout.class);
    emailLayout.setPadding(false);
    emailLayout.add(createPropertyCaption(customer, "email"), new Span(customer.getEmail()));

    HorizontalLayout gradeLayout = uiComponents.create(HorizontalLayout.class);
    gradeLayout.setPadding(false);
    gradeLayout.add(createPropertyCaption(customer, "grade"), createGradeBadge(customer));

    VerticalLayout infoLayout = uiComponents.create(VerticalLayout.class);
    infoLayout.add(emailLayout, gradeLayout);
    return infoLayout;
}

private Component createPropertyCaption(Customer customer, String property) {
    Span span = uiComponents.create(Span.class);
    String propertyCaption = getPropertyCaption(customer, property);
    span.setText("%s: ".formatted(propertyCaption));
    return span;
}

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

private Component createGradeBadge(Customer customer) {
    Span span = uiComponents.create(Span.class);
    CustomerGrade gradeValue = customer.getGrade();
    String gradeCaption = metadataTools.format(gradeValue);

    span.setText(gradeCaption);
    span.getElement().getThemeList().add("badge " + getGradeColor(Objects.requireNonNull(gradeValue)));
    return span;
}

private String getGradeColor(CustomerGrade customerGrade) {
    return switch (customerGrade) {
        case STANDARD -> "contrast";
        case HIGH -> "success";
        case PREMIUM -> "primary";
    };
}

Fragment Renderer

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

  1. Create the fragment descriptor:

    <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 the fragment controller.

    Extend FragmentRenderer using the fragment root component and rendered entity as its type parameters:

    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 configure gridLayout or its direct child elements:

Name Description Default

alignSelf

Set on a child element to control its alignment within the grid cell along the block (column) axis. This corresponds to the CSS align-self property.

AUTO

columnMinWidth

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

19rem

gap

Controls the spacing between grid cells, including both row and column gaps. The value must be a valid CSS length (for example, "10px" or "1rem").

0.5rem

justifySelf

Set on a child element to control its alignment within the grid cell along the inline (row) axis. This corresponds to the CSS justify-self property.

AUTO

Handlers

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

The following handlers are specific to gridLayout.

Name Description

itemLabelGenerator

Customizes the text produced by the default item renderer.

renderer

Sets the renderer used to create a component for each item. See Custom Item Renderer.

Elements

A non-data-aware gridLayout can contain components directly. A data-aware layout can contain a fragmentRenderer that creates a component for each item.