gridLayout

gridLayout располагает компоненты или элементы данных в адаптивной двумерной CSS-сетке.

XML-элемент

gridLayout

Java-класс

GridLayout

Основы

Добавьте дочерние компоненты декларативно и используйте gap и columnMinWidth, чтобы настроить интервалы и адаптивную ширину столбцов. Оба атрибута принимают CSS-величины длины и переменные.

<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>

Привязка к данным

Задайте itemsContainer, чтобы связать gridLayout с контейнером коллекции. Контейнер создаёт по одному отображаемому компоненту для каждого элемента контейнера данных.

<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>

По умолчанию компонент отображает имя экземпляра сущности.

Вы можете переопределить отображение по умолчанию, настроив пользовательский рендерер.

Элементы перечисления

Компонент также поддерживает отображение значений перечисления с помощью атрибута itemsEnum.

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

Пользовательский рендерер элементов

Используйте рендерер, чтобы заменить стандартную текстовую надпись элемента компонентом. В примере gridLayout связывается с контейнером коллекции в XML, а контроллер передаёт ComponentRenderer с помощью @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";
    };
}

Рендерер фрагмента

Элементы также можно отображать с помощью вложенного элемента fragmentRenderer.

  1. Создайте дескриптор фрагмента:

    <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. Создайте контроллер фрагмента.

    Расширьте FragmentRenderer, указав корневой компонент фрагмента и отображаемую сущность как параметры типа:

    Показать код
    @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. Используйте fragmentRenderer для компонента gridLayout:

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

Атрибуты

Общие атрибуты имеют одинаковое назначение для всех компонентов. Следующие атрибуты настраивают gridLayout или его непосредственные дочерние элементы:

Имя Описание По умолчанию

alignSelf

Задаётся у дочернего элемента и управляет его выравниванием внутри ячейки сетки по блочной оси (оси столбца). Соответствует CSS-свойству align-self.

AUTO

columnMinWidth

Задает минимальную ширину для столбцов сетки в gridLayout. Это гарантирует, что столбцы не станут слишком узкими при изменении размера контейнера. Значение должно быть допустимой CSS-величиной длины, например "50px" или "10rem".

19rem

gap

Задаёт интервалы между ячейками сетки по строкам и столбцам. Значение должно быть допустимой CSS-величиной длины, например "10px" или "1rem".

0.5rem

justifySelf

Задаётся у дочернего элемента и управляет его выравниванием внутри ячейки сетки по строчной оси (оси строки). Соответствует CSS-свойству justify-self.

AUTO

Обработчики

Общие обработчики настраиваются одинаково для всех компонентов.

Следующие обработчики специфичны для gridLayout.

Имя Описание

itemLabelGenerator

Настраивает текст, который создаёт стандартный рендерер элементов.

renderer

Задаёт рендерер, который создаёт компонент для каждого элемента. См. Пользовательский рендерер элементов.

Элементы

gridLayout без привязки к данным может содержать компоненты напрямую. Контейнер с привязкой к данным может содержать fragmentRenderer, который создаёт компонент для каждого элемента.