Hi everyone, I created composite component how to inject the components into the controller? I need to attach a onClick and onChange listener. Thanks
Hello!
Could you clarify which Jmix version you are using? Also, do you mean injecting a component into the View controller, or injecting inner components of a composite component into the component itself?
Jmix version: 2.6.0
Jmix Studio Plugin Version: 2.6.2-252
IntelliJ version: IntelliJ IDEA 2025.2 (Community Edition)
I want to inject the component into the view controller. The composite component is a JmixSelect + Button. Then i want to add a clicklistener on that button like i do with the other components like this @Subscribe(“statusField”)
// public void onStatusFieldComponentValueChange(final AbstractField.ComponentValueChangeEvent<JmixSelect<?>, ?> event) {
… …
}
class is this package it.evisionsrl.cdaplus.components.selectHelper;
import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.AbstractSinglePropertyField;
import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexLayout;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import io.jmix.flowui.UiComponents;
import io.jmix.flowui.component.select.JmixSelect;
import io.jmix.flowui.data.value.ContainerValueSource;
import io.jmix.flowui.kit.component.ComponentUtils;
import io.jmix.flowui.kit.component.button.JmixButton;
import io.jmix.flowui.model.InstanceContainer;
import it.evisionsrl.cdaplus.config.ConfigurationSelect;
import it.evisionsrl.cdaplus.service.HelperService;
import lombok.Getter;
import lombok.Setter;
public class SelectWithHelper extends Composite {
//public class SelectWithHelper extends Composite
//public class SelectWithHelper extends Composite {
@Getter
private JmixSelect select;
@Getter
private JmixButton button;
@org.springframework.beans.factory.annotation.Autowired
protected UiComponents uiComponents;
@Setter
private String itemKey;
private String idField;
private ConfigurationSelect configurationSelect;
HelperService helperService;
public SelectWithHelper(UiComponents uiComponents, ConfigurationSelect configurationSelect, HelperService helperService) {
this.uiComponents = uiComponents;
this.configurationSelect=configurationSelect;
this.select = uiComponents.create(JmixSelect.class);
this.helperService = helperService;
this.button = uiComponents.create(JmixButton.class);
System.out.println("INIT");
System.out.println(this.button);
}
@Override
protected HorizontalLayout initContent() {
HorizontalLayout content = super.initContent();
content.add(select, button );
content.getStyle().set("display","flex");
content.getStyle().set("gap","0");
// content.getStyle().set(“background-color”,“hsla(214, 57%, 24%, 0.1)”);
// content.getStyle().set(“background-color”,“var(–lumo-contrast-10pct”);
select.addClassName("select-helper");
Icon icon = VaadinIcon.QUESTION_CIRCLE.create();
icon.getStyle().set("color","var(--lumo-secondary-text-color)");
// icon.setColor(" color: var(–lumo-secondary-text-color);");
button.setIcon(icon);
button.getStyle().set("border-radius","0");
button.getStyle().set("background-color","var(--lumo-contrast-10pct)");
button.getStyle().set("margin-top","4px");
button.getStyle().set("margin-bottom","4px");
button.getStyle().set("fill","currentColor");
// ComponentUtils.setItemsMap(select, configurationSelect.getStatusMaps());
// System.out.println("ITEMS: " + itemKey);
// ComponentUtils.setItemsMap(select, configurationSelect.getFieldsWithSelect().get(itemKey));
// this.button = this.helperService.createHelperButton(getId().get());
return content;
}
public void setDataContainer(InstanceContainer<Object> instanceContainer, String idField) {
select.setValueSource(new ContainerValueSource<>(instanceContainer, itemKey));
// System.out.println(itemKey);
// System.out.println(configurationSelect.getFieldsWithSelect().get(itemKey));
// System.out.println("REG: " + ComponentUtils.(select, configurationSelect.getFlPcMaps()));
ComponentUtils.setItemsMap(select, configurationSelect.getFieldsWithSelect().get(itemKey));
this.button = this.helperService.createSingleClickListener(this.button,idField);
}
}
Let’s imagine we have a HelperSelect
component.
HelperSelect.class
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.select.Select;
import com.vaadin.flow.shared.Registration;
import java.util.EventObject;
import java.util.function.Consumer;
public class HelperSelect extends Composite<HorizontalLayout> implements HasSize {
private Select<String> select;
private Button helperButton;
@Override
protected HorizontalLayout initContent() {
HorizontalLayout layout = new HorizontalLayout();
select = createSelect();
helperButton = createButton();
layout.add(select, helperButton);
return layout;
}
public Registration addHelperButtonClickListener(Consumer<HelperButtonClickEvent> listener) {
return helperButton.addClickListener(event -> {
listener.accept(new HelperButtonClickEvent(helperButton, this, event.isFromClient()));
});
}
public Registration addValueChangeListener(HasValue.ValueChangeListener<AbstractField.ComponentValueChangeEvent<Select<String>, String>> listener) {
return select.addValueChangeListener(listener);
}
private Select<String> createSelect() {
Select<String> select = new Select<>();
select.setItems("One", "Two", "Three");
return select;
}
private Button createButton() {
Button helperButton = new Button();
helperButton.setIcon(VaadinIcon.QUESTION_CIRCLE.create());
return helperButton;
}
public static class HelperButtonClickEvent extends EventObject {
protected HelperSelect helperSelect;
protected boolean fromClient;
public HelperButtonClickEvent(Button source, HelperSelect helperSelect, boolean fromClient) {
super(source);
this.helperSelect = helperSelect;
this.fromClient = fromClient;
}
@Override
public Button getSource() {
return (Button) source;
}
public HelperSelect getHelperSelect() {
return helperSelect;
}
public boolean isFromClient() {
return fromClient;
}
}
}
To enable generation handlers for custom component and injecting it to a controller from a descriptor, follow the following steps:
-
Create XSD schema to enable working with component in View descriptors:
ui.xsd
<xs:schema targetNamespace="http://myschema.io/schema/demo/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:layout="http://jmix.io/schema/flowui/layout" xmlns="http://myschema.io/schema/demo/ui" elementFormDefault="qualified"> <xs:element name="helperSelect"> <xs:complexType> <xs:complexContent> <xs:extension base="layout:baseComponent"> <xs:attributeGroup ref="layout:hasSize"/> </xs:extension> </xs:complexContent> </xs:complexType> </xs:element> </xs:schema>
-
Create loader class to load component from View descriptor.
HelperSelectLoader.class
public class HelperSelectLoader extends AbstractComponentLoader<HelperSelect> { @Override protected HelperSelect createComponent() { return factory.create(HelperSelect.class); } @Override public void loadComponent() { componentLoaderSupport.loadSizeAttributes(resultComponent, element); } }
-
Register component with loader in Jmix components factory:
@Bean public ComponentRegistration helperSelect() { return ComponentRegistrationBuilder.create(HelperSelect.class) .withComponentLoader("helperSelect", HelperSelectLoader.class) .build(); }
-
In order for the component to be recognized by Studio, create a Studio description for the component.
StudioMyDemoComponents.class
import com.company.demo.component.HelperSelect; import io.jmix.flowui.kit.meta.*; @StudioUiKit public interface StudioMyDemoComponents { @StudioComponent( name = "HelperSelect", xmlElement = "helperSelect", xmlnsAlias = "hs", xmlns = "http://myschema.io/schema/demo/ui", category = "Components", classFqn = "com.company.demo.component.HelperSelect", properties = { @StudioProperty(xmlAttribute = "alignSelf", category =StudioProperty.Category.POSITION, type = StudioPropertyType.ENUMERATION, classFqn = "com.vaadin.flow.component.orderedlayout.FlexComponent$Alignment", defaultValue = "AUTO", options = {"START", "END", "CENTER", "STRETCH", "BASELINE", "AUTO"}), @StudioProperty(xmlAttribute = "css", category = StudioProperty.Category.LOOK_AND_FEEL, type = StudioPropertyType.STRING), @StudioProperty(xmlAttribute = "height", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}), @StudioProperty(xmlAttribute = "id", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.COMPONENT_ID), @StudioProperty(xmlAttribute = "maxHeight", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}), @StudioProperty(xmlAttribute = "maxWidth", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}), @StudioProperty(xmlAttribute = "minHeight", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}), @StudioProperty(xmlAttribute = "minWidth", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}), @StudioProperty(xmlAttribute = "visible", category = StudioProperty.Category.GENERAL, type = StudioPropertyType.BOOLEAN, defaultValue = "true"), @StudioProperty(xmlAttribute = "width", category = StudioProperty.Category.SIZE, type = StudioPropertyType.SIZE, options = {"AUTO", "100%"}), } ) HelperSelect helperSelect(); }
-
Refresh gradle in your project.
Now the component can be injected to the controller and its handlers can be generated:
@Subscribe("helperSelect")
public void onHelperSelectComponentValueChange(final AbstractField.ComponentValueChangeEvent<HelperSelect, ?> event) {
notifications.show("HelperSelect value changed: " + event.getValue());
}
@Subscribe("helperSelect")
public void onHelperSelectHelperButtonClick(final HelperSelect.HelperButtonClickEvent event) {
notifications.show("Button is clicked");
}
The full example: demo-2-6-x.zip (90.5 KB)