CheckboxGroup and MultiSelectComboBox does not work with record type models created from java

I have tried to set a list of strings e.g. roles to a field in my object where it takes a list of strings but could not make it work. Below site shows implementation of this partially.
[Binding Data to Input Fields | Forms | Guides | React | Hilla Docs]

import {FormLayout} from '@vaadin/react-components/FormLayout.js';
import {TextField} from '@vaadin/react-components/TextField.js';
import {PasswordField} from '@vaadin/react-components/PasswordField.js';
import Button from "@mui/material/Button";
import {UserEndpoint} from "Frontend/generated/endpoints";
import {useForm} from "@vaadin/hilla-react-form";
import { DatePicker } from '@vaadin/react-components/DatePicker.js';
import {CheckboxGroup, Checkbox, EmailField} from "@vaadin/react-components";
import {useEffect} from "react";
import CreateUserDto from "Frontend/generated/com/skgworks/restorect/dto/CreateUserDto";
import CreateUserDtoModel from "Frontend/generated/com/skgworks/restorect/dto/CreateUserDtoModel";

export default function CreateUser() {
    const responsiveSteps = [
        { minWidth: '0', columns: 1 },
        { minWidth: '500px', columns: 2 },
    ];

    const items = [
        {
            label: "User",
            value: "USER",
        },
        {
            label:"Admin",
            value: "ADMIN",
        },
    ];


    const {model,submit,field,addValidator } = useForm(CreateUserDtoModel,{
        onSubmit: async (user)=>{
            await UserEndpoint.createUser(user);
        }
    });


    useEffect(() => {
        addValidator({
            message: 'Please check that the password is repeated correctly',
            validate: (value: CreateUserDto) => {
                if (value.password != value.confirmPassword) {
                    return [{ property: model.confirmPassword }];
                }
                return [];
            }
        });
    }, []);

    return (
        <FormLayout responsiveSteps={responsiveSteps}>
            <TextField label="First name" {...field(model.name)}/>
            <TextField label="Last name"  {...field(model.surname)}/>
            <TextField label="Username" {...field(model.username)}/>
            <DatePicker label="Birthdate" {...field(model.birthdate)}/>
            <EmailField label="Email" {...field(model.email)}/>
            <PasswordField label="Password" {...field(model.password)}/>
            <PasswordField label="Confirm password" {...field(model.confirmPassword)}/>
            <CheckboxGroup label="Roles"
                           {...field(model.roles)}>
                {items.map(option => (
                    <Checkbox key={option.value} value={option.value}
                              label={option.label} />

                ))}
            </CheckboxGroup>

            <Button variant="contained" onClick={submit}>Create</Button>
        </FormLayout>
    );
}

and my generated models are as below

interface CreateUserDto {
    username?: string;
    name?: string;
    surname?: string;
    email?: string;
    password?: string;
    confirmPassword?: string;
    birthdate?: string;
    roles?: Array<string | undefined>;
    created?: string;
    updated?: string;
}
export default CreateUserDto;
import { _getPropertyModel as _getPropertyModel_1, ArrayModel as ArrayModel_1, makeObjectEmptyValueCreator as makeObjectEmptyValueCreator_1, ObjectModel as ObjectModel_1, StringModel as StringModel_1 } from "@vaadin/hilla-lit-form";
import type CreateUserDto_1 from "./CreateUserDto.js";
class CreateUserDtoModel<T extends CreateUserDto_1 = CreateUserDto_1> extends ObjectModel_1<T> {
    static override createEmptyValue = makeObjectEmptyValueCreator_1(CreateUserDtoModel);
    get username(): StringModel_1 {
        return this[_getPropertyModel_1]("username", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.lang.String" } }));
    }
    get name(): StringModel_1 {
        return this[_getPropertyModel_1]("name", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.lang.String" } }));
    }
    get surname(): StringModel_1 {
        return this[_getPropertyModel_1]("surname", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.lang.String" } }));
    }
    get email(): StringModel_1 {
        return this[_getPropertyModel_1]("email", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.lang.String" } }));
    }
    get password(): StringModel_1 {
        return this[_getPropertyModel_1]("password", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.lang.String" } }));
    }
    get confirmPassword(): StringModel_1 {
        return this[_getPropertyModel_1]("confirmPassword", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.lang.String" } }));
    }
    get birthdate(): StringModel_1 {
        return this[_getPropertyModel_1]("birthdate", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.time.LocalDate" } }));
    }
    get roles(): ArrayModel_1<StringModel_1> {
        return this[_getPropertyModel_1]("roles", (parent, key) => new ArrayModel_1(parent, key, true, (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.lang.String" } }), { meta: { javaType: "java.util.List" } }));
    }
    get created(): StringModel_1 {
        return this[_getPropertyModel_1]("created", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.util.Date" } }));
    }
    get updated(): StringModel_1 {
        return this[_getPropertyModel_1]("updated", (parent, key) => new StringModel_1(parent, key, true, { meta: { javaType: "java.util.Date" } }));
    }
}
export default CreateUserDtoModel;

Looks like the problem might be related to all your DTO’s properties being defined as optional. I guess in that case the Hilla form might not initialize roles with an initial array instance, and then can’t bind it to the checkbox group.

In any case, it seems to work when using non-nullable properties. For this you can add a package-info.java file to the package that contains your DTOs, with the following content (update the package name to the actual name of your package):

@NonNullApi
package org.vaadin.example.dtos;

import org.springframework.lang.NonNullApi;

That will cause Hilla to generate TS interfaces and models classes with non-optional properties, and the form should thus initialize the roles property with an empty array.

1 Like

thank you very much, this solved my problem