如何在Thymeleaf中为多个对象填写一个表单中的字段?

时间:2017-03-14 21:53:36

标签: post spring-boot view controller thymeleaf

我有以下模型(没有getter和setter以提高可读性):

   @Entity
public class Recipe extends BaseEntity {
  private String name;
  private String description;
  private Category category;

  @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
  private List<Ingredient> ingredients;

  @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
  private List<Instruction> instructions;

  @ManyToMany
  private List<User> administrators;

  private int preparationTime;
  private int cookTime;

  public Recipe(){
    super();
    ingredients = new ArrayList<>();
    instructions = new ArrayList<>();
    administrators = new ArrayList<>();
  }

  public Recipe(String name, String description, Category category, int preparationTime, int cookTime) {
    this();
    this.name = name;
    this.description = description;
    this.category = category;
    this.preparationTime = preparationTime;
    this.cookTime = cookTime;
  }

*

@Entity
public class Ingredient extends BaseEntity {
  private String name;
  private String condition;
  private double quantity;
  private Measurement measurement;

  @ManyToOne
  private Recipe recipe;

  public Ingredient(){
    super();
  }

  public Ingredient(String name, String condition, double quantity, Measurement measurement) {
    this();
    this.name = name;
    this.condition = condition;
    this.quantity = quantity;
    this.measurement = measurement;
  }

*

@Entity
public class Instruction extends BaseEntity {
  private String name;
  private String description;

  @ManyToOne
  private Recipe recipe;

  public Instruction(){
    super();
  }

  public Instruction(String name, String description) {
    this();
    this.name = name;
    this.description = description;
  }

我需要做的是在一个Thymeleaf表单中填写每个对象的字段并发布它。我知道如何用一个对象来做。请解释如何为多个对象设置from和controller,所以最后生病的食谱上贴有成分和说明列表。谢谢!

EDITED: 这是一个控制器方法:

     @RequestMapping("/recipes/add")
  public String formNewRecipe(Model model) {
    Recipe recipe = new Recipe();

    if (!model.containsAttribute("recipe")) {
      model.addAttribute("recipe", recipe);
    }
    model.addAttribute("action", "/recipes");
    model.addAttribute("heading", "New Recipe");
    model.addAttribute("submit", "Save");
    model.addAttribute("categories", Category.values());
    model.addAttribute("measurements", Measurement.values());
    return "edit";
  }

  @RequestMapping(value = "/recipes", method = RequestMethod.POST)
  public String addRecipe(@Valid Recipe recipe,
                          BindingResult result,
                          RedirectAttributes redirectAttributes) {

    if (result.hasErrors()) {
      redirectAttributes
          .addFlashAttribute("org.springframework.validation.BindingResult.recipe", result);
      redirectAttributes.addFlashAttribute("recipe", recipe);
      return "redirect:/recipes/add";
    }

    recipes.save(recipe);
    redirectAttributes.addFlashAttribute("flash",
        new FlashMessage("New Recipe Created!!!", FlashMessage.Status.SUCCESS));
    return "redirect:/recipes/" + recipe.getId();
  }

和Thymeleaf形式:

<form th:action="@{${action}}" method="post" th:object="${recipe}">

                <div class="grid-100 row controls">
                    <div class="grid-50">
                        <h2 th:text="${heading}"></h2>
                    </div>
                    <div class="grid-50">
                        <div class="flush-right">
                            <input class="button" type="submit" th:value="${submit}"/>
                            <a th:href="@{|/recipes|}" class="secondary">
                                <button class="secondary">Cancel</button>
                            </a>
                        </div>
                    </div>
                </div>
                <div class="clear"></div>

                <div class="grid-100 row">
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Name </label>
                        </p>
                    </div>
                    <div class="grid-40">
                        <p><input type="text" th:field="*{name}"/>
                        <div class="error-message"
                             th:if="${#fields.hasErrors('name')}"
                             th:errors="*{recipe.name}">
                        </div>
                        </p>
                    </div>
                </div>
                <div class="clear"></div>

                <div class="grid-100 row">
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Description </label>
                        </p>
                    </div>
                    <div class="grid-40">
                        <p><textarea rows="4" th:field="*{description}"></textarea>
                        <div class="error-message"
                             th:if="${#fields.hasErrors('description')}"
                             th:errors="*{recipe.description}">
                        </div>
                        </p>
                    </div>
                </div>
                <div class="clear"></div>

                <div class="grid-100 row">
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Category </label>
                        </p>
                    </div>
                    <div class="grid-30">
                        <p>
                            <select th:field="*{category}">
                                <option value="" disabled="disabled">Recipe Category</option>
                                <option th:each="c : ${categories}"
                                        th:value="${c.name}"
                                        th:text="${c.name}">All Categories</option>
                            </select>
                        </p>
                    </div>
                </div>
                <div class="clear"></div>

                <div class="grid-100 row">
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Prep Time </label>
                        </p>
                    </div>
                    <div class="grid-20">
                        <p>
                            <input type="number" th:field="*{preparationTime}"/>
                        <div class="error-message"
                             th:if="${#fields.hasErrors('preparationTime')}"
                             th:errors="*{preparationTime}"></div>
                        </p>
                    </div>
                </div>
                <div class="clear"></div>

                <div class="grid-100 row">
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Cook Time </label>
                        </p>
                    </div>
                    <div class="grid-20">
                        <p>
                            <input type="number" th:field="*{cookTime}"/>
                        <div class="error-message"
                             th:if="${#fields.hasErrors('cookTime')}"
                             th:errors="*{cookTime}"></div>
                        </p>
                    </div>
                </div>
                <div class="clear"></div>

                <div class="grid-100 row">
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Ingredients </label>
                        </p>
                    </div>
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Item </label>
                        </p>
                    </div>
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Condition </label>
                        </p>
                    </div>
                    <div class="grid-15">
                        <p class="label-spacing">
                            <label> Quantity </label>
                        </p>
                    </div>
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Measurement </label>
                        </p>
                    </div>

                    <div class="ingredient-row">
                        <div class="prefix-20 grid-20">
                            <p>
                                <input type="text" th:field="*{ingredients[0].name}"/>
                            <div class="error-message"
                                 th:if="${#fields.hasErrors('ingredients[0].name')}"
                                 th:errors="*{ingredients[0].name}"></div>
                            </p>
                        </div>
                        <div class="grid-20">
                            <p>
                                <input type="text" th:field="*{ingredients[0].condition}"/>
                            <div class="error-message"
                                 th:if="${#fields.hasErrors('ingredients[0].condition')}"
                                 th:errors="*{ingredients[0].condition}"></div>
                            </p>
                        </div>
                        <div class="grid-15">
                            <p>
                                <input type="number" th:field="*{ingredients[0].quantity}"/>
                            <div class="error-message"
                                 th:if="${#fields.hasErrors('ingredients[0].quantity')}"
                                 th:errors="*{ingredients[0].quantity}"></div>
                            </p>
                        </div>
                        <div class="grid-20">
                            <p>
                                <select th:field="*{ingredients[0].measurement}">
                                    <option value="" disabled="disabled">Measurement</option>
                                    <option th:each="i : ${measurements}"
                                            th:value="${i.name}"
                                            th:text="${i.name}">Unknown
                                    </option>
                                </select>
                            </p>
                        </div>
                    </div>

                    <div class="prefix-20 grid-80 add-row">
                        <p>
                            <button>+ Add Another Ingredient</button>
                        </p>
                    </div>

                </div>
                <div class="clear"></div>

                <div class="grid-100 row">
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Instructions </label>
                        </p>
                    </div>
                    <div class="grid-20">
                        <p class="label-spacing">
                            <label> Steps </label>
                        </p>
                    </div>
                    <div class="grid-60">
                        <p class="label-spacing">
                            <label> Description </label>
                        </p>
                    </div>

                    <div class="instruction-row">
                        <div class="prefix-20 grid-20">
                            <p>
                                <input type="text" th:field="*{instructions[0].name}"/>
                            <div class="error-message"
                                 th:if="${#fields.hasErrors('instructions[0].name')}"
                                 th:errors="*{instructions[0].name}"></div>
                            </p>
                        </div>
                    </div>

                    <div class="instruction-row">
                        <div class="grid-50">
                            <p>
                                <input type="text" th:field="*{instructions[0].description}"/>
                            <div class="error-message"
                                 th:if="${#fields.hasErrors('instructions[0].description')}"
                                 th:errors="*{instructions[0].description}"></div>
                            </p>
                        </div>
                    </div>

                    <div class="prefix-20 grid-80 add-row">
                        <p>
                            <button>+ Add Another Step</button>
                        </p>
                    </div>

                </div>
                <div class="clear"></div>

                <div class="row">&nbsp;</div>
            </form>

1 个答案:

答案 0 :(得分:3)

它看起来像这样:

<form th:object="${recipe}">
    <input type="text" th:field="*{ingredients[0].name}" />
    <input type="text" th:field="*{ingredients[1].name}" />

    <input type="text" th:field="*{instructions[0].name}" />
    <input type="text" th:field="*{instructions[0].description}" />
</form>

如果您有大量动态成分,th:each可能如下所示:

 <th:block th:each="ingredient,i : ${recipe.ingredients}">
    <input type="text" th:field="*{ingredients[__${i.index}__].name}" /><br />
    <input type="text" th:field="*{ingredients[__${i.index}__].condition}" /><br />
    <input type="text" th:field="*{ingredients[__${i.index}__].quantity}" /><br />
    <input type="text" th:field="*{ingredients[__${i.index}__].measurement.anotherField}" /><br />
 </th:block>

动态地向表单添加另一个成分有点痛苦......你要么必须提交表单并在控制器中修改Recipe对象(添加一个成分,然后重定向回到表单)。或者您可以使用javascript复制字段,确保名称/ id / etc与其他字段匹配,索引递增。