2

I have a problem with retrieving values from a thymeleaf form in a Spring MVC app. This app has multiple forms, and all but one work fine. The failing one is slightly different in that it has a dropdown that is fed from a list of objects. The UI works OK, the problem is that the POST does not contain any data in the model attribute.

Here are the two POJOs, first the one behind the drop-down:

@Entity
@Table(name = "musicgenre")
public class MusicGenre {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String genre;

    public MusicGenre() {
    }

    public MusicGenre(String genre) {
        this.genre = genre;
    }

    public Integer getId() {
        return id;
    }

    public String getGenre() {
        return genre;
    }

    @Override
    public String toString() {
        return "MusicGenre{" +
                "id=" + id +
                ", genre='" + genre + '\'' +
                '}';
    }
}

and the one to be filled in:

@Entity
@Table(name = "musicwish")
public class MusicWish {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String artist;
    private String song;
    private Integer genreId;
    private String notes;

    public MusicWish() {
    }

    public Integer getId() {
        return id;
    }

    public String getArtist() {
        return artist;
    }

    public String getSong() {
        return song;
    }

    public Integer getGenreId() {
        return genreId;
    }

    public String getNotes() {
        return notes;
    }

    @Override
    public String toString() {
        return "MusicWish{" +
                "id=" + id +
                ", artist='" + artist + '\'' +
                ", song='" + song + '\'' +
                ", genreId=" + genreId +
                ", notes='" + notes + '\'' +
                '}';
    }

    private MusicWish(final Builder builder) {
        this.artist = builder.artist;
        this.song = builder.song;
        this.genreId = builder.genreId;
        this.notes = builder.notes;
    }

    public static class Builder {
        private String artist;
        private String song;
        private Integer genreId;
        private String notes;
        public Builder withArtist(final String artist) { this.artist = artist; return this; }
        public Builder withSong(final String song) { this.song = song; return this; }
        public Builder withGenreId(final Integer genre) { this.genreId = genre; return this; }
        public Builder withNotes(final String notes) { this.notes = notes; return this; }
    }
}

I omit the service class, as that's working fine. The Controller:

@Controller
public class MusicController {

    @Autowired
    private MusicService musicService;

    @RequestMapping(path = "/music-wishlist", method = RequestMethod.GET)
    public ModelAndView getMusicWishlist(Model model) {
        ModelAndView modelAndView = new ModelAndView("music-wishlist");
        modelAndView.addObject("wishes", musicService.getWishes());
        modelAndView.addObject("genres", musicService.getGenreMap());
        return modelAndView;
    }

    @RequestMapping(path = "/music-wish", method = RequestMethod.GET)
    public ModelAndView getMusicWish(Model model) {
        ModelAndView modelAndView = new ModelAndView("music-wish");
        modelAndView.addObject("musicWish", new MusicWish());
        modelAndView.addObject("genreList", musicService.getGenres());
        return modelAndView;
    }

    @RequestMapping(path = "/music-wish", method = RequestMethod.POST)
    public ModelAndView postMusicWish(
            @ModelAttribute("musicWish") MusicWish musicWish) {
        ModelAndView modelAndView = new ModelAndView("music-wishlist");
        modelAndView.addObject("wishes", musicService.getWishes());
        modelAndView.addObject("genres", musicService.getGenreMap());
        return modelAndView;
    }
}

and finally the HTML snippet with the form:

        <form th:action="@{/music-wish}"
              th:object="${musicWish}"
              method="post"
              class="registration-text">

            <p class="registration-input-label"
               th:text="#{music.artist}"></p>
            <input type="text"
                   class="registration-input-text"
                   th:field="*{artist}"/>

            <p class="registration-input-label"
               th:text="#{music.song}"></p>
            <input type="text"
                   class="registration-input-text"
                   th:field="*{song}"/>

            <p class="registration-input-label"
               th:text="#{music.genre}"></p>
            <select th:field="*{genreId}">
                <option
                        th:each="genre: ${genreList}"
                        th:value="${genre.id}"
                        th:text="${genre.genre}" ></option>
            </select>

            <p class="registration-input-label"
               th:text="#{music.notes}"></p>
            <input type="text"
                   class="registration-input-text"
                   th:field="*{notes}"/>
            <p style="margin-top: 20px;">
                <input type="submit" th:value="#{button.submit}" />
                <input type="reset" th:value="#{button.reset}" />
            </p>
        </form>

The problem is when the POST handler receives the musicWish, all fields are still null. The same pattern works fine in other forms, but fails here ... even when I comment the dropdown out. I'd appreciate any help with identifying the bug I places in here.

thx,

Stefan

IndyStef
  • 747
  • 1
  • 6
  • 15

2 Answers2

2

Your view models attributes don't have any setter , you need to provide public setter for fields which you want to bind

1

Your classes MusicWish & MusinGenre do not contain any public setter methods to set values.

I would like to suggest you use LOMBOK jar to make a more clean code. This avoids you to mention getter & Setter along with Constructors in your class. But, it will support you all functionality by just defining the @Getter, @Setter, @NoArgsConstructor & @AllArgsConstructor. For example,

@Entity
@Table(name = "musicgenre")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class MusicGenre {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String genre;
}
Nallamachu
  • 1,420
  • 18
  • 34