ajaxspringspring-bootthymeleafmodelattribute

Spring modelattribute not recognized anymore after ajax update


i´ve encountered the following issue several times:
I use a Controller to bind a dto to a html form (via thymeleaf). Please note the model named "invoiceDto"

@RequestMapping(value = {"/create"}, method = RequestMethod.GET)
        public String create(Locale locale, Model model) throws Exception {
            final String login = this.getAuthentication().getCurrentApplicationUser();
            if (login == null || login.isEmpty())
                throw new Exception(this.getMessageSource().getMessage("label.findError", null, locale));
    
            final Future<Setting> setting = settingService.findFirst();
            final Future<ApplicationUserContactProjection> applicationUserContactProjection = applicationUserService.findByLogin(login);
            while (!setting.isDone() && !applicationUserContactProjection.isDone()) {
                Thread.sleep(100);
            }
            if (setting.get() == null || applicationUserContactProjection.get() == null)
                throw new Exception(this.getMessageSource().getMessage("label.error.findError",
                        null, locale));
    
            model.addAttribute("invoiceDto", new InvoiceDto(setting.get(), applicationUserContactProjection.get()));
            model.addAttribute("message", this.getMessageSource().getMessage("label.navigation.invoiceCreation", null, locale));
            return "invoice/create";
    }

I have a html form (thymeleaf generated) where i use the above Java pojo dto with the given modelattribute name to fill my Input fields. This is an excerpt of it. The important part is the div with the id of "invoiceLineItems" where thymeleaf replaces its child div with a lineItems Fragment:

<form action="#" th:action="@{/invoice/newinvoice}" th:object="${invoiceDto}" role="form" method="post"
              class="form-signin" id="editInvoiceForm"
              accept-charset="utf-8">
        <div id="invoiceLineItems"><div th:replace="invoice/items :: lineItems"></div></form>

The fragement contains the following stuff - an excerpt of it:

<td>
                    <input type="text"
                           th:field="*{items[__${index.index}__].lineItemTotalPrice}"
                           readonly
                           class="form-control" disabled
                           id="lineItemTotalPrice"/>
                </td>

Excerpt of the given pojo:

public class InvoiceDto implements Serializable {
        private Invoice invoice;
        private List<LineItem> items;

I access the list like this:

th:field="*{items[__${index.index}__].lineItemTotalPrice}"

The Problem:
I can add items dynamically via Ajax. I serialize the whole form (for convenience reasons) and call a Controller Method:

@RequestMapping(value = {"/newlineitem"}, method = RequestMethod.POST)
    public String newLineItem(@ModelAttribute("invoiceDto") InvoiceDto invoiceDto,
                              Model model)
            throws Exception {
    
        invoiceDto.addItem(new LineItem());
        final Future<InvoiceDto> calculatedInvoiceDto = invoiceService.calculateInvoice(invoiceDto);
        while (!calculatedInvoiceDto.isDone()) {
            Thread.sleep(100);
        }
        model.addAttribute("invoiceDto", calculatedInvoiceDto.get());
        return "invoice/dynamicitems :: lineItems";
    }

As you can see, i let thymeleaf render a Special view, because after the Ajax success spring cannot set the modelattributes proper.
In short: After the Ajax Returns the partial view, the following will throw an exception:

th:field="*{items[__${index.index}__].lineItemTotalPrice}"

whereas this works - note the prefixed invoiceDto:

th:field="*{invoiceDto.items[__${index.index}__].lineItemTotalPrice}"

Question: What´s wrong here?
Why do i have to prefix the name of the modelattribute after the partial Ajax update, whereas in the first run i don´t have to?

Thank you for your help!

EDIT:
For my share it looks like the way that spring "forgets" the originally named form modelattribute "invoiceDto" if the page is partially updated by an ajax call (to another spring controller, modifying invoiceDto) through a partial thymeleaf html.

So after the controller returns the partial thymeleaf view i have to access its fields with prefixed "invoiceDto", as if there would be no invoiceDto attribute.

Thanks again for your help!

UPDATE
As there is no progress on this i have raised a thymeleaf issue:
https://github.com/thymeleaf/thymeleaf/issues/795

Nevertheless i think this is a spring issue, because i have the same results with JSP.

Repository to comprehend this issue
https://mygit.th-deg.de/tlang/thymefail


Solution

  • If I understand your problem correctly, you're trying to use the *{} notation when there is no active object. When the ajax method returns just the "lineItems" fragment, Thymeleaf has no way of knowing that it belongs to a form with a th:object on it.

    I guess the best solution is to return the whole form then, in js, extract the lineItems. Or maybe just get rid of th:object altogether (convenient only when you want to show validation errors imho).