• 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
543 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,280 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,280 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,280 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ź 318 wizyt
0 głosów
1 odpowiedź 410 wizyt
pytanie zadane 19 lutego 2019 w Java przez anonymousProgrammer Początkujący (350 p.)
0 głosów
1 odpowiedź 1,373 wizyt
pytanie zadane 26 stycznia 2019 w Java przez niezalogowany

92,452 zapytań

141,262 odpowiedzi

319,078 komentarzy

61,854 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.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...