title: Thymeleaf + Map<String, Boolean> as input
body: <p>Using Spring Framework 5.3.20, Hibernate 6.1.0, Thymeleaf 3.1.0M2
  I want to make a controller method for class Dish, that would display map of checkboxes containing names of ingredients that are checked, if ingredients are present and edit those ingredients using it.</p>
  <p>So far I managed only to display ingredients that were previously saved in the database, but I cannot change/update ingredients for those dishes using my Update Dish Template.</p>
  <p>Currently I'm getting</p>
  <blockquote>
  <p>java.lang.NullPointerException: Cannot invoke &quot;java.util.Map.keySet()&quot; because &quot;dishIngredients&quot; is null
  at project.service.DishController.getIngredientSetFromMap(DishController.java:104) ~[classes/:na]</p>
  </blockquote>
  <p>So it seems like I can create the map in my view, but cannot get it back.</p>
  <p>Class Dish:</p>
  <pre><code>@Entity
  @Table(name=&quot;Dish&quot;)
  public class Dish implements Serializable {
  
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = &quot;id&quot;, nullable = false)
  private Long id;
  
  @ManyToMany(cascade = CascadeType.MERGE)
  @JoinTable(
          name = &quot;dish_ingredient&quot;,
          joinColumns = { @JoinColumn(name = &quot;fk_dish_id&quot;)},
          inverseJoinColumns = { @JoinColumn(name = &quot;fk_ingredient_id&quot;)} )
  private Set&lt;Ingredient&gt; ingredients = new HashSet&lt;&gt;();
  
  
  public Set&lt;Ingredient&gt; getIngredients() { return ingredients; }
  
  public String getPictureLink() {
      return pictureLink;
  }
  
  
  public void setIngredients(Set&lt;Ingredient&gt; ingredients) {
      this.ingredients = ingredients; } }
  </code></pre>
  <p>Class Ingredient:</p>
  <pre><code>@Entity
  @Table(name=&quot;ingredient&quot;)
  public class Ingredient implements Serializable {
  
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          @Column(name = &quot;id&quot;, nullable = false)
          private Long ingredientId;
  
          @Column(name = &quot;name&quot;)
          private String name;
  
          @ManyToMany(cascade = CascadeType.MERGE)
          @JoinTable(
                  name = &quot;dish_ingredient&quot;,
                  joinColumns = { @JoinColumn(name = &quot;fk_ingredient_id&quot;)},
                  inverseJoinColumns = { @JoinColumn(name = &quot;fk_dish_id&quot;)} )
          private Set&lt;Dish&gt; dishes;
  
          public Long getIngredientId() {
                  return ingredientId;
          }
  
          public void setIngredientId(Long ingredientId) {
                  this.ingredientId = ingredientId;
          }
  
          public String getName() {
                  return name;
          }
  
          public void setName(String name) {
                  this.name = name;
          }
  
          public Set&lt;Dish&gt; getDishes() {
                  return dishes;
          }
  
          public void setDishes(Set&lt;Dish&gt; dishes) {
                  this.dishes = dishes;
          }
  }
  </code></pre>
  <p>class DishIngredient:</p>
  <pre><code>@Entity
  @Table(name = &quot;dish_ingredient&quot;)
  public class DishIngredient {
  
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = &quot;id&quot;, nullable = false)
      private Long id;
  
      @ManyToOne
      private Dish dish;
  
      @ManyToOne
      private Ingredient ingredient;
  
      public Long getId() {
          return id;
      }
  
      public void setId(Long id) {
          this.id = id;
      }
  
      public Dish getDish() {
          return dish;
      }
  
      public void setDish(Dish dish) {
          this.dish = dish;
      }
  
      public Ingredient getIngredient() {
          return ingredient;
      }
  
      public void setIngredient(Ingredient ingredient) {
          this.ingredient = ingredient;
      }
  }
  </code></pre>
  <p>Class DishController:</p>
  <pre><code>    @GetMapping(&quot;/dish/update/{id}&quot;)
      public String updateDishForm(@PathVariable(&quot;id&quot;) long id, Model model) {
          Dish dish = dishService.getDish(id);
          model.addAttribute(&quot;dish&quot;, dish);
          List&lt;Ingredient&gt; dishIngredients = new ArrayList&lt;&gt;(dishService.getIngredients(id));
          List&lt;Ingredient&gt; allIngredients = new ArrayList&lt;&gt;(ingredientService.getAllIngredients());
          getIngredientSetFromMap(getCheckedIngredientsMap(dishIngredients, allIngredients));
          model.addAttribute(&quot;map&quot;, getCheckedIngredientsMap(dishIngredients, allIngredients));
          return &quot;updatedish&quot;;
      }
  
      @PostMapping(&quot;dish/save&quot;)
      public String saveDish(@ModelAttribute(&quot;dish&quot;) Dish dish, Model model){
          Map&lt;String, Boolean&gt; dishIngredientsMap = (Map&lt;String, Boolean&gt;) model.getAttribute(&quot;map&quot;);
          Set&lt;Ingredient&gt; ingredients = getIngredientSetFromMap(dishIngredientsMap);
          model.addAttribute(&quot;map&quot;, dishIngredientsMap);
          dish.setIngredients(ingredients);
          dishService.saveDish(dish);
          return &quot;redirect:/&quot;;
      }
  
  
      public Map&lt;String, Boolean&gt; getCheckedIngredientsMap(List&lt;Ingredient&gt; dishIngredients,
                                                           List&lt;Ingredient&gt; allIngredients) {
          Map&lt;String,Boolean&gt; ingredientsPresentInDish = new TreeMap&lt;&gt;();
          Set&lt;String&gt; dishIngredientsNames = dishIngredients.stream().map(Ingredient::getName).collect(Collectors.toSet());
          Set&lt;String&gt; allIngredientsNames = allIngredients.stream().map(Ingredient::getName).collect(Collectors.toSet());
  
          for (String ingredientName : allIngredientsNames) ingredientsPresentInDish.put(ingredientName, false);
  
          for (String dishIngredientsName : dishIngredientsNames) {
              for (String allIngredientsName : allIngredientsNames) {
                  if (allIngredientsName.equals(dishIngredientsName)) {
                      ingredientsPresentInDish.put(allIngredientsName, true);
                  }
              }
          }
          return ingredientsPresentInDish;
      }
  
      public Set&lt;Ingredient&gt; getIngredientSetFromMap(Map&lt;String, Boolean&gt; dishIngredients) {
          Set&lt;Ingredient&gt; ingredientsSet = new HashSet&lt;&gt;();
          for (String key : dishIngredients.keySet()) {
              if (dishIngredients.get(key)) ingredientsSet.add(ingredientService.getIngredientByName(key));
          }
          return ingredientsSet;
      }
  
  </code></pre>
  <p>Template updatedish:</p>
  <pre><code>
  &lt;!DOCTYPE html&gt;
  &lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot; lang=&quot;en&quot;&gt;
  &lt;head&gt;
      &lt;meta charset=&quot;UTF-8&quot;&gt;
  &lt;/head&gt;
  &lt;body&gt;
  &lt;div th:if=&quot;${dish} != null&quot;&gt;
      &lt;div class=&quot;container&quot;&gt;
          &lt;h1&gt;Edit dish&lt;/h1&gt;
          &lt;hr&gt;
  
          &lt;form action=&quot;#&quot; th:action=&quot;@{/dish/save}&quot; th:object=&quot;${dish}&quot; method=&quot;POST&quot;&gt;
              Dish name: &lt;input type=&quot;text&quot; th:field=&quot;*{name}&quot; placeholder=&quot;Dish name&quot;&gt;&lt;br&gt;
  
              &lt;div id =&quot;map&quot;&gt;
                          &lt;span&gt;MAP:&lt;/span&gt;
                          &lt;input type=&quot;checkbox&quot; class=&quot;ingredientsMap&quot; name=&quot;ingredientsMap&quot;
                                 th:each=&quot;key: ${map.keySet()}&quot;
                                 th:text=&quot;${key}&quot;
                                 th:value=&quot;${key}&quot;
                                 th:checked=&quot;${map.get(key)}&quot;&gt;
              &lt;/div&gt;
  
              &lt;button type=&quot;submit&quot;&gt;Save Dish&lt;/button&gt;
          &lt;/form&gt;
      &lt;/div&gt;
  
  &lt;/div&gt;
  &lt;div th:unless=&quot;${dish} != null&quot;&gt;
  Dish not found!
  &lt;/div&gt;
  &lt;a th:href=&quot;@{/}&quot;&gt;Back to Ingredients List&lt;/a&gt;
  
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>