Does Jmix allow to iterate UI component created in xml within a loop in JAVA

Hi,

I am trying to create a GroupBox in XML and would like to reference the GroupBox ID in Java within a loop. My goal is to dynamically generate the number of GroupBoxes based on the number of iterations in the loop.

Is it possible to achieve this in Jmix? If so, could you please provide guidance or examples on how to implement this functionality?

For Reference code
XML;

<vbox id="vboXID" visible="true" spacing="true">
                <label id="spaceLabel2" width="60px"/>
               <groupBox id="group" visible="false">
                   <comboBox id="comboBox" caption="ComboBox"/>
               </groupBox>
</vbox>

JAVA:

@Subscribe
    public void onAfterInit(final AfterInitEvent event)
    {
        for (DummyClass dummyclass : dummyclass) {
            group.setVisible(true);
            group.setCaption("Name " + dummyclass.getname());
            group.setCollapsable(true);
            group.setExpanded(false);
            group.setHeight("10px");
            htmlAttributes.applyCss(group, "background-color: #F8EDF7; display: flex; flex-direction: column; border: 1px solid #E0E0E0; border-radius: 5px;");

            group.addExpandedStateChangeListener(expandedStateChangeEvent ->
            {
                if (group.isExpanded()) {
                    group.setHeight(Component.AUTO_SIZE);
                    comboBox.setVisible(true);

                    htmlAttributes.applyCss(group, "background-color: #FFFFFF; display: flex; flex-direction: column; border: 1px solid #E0E0E0; border-radius: 5px;");
                } else {
                    group.setHeight("10px");
                    htmlAttributes.applyCss(group, "background-color: #F8EDF7; display: flex; flex-direction: column; border: 1px solid #E0E0E0; border-radius: 5px;");

                }
            });

        }
    }

Hello,
It seems you are using Jmix 1.x version, which has GroupBoxLayout.
To give you an example, I will use 2.5.2, and VerticalLayout, but the principles are the same.
I don’t quite understand what you want to achieve, but I feel this may help.

So let us assume you make a view, and it contains a top hbox that will contain some buttons, and after that vbox , which will contain the rest.
If we want to do something with them, we must give them id. We then add a button testBtn to the topHbox.
While on-click events of buttons can be defined with @Subscribe (studio Generate handler button) for the rest of the components and manipulations, we need to refer to them in Java code (studio Inject button) with @ViewComponent.
So, now we have a topHbox, with a button that will do your loop, and a vbox that is referenced in the code and will contain some components that we will iterate through repeatField and firstCheck, secondCheck checkboxes. After that, based on repeat value, we will programmatically create some Vertical laouts inside original one, based on repeat number.

You need

    @Autowired
    private UiComponents uiComponents;

in order to properly create and initialize them, new () will not work.

This is example View controller

public class Test2View extends StandardView {
    @Autowired
    private UiComponents uiComponents;

    @ViewComponent
    private VerticalLayout mainVBox;

    @Subscribe(id = "testBtn", subject = "clickListener")
    public void onTestBtnClick(final ClickEvent<JmixButton> event) {
        int repeat = 0;
        boolean checkBoxResult = true;

        for (Component component : mainVBox.getChildren().toList()) {
            //find instance of integer field and get its value (not using @ViewComponent), there should not be more than 1
            if (component instanceof JmixIntegerField) {
                JmixIntegerField integerField = (JmixIntegerField) component;
                Integer value = integerField.getValue();
                if (value != null) {
                    repeat = value;
                }
            }
            // we do this only if all the checkboxes are checked
            if (component instanceof JmixCheckbox) {
                JmixCheckbox checkBox = (JmixCheckbox) component;
                Boolean checked = checkBox.getValue();

                checkBoxResult = checkBoxResult && Boolean.TRUE.equals(checked);
            }
        }

        //if all checked and repeat greater than 0, we create programatically some new vboxes, and in each of the new checkbox
        if (checkBoxResult && repeat > 0) {

            JmixButton newButton = createButton();
            mainVBox.add(newButton);

            for (int i = 1; i < repeat; i++) {
                VerticalLayout innerVerticalLayout = createVerticalLayout(repeat);

                JmixCheckbox newCheckBox = createCheckBox(repeat);
                innerVerticalLayout.add(newCheckBox);

                mainVBox.add(innerVerticalLayout);
            }
        }
    }

    private JmixButton createButton() {
        JmixButton button = uiComponents.create(JmixButton.class);
        button.setId("newButton");
        button.setText("New button to do something");
        button.addClickListener(e -> Notification.show("New button clicked"));
        return button;
    }

    private VerticalLayout createVerticalLayout(Integer repeat) {
        VerticalLayout verticalLayout = uiComponents.create(VerticalLayout.class);
        verticalLayout.setId("innerVerticalLayout" + repeat);
        verticalLayout.setWidth("50%");
        verticalLayout.setHeight("50%");
        return verticalLayout;
    }

    private JmixCheckbox createCheckBox(Integer repeat) {
        JmixCheckbox checkBox = uiComponents.create(JmixCheckbox.class);
        checkBox.setId("checkBox" + repeat);
        checkBox.setLabel("Repeat: " + repeat);
        return checkBox;
    }
}

and its XML descriptor


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://test2View.title">
    <layout>
        <hbox id="topHBox">
            <button id="testBtn" text="Test"/>
        </hbox>
        <vbox id="mainVBox">
            <integerField id="repeatField" required="true"/>
            <checkbox id="firstCheck"/>
            <checkbox id="secondCheck"/>
        </vbox>
    </layout>
</view>

Kind regards,
Mladen

If you call this more than once, there will be a problem. When you don’t need to give an explicit id to a component, don’t use setId.