gridLayout
gridLayout arranges components or data items in a responsive, two-dimensional CSS grid.
XML Element |
|
|---|---|
Java Class |
|
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.
<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>
@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.
-
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> -
Create the fragment controller.
Extend
FragmentRendererusing 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; } } -
Use
fragmentRendererfor agridLayoutcomponent:<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 |
|---|---|---|
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. |
|
|
Sets the minimum width for the grid columns in |
|
|
Controls the spacing between grid cells, including both row and column gaps. The value must be a valid CSS length (for example, |
|
|
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. |
|
Handlers
Common handlers are configured in the same way for all components.
The following handlers are specific to gridLayout.
| Name | Description |
|---|---|
Customizes the text produced by the default item 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.