mrSmith91
mrSmith91

Reputation: 368

How to pass id to th:value and get data of type List<Ingredient>?

I'm learning Spring boot from Spring in Actions 5th, Chapter 3, JDBC.

I have Taco class:

@Data
public class Taco {

    private Long id;
    private Date createdAt;

    @NotBlank
    @Size(min = 5, message = "Name must be at least 5 characters long")
    private String name;

    @Size(min = 1, message = "You must choose at least 1 ingredient")
    @NotNull(message = "You must choose at least 1 ingredient")
    private List<Ingredient> ingredients;   
}

And Ingredient class:

@Data
@AllArgsConstructor
public class Ingredient {

    private String id;
    private String name;
    private Type type;

    public static enum Type{
        WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
    }
}

I use Thymeleaf in front end, like this:

<form method="POST" th:object="${design}">

    <span class="validationError"
              th:if="${#fields.hasErrors('ingredients')}"
              th:errors="*{ingredients}">Ingredients Error</span>                                                

    <div class="grid">

        <!-- WRAP -->
        <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>
            </div>
        </div>

        <!-- PROTEIN -->
        <div class="ingredient-group" id="proteins">
            <h3>Designate your PROTEIN:</h3>
            <div th:each="ingredient : ${protein}" >
                <input name="ingredients" type="checkbox" th:value="${{ingredient.id}}" />
                <span th:text="${ingredient.name}">INGREDIENT</span>
            </div>
        </div>  

And my controller

@GetMapping
public String showDesignForm(Model theModel) {

    List<Ingredient> ingredients = new ArrayList<>();

    ingredientRepository.findAll().forEach(i -> ingredients.add(i));

    Type[] types = Ingredient.Type.values();

    for(Type type : types) {
        theModel.addAttribute(
                type.toString().toLowerCase(), 
                filterByType(ingredients, type));
    }

    theModel.addAttribute("design", new Taco());

    return "design";
}

My JdbcIngredientRepository

@Repository
public class JdbcIngredientRepository implements IngredientRepository, RowMapper<Ingredient>{

    private JdbcTemplate jdbc;

    @Autowired
    public JdbcIngredientRepository(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    @Override
    public Iterable<Ingredient> findAll() {

        String sqlQuery = "select id, name, type from Ingredient";

        return jdbc.query(sqlQuery, this::mapRow);
    }

The problem is when I submit, I got error message: Can't convert from List<String> to List<Ingredient>.

I can solve this problem by changing private List<Ingredient> ingredients; to private List<String> ingredients;. But I think it is a bad practice? Anyone have better way to directly pass data that selected from th:value to List ?

Upvotes: 0

Views: 658

Answers (1)

marstran
marstran

Reputation: 28036

You can just convert the strings from the repository to ingredients by mapping the result of ingredientRepository.findAll().

@GetMapping
public String showDesignForm(Model theModel) {

    List<Ingredient> ingredients = 
        ingredientRepository.findAll()
            .stream()
            .map(ingredientName -> new Ingredient(ingredientName))
            .collect(Collectors.toList());

    Type[] types = Ingredient.Type.values();

    for(Type type : types) {
        theModel.addAttribute(
                type.toString().toLowerCase(), 
                filterByType(ingredients, type));
    }

    theModel.addAttribute("design", new Taco());

    return "design";
}

Upvotes: 1

Related Questions