我有以下模型(没有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"> </div>
</form>
答案 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与其他字段匹配,索引递增。