Search code examples
javahtmlspringthymeleaf

Create a fragment for a select multiple element in thymeleaf


I'm trying to create a fragment for the multiselect field in my webapp. But I'm running into various issues with regards to passing in the field that should be bound to the select element. I figure I have a syntax error, but I'm not sure what it is.

For selectedField in my addUser.jsp file, I've tried ${selectedAdminAccess}, *{selectedAdminAccess}, ${userForm.selectedAdminAccess}, etc.

And with those various combinations, in the forms.html file, I've tried ${selectField}, *{selectField}, and the preprocessor directive: ${__${selectField}__}. Have I just missed the correct combination?

The error I'm getting is "Neither BindingResult nor plain target object for bean name 'X' available as request attribute". Where X is (for the preprocessor attempt) the fieldname entered in the addUser.jsp page, or "selectField" when not using the preprocessor attempt.

Here's what I've got.

Controller

@GetMapping(value={"/adduser"}
public ModelAndView getAddUserPage(Model model, @RequstParam(required = false) Long selectedUserId) {

   ModelAndView mav = new ModelAndView("/addUser");
   UserForm userForm = new UserForm();
   mav.addObject("userForm", userForm);
   return mav;
}

model UserForm

public class UserForm {
    private List<Office> adminAccess = new ArrayList<>();
    private List<String> selectedAdminAccess = new ArrayList<>();
// Getter/Setter 
...
}

addUser.jsp

<form method="post" action="#" data-th-action="@{/addUser}" data-th-object="${userForm}">
   <!-- other interesting page stuff -->
   <div data-th-replace="fragments/forms.html :: multi-select(
      id='adminSelect',
      selectClass='office-target',
      selectField='*{selectedAdminAccess}',
      optionList='*{adminAccess}',
      optionListValue='id',
      optionListText='name',
      size=8)"></div>
</form>

forms.html

<div data-th-fragment="multi-select(id, selectClass, selectField, optionList, optionListValue, optionListText, size)" >
    <select id="${id}" class="mdb-select colorful-select md-form ${selectClass}" size="${size}" multiple
            searchable data-th-field="${__${selectField}__}">
        <option data-th-each="option : ${adminAccess}"
                data-th-value="${option.__${optionListValue}__}"
                data-th-text="${option.__${optionListText}__}"></option>
    </select>

</div>

Solution

  • The ultimate goal here is to have an expression data-th-field="*{selectedAdminAccess}" To accomplish this:

    • For selectField, you need to pass a string with value 'selectedAdminAccess'.
    • For th:field, use the preprocessing expression *{__${selectField}__}.

    The HTML you provided looks like it has other problems... but I think your final HTML will look something like this:

    <!-- page -->
    <form method="post" action="#" data-th-action="@{/addUser}" data-th-object="${userForm}">
      <!-- other interesting page stuff -->
      <div data-th-replace="fragments/forms.html :: multi-select(
        id='adminSelect',
        selectClass='office-target',
        selectField='selectedAdminAccess',
        optionList=*{adminAccess},
        optionListValue='id',
        optionListText='name',
        size=8
      )" />
    </form>
    
    <!-- fragment -->
    <div data-th-fragment="multi-select(id, selectClass, selectField, optionList, optionListValue, optionListText, size)" >
        <select data-th-id="${id}"
                data-th-class="|mdb-select colorful-select md-form ${selectClass}|"
                data-th-size="${size}"
                multiple
                searchable
                data-th-field="*{__${selectField}__}">
            <option data-th-each="option : ${optionList}"
                    data-th-value="${option.optionListValue}"
                    data-th-text="${option.optionListText}" />
        </select>
    </div>