• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Spring MVC + thymeleaf -> jak wyciągnąć listę z widoku

VPS Starter Arubacloud
0 głosów
583 wizyt
pytanie zadane 26 stycznia 2020 w Java przez niezalogowany

Hej. Używając Springa MVC, przekazuję do widoku kilka list obiektów. Użytkownika może wybrać po jednym obiekcie (które reprezentuje zwykły napis na stronie) z każdej listy. Następnie wysyłając metodą POST, strona musi przechwycić formularz i z  tych wybranych obiektów utworzyć nową listę. Jednak przy próbuje wysłania formularza otrzymuję komunikat "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 tacos.domain.Ingredient for property ingredients[0]: no matching editors or conversion strategy found".

 

@ModelAttribute(name = "taco")
public Taco taco() {
    return new Taco();
}

@GetMapping
    public String showDesignForm(Model model) {
        List<Ingredient> ingredients = new ArrayList<>();
        ingredientRepository.findaAll().forEach(ingredients::add);
        Type[] types = Ingredient.Type.values();
        for (Type type : types) {
            model.addAttribute(type.toString().toLowerCase(), filterByType(ingredients, type));
        }
        return "design";
    }
@PostMapping
    public String processDesign(@Valid Taco taco, Errors errors, @ModelAttribute Order order) {
        if (errors.hasErrors()) {
            return "design";
        }
        Taco saved = designRepo.save(taco);
        order.addDesign(saved);
        return "redirect:/orders/current";
    }



Widok html:

 

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Aplikacja Taco Cloud</title>
</head>
<body>
    <h1>Przygotuj własne Taco!</h1>
    <img th:src="@{/images/TacoCloud.png}" alt="">
    <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>Wybierz rodzaj mąki:</h3>
                <div th:each="ingredient : ${wrap}">
                    <input type="checkbox" th:name="ingredients" th:value="${ingredient.id}"/>
                    <span th:text="${ingredient.name}">Składnik</span><br/>
                </div>
            </div>
            <div class="ingredient-group" id="proteins">
                <h3>Wybierz mięso:</h3>
                <div th:each="ingredient : ${protein}">
                    <input type="checkbox" th:name="ingredients" th:value="${ingredient.id}"/>
                    <span th:text="${ingredient.name}">Składnik</span>
                </div>
            </div>
            <div class="ingredient-group" id="cheeses">
                <h3>Wybierz sery:</h3>
                <div th:each="ingredient : ${cheese}">
                    <input type="checkbox" th:name="ingredients" th:value="${ingredient.id}" />
                    <span th:text="${ingredient.name}">Składnik</span><br>
                </div>
            </div>
            <div class="ingredient-group" id="veggies">
                <h3>Wybierz warzywa:</h3>
                <div th:each="ingredient : ${veggies}">
                    <input type="checkbox" th:name="ingredients" th:value="${ingredient.id}" />
                    <span th:text="${ingredient.name}">Składnik</span><br>
                </div>
            </div>
            <div class="ingredient-group" id="sauces">
                <h3>Wybierz sosy:</h3>
                <div th:each="ingredient : ${sauce}">
                    <input type="checkbox" th:name="ingredients" th:value="${ingredient.id}"/>
                    <span th:text="${ingredient.name}">Składnik</span>
                </div>
            </div>
        </div>
        <div>
        <h3>Nadaj nazwę przygotowanemu taco:</h3>
        <input type="text" th:field="*{name}"/>
            <span class="validationError"
                  th:if="${#fields.hasErrors('name')}"
                  th:errors="*{name}">Name Error</span>
            <br/>
        <br>
        <button>Wyślij swoje zamówienie</button>
        </div>
    </form>
</body>
</html>

 

komentarz 27 stycznia 2020 przez mbabane Szeryf (79,300 p.)

Pokaż obiekt Taco Ingredient. A także tę metodę filterByType.

Inna uwaga:

 ingredientRepository.findaAll()

powyższe najpewniej zwraca listę, więc możesz zrobić tak:

List<Ingredient> ingredients = ingredientRepository.findaAll();

I inna rzecz to te checkboxy zdaje się, że nie mają atrybutu th:field, więc nawet jeśli je wyślesz to nie zostaną przypisane do żadnej zmiennej/obiektu.

komentarz 27 stycznia 2020 przez niezalogowany
private List<Ingredient> filterByType(List<Ingredient> ingredients, Type type) {

        return ingredients.stream()
                .filter(x -> x.getType().equals(type))
                .collect(Collectors.toList());

    }

 

@Data
public class Taco {

    private Long id;
    private Date createdAt;
    private String name;
    private List<Ingredient> ingredients;
}
@Data
@RequiredArgsConstructor
public class Ingredient {

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

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


 

1 odpowiedź

0 głosów
odpowiedź 28 stycznia 2020 przez mbabane Szeryf (79,300 p.)

Pierwsze co bym zrobił to dodał @NoArgsConstructor do Ingredient.

komentarz 28 stycznia 2020 przez niezalogowany
Nie wiem, dlaczego to nie działa :( Dawno nie robiłem nic z frontem.
komentarz 28 stycznia 2020 przez mbabane Szeryf (79,300 p.)

Tutaj zdaje się chodzi o to że ten Ingredient jest tak na prawdę złożonym obiektem, a w momencie kiedy thymeleaf chce wrzucić wartość z checkbox'a nie jest w stanie go utworzyć. Wartość w checkboxie to prosty String a lista w Taco zawiera złożony obiekt. Jednym z rozwiązań (bo pewnie jest ich więcej jak zwykle) będzie stworzenie obiektu przejściowego z formularza, a potem w kontrolerze zrobisz z niego docelowy obiekt, do bazy danych. W Twoim przypadku pewnie można to zrobić tak:

@Data
public class TacoForm {
    private String name;
    private List<Long> ingredients;
}

Powyższy obiekt trzeba odpowiednio zbindować z formularzem.

W kontrolerze musisz go następnie zamienić na docelowy obiekt Taco. Innego sposobu na tę chwilę nie znam, być może jest i szukałbym go w dokumentacji thymeleafa (Takie obiekty przejściowe są dość częstą praktyką więc na stacku możesz się natknąć na podobne rozwiązanie).

https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#integrating-thymeleaf-with-spring

(Zauważyłem że uzywasz typu java.utill.Date polecam wymienić go na java.time.LocalDate lub java.time.LocalDateTime (jeśli chcesz mieć datę i czas)).

Podobne pytania

0 głosów
1 odpowiedź 349 wizyt
0 głosów
1 odpowiedź 468 wizyt
pytanie zadane 19 lutego 2019 w Java przez anonymousProgrammer Początkujący (350 p.)
0 głosów
1 odpowiedź 1,572 wizyt
pytanie zadane 26 stycznia 2019 w Java przez niezalogowany

92,973 zapytań

141,938 odpowiedzi

321,180 komentarzy

62,301 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...