Reputation: 178
I am following Spring in Action 5 and have problem with creating Taco model after pressing submit button. This is my design Taco controller class:
@GetMapping
public String showDesignForm(Model model){
List<Ingredient> ingredients = new ArrayList<>();
ingredientRepository.findAll().forEach(i -> ingredients.add(i));
Type[] types = Ingredient.Type.values();
for (Type type : types){
model.addAttribute(type.toString().toLowerCase(),
filterByType(ingredients, type));
}
return "welcomePage";
}
@ModelAttribute(name = "taco")
public Taco taco(){
return new Taco();
}
@PostMapping
public String processDesign(@Valid Taco taco, Errors errors, @ModelAttribute Order order){
if(errors.hasErrors()) {
return "welcomePage";
}
Taco saved = tacoRepository.save(taco);
order.addDesign(saved);
return "redirect:/orders/current";
}
And the error message which I catch:
org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'taco' on field 'ingredients': rejected value [CARN]; codes [typeMismatch.taco.ingredients,typeMismatch.ingredients,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [taco.ingredients,ingredients]; arguments []; default message [ingredients]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'ingredients'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'org.server.models.Ingredient' for property 'ingredients[0]': no matching editors or conversion strategy found]
Taco entity looks like:
@Data
@Entity
public class Taco {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private Date createdAt;
@NotNull
@Size(min = 3, message="Name must be at least 3 characters long")
private String name;
@ManyToMany(targetEntity = Ingredient.class)
@Size(min=1, message="You must choose at least 1 ingredient")
private List<Ingredient> ingredients = new ArrayList<>();
@PrePersist
void createdAt(){
this.createdAt = new Date();
}
}
And my entity with Ingredients:
@Data
@RequiredArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@Entity
public class Ingredient {
@Id
private final String id;
private final String name;
@Enumerated(EnumType.STRING)
private final Type type;
public static enum Type{
WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
}
}
This is html page which must create new Taco object with picked ingredients:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Testing Firs Page</title>
</head>
<body>
<h1>Design your taco!</h1>
<img th:src="@{/images/taco.jpg}" alt="myImage"/>
<form method="POST" th:object="${taco}">
<span class="validationError"
th:if="${#fields.hasErrors('ingredients')}"
th:errors="*{ingredients}">Ingredient Error</span>
<div class="grid">
<div class="ingredient-group" id="wraps">
<h3>Designate your wrap:</h3>
<div th:each="ingredient : ${wrap}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="proteins">
<h3>Pick your protein:</h3>
<div th:each="ingredient : ${protein}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="cheeses">
<h3>Choose your cheese:</h3>
<div th:each="ingredient : ${cheese}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="veggies">
<h3>Determine your veggies:</h3>
<div th:each="ingredient : ${veggies}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
<div class="ingredient-group" id="sauces">
<h3>Select your sauce:</h3>
<div th:each="ingredient : ${sauce}">
<input name="ingredients" type="checkbox" th:value="${ingredient.id}" />
<span th:text="${ingredient.name}">INGREDIENT</span><br/>
</div>
</div>
</div>
<div>
<h3>Name your taco creation:</h3>
<input type="text" th:field="*{name}"/>
<span class="validationError"
th:if="${#fields.hasErrors('name')}"
th:errors="*{name}">Name Error</span>
<br/>
<button>Submit your taco</button>
</div>
</form>
</body>
</html>
How can I fix it? Thanks for advance.
Upvotes: 6
Views: 10228
Reputation: 41
package tacos.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import tacos.Ingredient;
import tacos.data.IngredientRepository;
@Component
public class IngredientByIdConverter implements Converter<String, Ingredient> {
private IngredientRepository ingredientRepo;
@Autowired
public IngredientByIdConverter(IngredientRepository ingredientRepo) {
this.ingredientRepo = ingredientRepo;
}
@Override
public Ingredient convert(String id) {
return ingredientRepo.findById(id);
}
}
Upvotes: 2
Reputation: 74
In Spring in Action, you should add IngredientByIdConverter class. this class is convert Ingredient to String.
@Component
public class IngredientByIdConverter
implements Converter<String, Ingredient> {
private IngredientRepository ingredientRepo;
@Autowired
public IngredientByIdConverter(IngredientRepository ingredientRepo) {
this.ingredientRepo = ingredientRepo;
}
@Override
public Ingredient convert(String id) {
return ingredientRepo.findById(id);
}
}
Upvotes: 2
Reputation: 159096
The error is:
Cannot convert value of type
java.lang.String
to required typeorg.server.models.Ingredient
for propertyingredients[0]
You didn't share the code for Taco
or Ingredient
or the payload of the POST
request, so we cannot say for sure what you need to change.
However, if you add a constructor to Ingredient
that takes a String
argument, I believe Spring will use that.
How you create an Ingredient
object from a String
value will of course depend on what the strings content is, so that's entirely up to you to figure out. If you need help with that, create a new question, and include the relevant information, such as the code of your POJO classes and the content of the POST
request.
Upvotes: 0