Search code examples
javaformsspring-bootdata-bindingfreemarker

Spring Boot form data binding a list with FreeMarker


I am trying to bind my form to a data transfer object. The form is a FreeMarker template. They are as follows:

The Data object:

@Data
public class TransferObject {
    private List<Subclass> subclassInstances;

    public TransferObject(Data data) {
        // this takes the data and populates the object, also works
        // we end up with a list of subclasses.
    }

    @Data //lombok -> generates getters and setters
    @AllArgsConstructor
    private static class Subclass {
        private String id;
        private String code;
    }
}

The Controller:

@GetMapping({"/endpoint", "/endpoint"})
public String endpoint(Model model, @RequestParam(value="code", required=false, defaultValue="") String code) {

    // this retrieves the data, but that works so it's irrelevant here
    Data data = this.dataService.findByCode(code).orElse(null);

    if(data != null) {
        TransferObject transferObject = new TransferObject(data);
        model.addAttribute("data", transferObject);

    } else {
        log.warn("no data found");
    }

    return "endpoint";
}

The Freemarker template:


<form:form action="/endpoint" method="post" modelAttribute="data">
    <#if data??>
        <#list data.subclasses as subclass>
            ${subclass} <!-- this shows an object with 2 fields that are filled -->

            <@spring.bind "data.subclasses[${subclass?index}].id"/>
            <input type="text" value="${subclass.id}"/> <!-- This line fails -->

            <@spring.bind "data.subclasses[${subclass?index}].code"/>
            <input type="text" value="${subclass.code}"/>

        </#list>
    </#if>
</form:form>

There is an error in the template that states:
[The following has evaluated to null or missing: ==> sublcass.id] I don't get that because I print the subclass just above that and it is there..

I also tried changing

<input type="text" value="${subclass.id}"/>

to

<input type="text" value="${data.subclasses[subclass?index].id}"/>

But then it tells me that 'data' is null or missing. What am I doing wrong?


Solution

  • I found the issue after all:

    The problem was in the TranferObject. The Sublclass class has private access. so any getters or setters aren't found. That's why the FreeMarker template could not find the .id property.

    When I tried to access the getter in normal Java code I got a compilation error: Error:(65, 77) java: getId() in Data.Subclass is defined in an inaccessible class or interface Which in my opinion is better than exclaiming it's null or missing.