title: How to bind an input value in a list to a bean attribute with Spring
body: <p>I need to bind an object with a list of objects as an attribute. It is a static list and it is created and partially filled in the Controller. It is displayed correctly in the view, however the value in the input field is not set when the form is sent to the controller. I did some research and found several similar questions, however none of the proposed solutions worked for me. </p>
  
  <p>edit.jsp</p>
  
  <pre><code>&lt;c:forEach items="${priceConfigurationForm.priceList}" var="price" varStatus="priceStatus"&gt;
                      &lt;tr&gt;
                          &lt;td&gt;
                              &lt;spring:bind path="priceConfigurationForm.priceList[${priceStatus.index}].country"&gt;
                                  ${price.country}
                                  &lt;input type="hidden" name="&lt;c:out value="${status.expression}"/&gt;"
                                         id="&lt;c:out value="${status.expression}"/&gt;"
                                         value="&lt;c:out value="${status.value}"/&gt;"/&gt;
                              &lt;/spring:bind&gt;
                          &lt;/td&gt;
  
                          &lt;td&gt;
                              &lt;spring:bind path="priceConfigurationForm.priceList[${priceStatus.index}].amount"&gt;
                                  &lt;input type="text" name="price" value="${price.amount}"/&gt;
                                  &lt;input type="hidden"
                                         name="&lt;c:out value="${status.expression}"/&gt;"
                                         id="&lt;c:out value="${status.expression}"/&gt;"
                                         value="&lt;c:out value="${status.value}"/&gt;"/&gt;
                              &lt;/spring:bind&gt;
                          &lt;/td&gt;
  
                          &lt;td&gt;
                              &lt;spring:bind path="priceConfigurationForm.priceList[${priceStatus.index}].currency"&gt;
                                  ${price.currency}
                                  &lt;input type="hidden" name="&lt;c:out value="${status.expression}"/&gt;"
                                         id="&lt;c:out value="${status.expression}"/&gt;"
                                         value="&lt;c:out value="${status.value}"/&gt;"/&gt;
                              &lt;/spring:bind&gt;
                          &lt;/td&gt;
                      &lt;/tr&gt;
                  &lt;/c:forEach&gt;
  </code></pre>
  
  <p>Abstract from Controller populating the list:</p>
  
  <pre><code>@RequestMapping(value = "/price/create", method = RequestMethod.GET)
      public String toCreatePriceConfiguratioView(Model model) {
  
          log.info("::createPriceConfiguration: {}");
  
          final PriceConfigurationForm priceConfigurationForm = new PriceConfigurationForm();
  
          final List&lt;PriceConfigurationForm.Price&gt; prices = new ArrayList&lt;&gt;();
          for (Country country : Country.values()) {
              PriceConfigurationForm.Price price = new PriceConfigurationForm.Price();
              price.setAmount(100);
              price.setCountry(country.getCountryCode());
              price.setCurrency(country.getCurrency());
              prices.add(price);
          }
  
          priceConfigurationForm.setPriceList(prices);
  
          model.addAttribute("priceConfigurationForm", priceConfigurationForm);
  
          return "/secured/sources/onboarding/price/edit";
      }
  </code></pre>
  
  <p>Abstract from Controller for storing the list:</p>
  
  <pre><code>@RequestMapping(value = "/price/create", method = RequestMethod.POST)
      public String createPriceConfiguration(Model model, @ModelAttribute("priceConfigurationForm") @Valid PriceConfigurationForm priceConfigurationForm, BindingResult bindingResult, RedirectAttributes attributes) {
  
          log.info("::createPriceConfiguration {}", priceConfigurationForm);
          // TODO: Validate, before create
  
          /* transform form to domain object */
          PriceConfiguration configuration = PriceConfigurationForm.toPriceConfiguration(priceConfigurationForm);
          onboardingApi.createPriceConfiguration(configuration);
  
          attributes.addFlashAttribute("message", success("price configuration saved", priceConfigurationForm.getName()));
          return "/secured/sources/onboarding/price/index";
      }
  </code></pre>
  
  <p>Object:</p>
  
  <pre><code>@Data
  public class PriceConfigurationForm {
  
      private String name;
  
      private String description;
  
      private List&lt;Price&gt; priceList;
  
      private Map&lt;String, Long&gt; countryToPriceMap;
  
      public static PriceConfiguration toPriceConfiguration(PriceConfigurationForm form) {
          final PriceConfiguration pc = new PriceConfiguration();
          pc.setName(form.getName());
          pc.setDescription(form.getDescription());
          final Map&lt;String, Long&gt; prices = form.getPriceList().stream().collect(toMap(Price::getCountry, Price::getAmount));
          pc.setCountryToPriceMap(prices);
          return pc;
      }
  
      @Data
      public static class Price {
          private String country;
          private long amount;
          private String currency;
      }
  }
  </code></pre>