I am new both to Spring Boot and Thymeleaf, and want to pass the value of a variable defined in the Java code to the HTML page. I searched the Web, but probably, I have overseen something important. I try to do it with the following code:
Favorite.java:
#Getter
#Setter
public class Favorite {
private String id;
private String target;
public Favorite(final String id, final String target) {
setId(id);
setTarget(this.target);
}
}
PortalController.java:
public class PortalController {
private final List<Favorite> myFavorites = new ArrayList<>();
#ModelAttribute("myFavorites")
public List<Favorite> myFavorites() {
if (myFavorites.size() == 0) {
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_ZWD_ESS_ABW", "ABC"));
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_CATS", "DEF"));
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_PEP_WISH_PLAN", "XYZ"));
}
return myFavorites;
}
index.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="de" xml:lang="de">
<head>
[…]
</head>
<body>
[…]
<ul>
<div th:switch="${not #lists.isEmpty(myFavorites)}">
<div th:case="true">
<div th:each="myFavorite : ${myFavorites}">
<li>
<td th:text="${myFavorite.id}"></td>
<td th:text="${myFavorite.task}"></td>
</li>
</div>
</div>
<div th:case="*">
Nothing to show!
</div>
</div>
</ul>
[…]
I get the "Nothing to show!" text, which means that myFavorites is empty. What do I miss or what did I misunderstood about this?
Edit:
I modified the PortalController after reading The #ModelAttribute in Depth to this:
public class PortalController {
private final List<Favorite> myFavorites = new ArrayList<>();
private final Map<String, List<Favorite>> favoritesMap = new HashMap<>();
#RequestMapping(value = "/getMyFavorites", method = RequestMethod.POST)
public String submit(#ModelAttribute("myFavorites") final List<Favorite> favorites,
final BindingResult result, final ModelMap model) {
if (result.hasErrors()) return "error";
model.addAttribute("myFavorites", favorites);
favoritesMap.put(favorites.toString(), favorites);
return "favoritesView";
}
#ModelAttribute
public void getMyFavorites(final Model model) {
if (myFavorites.size() == 0) {
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_ZWD_ESS_ABW", "ABC"));
[…]
}
model.addAttribute("myFavorites", myFavorites);
}
Unfortunately, there is still something missing or misunderstood by me so that the Web page returns still "Nothing to show!".
Edit 2:
This is the current state I have after reading the documentation requested here:
PortalController.java:
public class PortalController {
private final List<Favorite> myFavorites = new ArrayList<>();
#RequestMapping(value = "/getMyFavorites", method = RequestMethod.GET)
public String submit(#ModelAttribute("myFavorite") final List<Favorite> favorites,
final BindingResult result, final ModelMap model) {
if (result.hasErrors()) return "error";
model.addAttribute("myFavorites", favorites);
return "favoritesView";
}
#ModelAttribute
public void getMyFavorites(final Model model) {
if (myFavorites.size() == 0) {
myFavorites.add(new Favorite("ZEMPLOYEE_WORKTIME_ZWD_ESS_ABW", "ABC"));
[…]
}
model.addAttribute("myFavorites", myFavorites);
}
index.html:
<ul>
<div th:switch="${not #lists.isEmpty(myFavorite)}">
<div th:case="true">
<div th:each="myFavorite : ${myFavorites}">
<li>
<td th:text="${myFavorite.id}"></td>
<td th:text="${myFavorite.task}"></td>
</li>
</div>
</div>
<div th:case="false">
Nothing to show!
</div>
</ul>
But I still get "Nothing to show!", as ${myFavorites} is empty.
According to the documentation #ModelAttribute should be used with #RequestMapping. You can find a more detailed description of how this annotation works here.
Also for showing a table you can change your thymeleaf code to:
<table>
<thead>
<tr>
<th> id</th>
<th> target</th>
</tr>
</thead>
<tbody>
<tr th:if="${myFavorites.empty}">
<td colspan="2"> No Info </td>
</tr>
<tr th:each="myFavorite: ${myFavorites}">
<td><span th:text="${myFavorite.id}"> ID</span></td>
<td><span th:text="${myFavorite.target}"> Target</span></td>
</tr>
</tbody>
</table>
Related
I want to fill the form according to the related book when I click the view or update the link on the page. I know, there is a solution with opening another page but I want to do it on the same page. As you can see on the picture below I can properly get the list on the left table. I have tried a post method below but did not work. So what would you recommend to do it?
Controller class:
#PostMapping(path = "/listbooks")
public String getBook(#ModelAttribute BookConfig bookConfig, Model model)
throws IOException {
model.addAttribute("book", bookConfig);
return "list";
}
#GetMapping(path = "/listbooks")
public String showAllBooks(Model model) throws IOException {
model.addAttribute("books", bookService.getBookConfigList());
return "list";
}
HTML file:
<div class="table-responsive" th:if="${not #lists.isEmpty(books)}">
<table class="table table-hover" style="height:50px;">
<thead class="thead-inverse">
<tr>
<th>Name</th>
<th>View</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tr th:each="book : ${books}">
<td th:text="${book.name}">Book Name</td>
<td>View</td>
<td>Update</td>
<td>Delete</td>
</tr>
</table>
</div>
This is what I am trying to do on the HTML file:
<form th:if="${book != null}" th:object="${book}" th:action="#{/book/}"
method="post">
<div class="panel-heading">
<h4 class="panel-title"
">Edit
Book Configuration</h4>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3 form-group"
>
<label>Book name</label>
<input type="text" class="form-control" th:field="*{name}"/>
</div>
...
I have solved using JavaScript, Firstly, I have adjusted the getBook method below
#PostMapping("/books")
public String getBook(#RequestBody String bookName) throws IOException {
return "list";
}
and then I have add these two JS functions:
$(document).ready(function () {
$(".view").click(function () {
var $row = $(this).closest("tr"); // Find the row
var $text = $row.find(".bookname").text(); // get the text on the view link using its the class name
$.post("http://localhost:8081/books",
{
bookName: $text
},
function (data, status) {
assignDataToTable(data);
});
});
});
function assignDataToTable(data) {
alert("hey" + data);
document.getElementById("booknameinput").value = data;
}
I have a problem with passing an instance of "threads" in each loop from thymeleaf to Spring controller using a submit button. I'm trying to solve this by using the annotation #ModelAttribute, but one more instance of MessageThread is creating.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org" xmlns:form="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Форум</title>
</head>
<body>
<form method="post">
<input type="text" name="header">
<input type="text" name="text">
<button name="newThread" type="submit">Создать тред</button>
</form>
<table>
<thead>
<tr>
<th> Тред </th>
<th> ОП пост </th>
</tr>
</thead>
<tbody>
<tr th:if="${threads.isEmpty()}">
<td colspan="2"> Нет доступных тредов </td>
</tr>
<div>
<th:block th:each="thread : ${threads}">
<td th:type="id"><span th:text="${thread.getId()}"></span></td>
<td><span th:text="${thread.getHeader()}"> Title </span></td>
<td><span th:text="${thread.getText()}"> Title </span></td>
<form th:object="${thread}" th:method="post">
<td><button name="inThread" type="submit">В тред</button></td>
</form>
</th:block>
</div>
</tbody>
</table>
</body>
</html>
I can't find a way to pass an instance of "threads" from thymeleaf. All that I want is to press Submit button and pass the ${thread} to "toThread" method.
My controller:
#Controller
public class ThreadController {
private final MessageService messageService;
#Autowired
public ThreadController(MessageService messageService) {
this.messageService = messageService;
}
#GetMapping("/")
public String showThreads(Model model)
{
model.addAttribute("threads", messageService.getThreads());
return "threads-view";
}
#PostMapping(value = "/", params = "newThread")
public String addThread(Model model,
#RequestParam("header") String header,
#RequestParam("text") String text)
{
model.addAttribute("threads", messageService.getThreads());
messageService.addThread(header, text);
return "redirect:/";
}
#PostMapping(value = "/", params = "inThread")
public String toThread(#ModelAttribute("thread") MessageThread thread) {
System.out.println(thread.getId() + " " + thread.getHeader());
return "redirect:/thread:";
}
}
MessageThread class:
package com.project.imageboard.model;
import java.util.ArrayList;
import java.util.List;
public class MessageThread {
private String header;
private String text;
private int id;
private List<Message> messages = new ArrayList<>();
public MessageThread(String header, String text) {
this.header = header;
messages.add(new Message(text));
this.text = text;
this.id = messages.get(0).getId();
}
public int getId() {
return id;
}
public String getText() {
return text;
}
public String getHeader() {
return header;
}
public List<Message> getMessages() {
return messages;
}
public void insertMessage(Message message){
messages.add(message);
}
}
I would be grateful for any help.
your controller seems to be ok, you are mapping a post request to "/" and expecting to receive a MessageThread object which has to be built from the content of the request, the problem is in the template: you are not sending the data in the request from the client to the server, so spring has no way to assign the correct values to a new instance of MessageThread.
If we take this block from your template:
<th:block th:each="thread : ${threads}">
<td th:type="id"><span th:text="${thread.getId()}"></span></td>
<td><span th:text="${thread.getHeader()}"> Title </span></td>
<td><span th:text="${thread.getText()}"> Title </span></td>
<form th:object="${thread}" th:method="post">
<td><button name="inThread" type="submit">В тред</button></td>
</form>
</th:block>
1) The form tag is missing the action attribute, try adding something like th:action="#{/}" to target your request mapping on the server side.
2) You are not sending the actual content of the thread object to the server, for this you have to add input tags with the name of the imput matching the name of the field in the MessageThread object you want to populate. Something like the following:
<input type="hidden" th:field="*{header}" />
<input type="hidden" th:field="*{text}" />
In this example th:field is creating the name and value attributes for the input tags so you don't need to do it manually.
To sum up, there is no way to pass an actual "instance" of an object from the html running in the client to the java app running on the server, you can only send data using HTTP and parse that data on the server. The ModelAttribute annotation is instructing Spring to inspect an object (MessageThread in this case) and find in the data sent through the request the matching values to populate the object.
Hope this helps.
I need to create a simple search button via a custom method using CrudRepository and show the search results on a website.
Event.java
#Entity
#Table(name = "events")
public class Event {
private String name;
}
EventRepository.java
public interface EventRepository extends CrudRepository<Event, Long>{
public Iterable<Event> findByName(String name);
}
EventService.java
public interface EventService {
public Iterable<Event> findByName(String name);
}
EventServiceImpl.java
#Service
public class EventServiceImpl implements EventService {
#Override
public Iterable<Event> findByName(String name) {
return eventRepository.findByName(name);
}
}
EventsController.java
#Controller
#RequestMapping(value = "/events", produces = { MediaType.TEXT_HTML_VALUE })
public class EventsController {
#RequestMapping(value = "/search", method = RequestMethod.GET)
public String showEventsByName(#ModelAttribute Event event, #RequestParam (value = "search", required = false) String name, Model model) {
model.addAttribute("event", event);
model.addAttribute("searchResult", eventService.findByName(name));
return "events/index";
}
index.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/default}">
<head>
<title>All events</title>
</head>
<body>
<div layout:fragment="content">
<h3>Search for an event by name</h2>
<form th:object="${event}" th:action="#{/events/search}" method="get">
<input type="text" name="search" id="search" th:value="${search}"/>
<input type="submit" value="Search"/>
<div th:if="${not #lists.isEmpty(search)}">
<h2>Events search results</h2>
<table class="table table-striped table-hover">
<thead>
<tr>
<th><i class="fa fa-bolt"></i> Event</th>
<th><i class="fa fa-map-marker"></i> Venue</th>
<th><i class="fa fa-calendar"></i> Date</th>
<th><i class="fa fa-clock-o"></i> Time</th>
</tr>
</thead>
<tbody>
<tr th:each="event: ${searchResult}">
<td th:text="${event.name}">My Event</td>
<td th:text="${event.venue.getName()}">Event venue</td>
<td th:text="${{event.date}}">Event date</td>
<td th:text="${{event.time}}">Event time</td>
</tr>
</tbody>
</table>
</div>
</form>
<h1>All events</h1>
<table class="table table-striped table-hover">
<thead>
<tr>
<th><i class="fa fa-bolt"></i> Event</th>
<th><i class="fa fa-map-marker"></i> Venue</th>
<th><i class="fa fa-calendar"></i> Date</th>
<th><i class="fa fa-clock-o"></i> Time</th>
</tr>
</thead>
<tbody>
<tr th:each="e : ${events}">
<td th:text="${e.name}">My Event</td>
<td th:text="${e.venue.getName()}">Event venue</td>
<td th:text="${{e.date}}">Event date</td>
<td th:text="${{e.time}}">Event time</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
I am using the naming convention for custom CrudRepository methods, which means that the implementation should be provided by the interface already.
When I run the web application and submit some input in the form, the desired html view is displayed, but the search result never shows any event.
Picture 1
Picture 2
I tried removing #ModelAttribute Event event and model.addAttribute("event", event); as in this post which addresses the same issue it was not used, but the outcome was the same.
I know that there is a similar post already, but the solution suggested there does not work for me and I tried different approaches but the result was not as expected.
eventService.findByName(name);
Will look for exact name match
Create a new method for searching
Service Interface
public interface EventService {
public Iterable<Event> findByName(String name);
public Iterable<Event> searchForName(String name);
}
Repository
public interface EventRepository extends CrudRepository<Event, Long>{
public Iterable<Event> findByName(String name);
public Iterable<Event> findByNameContainingIgnoreCase(String name);
}
Service Class
#Service
public class EventServiceImpl implements EventService {
#Override
public Iterable<Event> findByName(String name) {
return eventRepository.findByName(name);
}
#Override
public Iterable<Event> searchForName(String name) {
return eventRepository.findByNameContainingIgnoreCase(name);
}
}
Update:
When searching for nothing, do you want to display nothing or everything? I prefer the latter. If you want to not show anything, check for the search parameter before calling the service.
#RequestMapping(value = "/search", method = RequestMethod.GET)
public String showEventsByName(#RequestParam (value = "search", required = false) String name, Model model) {
if(name!=null && !name.isEmpty()){
model.addAttribute("searchResult", eventService.searchForName(name));
} // else{ } create an empty list or handle null in view
// ...
}
if you want to hide the table header if there are no results use
th:if="${searchResult !=null and !searchResult.isEmpty()}" <!-- For Both -->
Problem solved, thanks guys ;) I EDITED THE CODE, LOOK AT THE BOTTOM
I am a Spring newbie and I am a little bit confused about using an object I already created through different controller in Spring MVC. I wrote a simple project that allow user to insert some car's details(brand, model and license plate number) and to see the list of submitted records (plus a reset all action and a selective delete of a single record). Now I also want to edit the values of a record but things get tough for me..
VeicoloController.java
#Controller
#RequestMapping("/veicolo")
#SessionAttributes("veicoli")
public class VeicoloController {
#Autowired VeicoloValidator veicoloValidator;
#GetMapping({"", "lista"})
public String lista() {
return "/veicolo/lista_veicoli";
}
#GetMapping("inserisci_veicolo")
public ModelAndView inserisci(){
return new ModelAndView("veicolo/inserisci_veicolo", "modelVeicolo", new Veicolo());
}
#PostMapping("save")
public ModelAndView salvaVeicolo(#ModelAttribute("modelVeicolo") Veicolo veicolo, BindingResult bindingResult,Model model)
{
System.out.println("Veicolo = " + veicolo);
veicoloValidator.validate(veicolo, bindingResult);
if (bindingResult.hasErrors()){
return new ModelAndView("veicolo/inserisci_veicolo");
}
else{
Set<Veicolo> veicoli;
if (!model.containsAttribute("veicoli")){
veicoli = new HashSet<>();
model.addAttribute("veicoli", veicoli);
}
else{
veicoli = (Set<Veicolo>) model.asMap().get("veicoli");
}
veicoli.add(veicolo);
return new ModelAndView("veicolo/lista_veicoli");
}
}
Now, here there are my problematic Controllers:
I use this controller to edit the row, using the license plate number as unique element to retrieve that particular vehicle object and passing it by querystring from the jsp(i'll show it later). In this way i can show the vehicle attribute in the edit forms (and hiding the license plate number form that cannot be modified)
#GetMapping("modifica")
public ModelAndView modificaVeicolo(#RequestParam("targa")String targa, Model model){
Set<Veicolo> veicoli = (Set<Veicolo>)model.asMap().get("veicoli");
Veicolo veicoloMod = null;
for(Veicolo v:veicoli){
if(v.getTarga().equalsIgnoreCase(targa)){
veicoloMod = v;
break;
}
model.addAttribute(veicoloMod);
}
return new ModelAndView("veicolo/modifica_veicolo", "modelVeicolo", veicoloMod);
}
This is how I tried to do the update Controller, I really don't know how to use the object I got in the previous controller that give me (i suppose) all the information I need of the car
#PostMapping("update")
public ModelAndView updateVeicolo(#ModelAttribute("modelVeicolo")Veicolo veicolo, BindingResult bindingResult,Model model)
{
// Veicolo veicoloToUpdate = (Veicolo)model.asMap().get("veicoli");
veicoloValidator.validate(veicolo, bindingResult);
if (bindingResult.hasErrors())
{
return new ModelAndView("veicolo/modifica_veicolo");
}
else
{
Set<Veicolo> veicoli =(Set<Veicolo>)model.asMap().get("veicoli");
veicoli.add(veicolo);
}
return new ModelAndView("veicolo/lista_veicoli");
}
modifica_veicolo.jsp
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Modifica il veicolo</title>
</head>
<body>
<h1>Modifica il veicolo</h1>
<br>
<br>
<form:form method="POST" action="${pageContext.request.contextPath}/veicolo/update" commandName="modelVeicolo" ModelAttribute="modelVeicolo">
<table>
<tr>
<p class="errorLine">
<form:errors path="marca" cssStyle="color: #ff0000"/>
</p>
<td><form:label path="marca">Marca</form:label></td>
<td><form:input path="marca" /></td>
</tr>
<tr>
<p class="errorLine">
<form:errors path="modello" cssStyle="color: #ff0000"/>
</p>
<td><form:label path="modello">Modello</form:label></td>
<td><form:input path="modello"/></td>
</tr>
<td><form:input type="hidden" path="targa" value="${veicolo.targa}" /></td>
<tr>
<td colspan="3">
<input type="submit" value="Submit"/>
</td>
</tr>
</table>
</form:form>
<br>
<br>
Torna alla Home
</body>
</html>
and lista_veicoli.jsp (from the list you can edit the record)
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Lista veicoli</title>
</head>
<body>
<h1>Lista Veicoli</h1>
<br>
<br>
<c:if test="${veicoli == null || veicoli.isEmpty()}">
Nessun veicolo registrato.
</c:if>
<div> </div>
<table>
<thead>
<tr>
<th>| Marca </th>
<th>| Modello |</th>
<th> Targa |</th>
</tr>
</thead>
<c:forEach var="veicolo" items="${veicoli}">
<tr>
<td>
${veicolo.marca}
</td>
<td>
${veicolo.modello}
</td>
<td>
${veicolo.targa}
</td>
<td>
<a href="${pageContext.request.contextPath}/veicolo/modifica?targa=${veicolo.targa}" > modifica </a>
</td>
<td>
<a href="${pageContext.request.contextPath}/veicolo/rimuovi?targa=${veicolo.targa}" > elimina </a>
</td>
</tr>
</c:forEach>
</table>
<br>
<br>
<p>Torna alla Home |</p>
<p>Cancella Lista |</p>
<p>Inserisci un altro veicolo |</p>
</body>
</html>
I am stuck now, if I click on the update button nothing happen and I doubt that things can be done way too better than this. I need your help :'(
Also, sorry for the long post!
EDIT:
ok, i changed this controller and it seems to work, but only for the first record inserted!
#PostMapping("update")
public ModelAndView updateVeicolo(#ModelAttribute("modelVeicolo")Veicolo veicolo,
BindingResult bindingResult,Model model)
{
veicoloValidatorNoTarga.validate(veicolo, bindingResult);
if (bindingResult.hasErrors())
{
return new ModelAndView("veicolo/modifica_veicolo");
}
else
{
Set<Veicolo> veicoli =(Set<Veicolo>)model.asMap().get("veicoli");
Veicolo daInserire = veicolo;
Veicolo daRimuovere = null;
for(Veicolo v : veicoli)
{
if(v.getTarga().equalsIgnoreCase(veicolo.getTarga())){
daRimuovere = v;
break;
}
}
veicoli.remove(daRimuovere);
veicoli.add(daInserire);
}
return new ModelAndView("veicolo/lista_veicoli");
}
If i want to edit the second or more element of the set an exception occurs. It says "HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException: Model object must not be null".
In fact in the modificaVeicolo Controller at the line
for(Veicolo v:veicoli){
if(v.getTarga().equalsIgnoreCase(targa)){
veicoloMod = v;
break;
}
targa value is the correct value of the second or third(etc) car, but v has the value of the first car's license plate! In theory it should have the value of the selected car
...any suggestions?
Key Point::If you choose to use HashSet storing Objects,the Class has to override equals and hashcode!
1.Veicolo.java
Override equlas and hashcode
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Veicolo veicolo = (Veicolo) o;
return targa.equals(veicolo.getTarga());
}
#Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + getTarga().hashCode();
return result;
}
2.VeicoloController.updateVeicolo()
Update object By deleting and adding
#PostMapping("update")
public ModelAndView updateVeicolo(#ModelAttribute("modelVeicolo") Veicolo veicolo, BindingResult bindingResult,
Model model) {
// Veicolo veicoloToUpdate = (Veicolo)model.asMap().get("veicoli");
veicoloValidator.validate(veicolo, bindingResult);
if (bindingResult.hasErrors()) {
return new ModelAndView("veicolo/modifica_veicolo");
} else {
Set<Veicolo> veicoli = (Set<Veicolo>) model.asMap().get("veicoli");
if (veicoli.contains(veicolo)) {
veicoli.remove(veicolo);
veicoli.add(veicolo);
}
}
return new ModelAndView("veicolo/lista_veicoli");
}
3.VeicoloController.modificaVeicolo()
Move the model.addAttribute into the loop
#GetMapping("modifica")
public ModelAndView modificaVeicolo(#RequestParam("targa") String targa, Model model) {
Set<Veicolo> veicoli = (Set<Veicolo>) model.asMap().get("veicoli");
Veicolo veicoloMod = null;
for (Veicolo v : veicoli) {
if (v.getTarga().equalsIgnoreCase(targa)) {
veicoloMod = v;
model.addAttribute(veicoloMod);
break;
}
}
return new ModelAndView("veicolo/modifica_veicolo", "modelVeicolo", veicoloMod);
}
I have a spring form with having backing object for it. The form is like this-
<sf:form cssClass="form-horizontal" commandName="campaignModel" method="post">
<sf:input path="campaign.name" class="form-control" />
<sf:input path="landingPageModels.landingPage.url" class="form-control" />
</sf:form>
Model class(Form backing Object) -
CampaignModel.java
public class CampaignModel {
private Campaign campaign = new CampaignImpl();
private List<LandingPageModel> landingPageModels = new Arraylist<LandingPageModel>;
public Campaign getCampaign() {
return campaign;
}
public void setCampaign(Campaign campaign) {
this.campaign = campaign;
}
public List<LandingPageModel> getLandingPageModels() {
return landingPageModels;
}
public void setLandingPageModels(List<LandingPageModel> landingPageModels) {
this.landingPageModels = landingPageModels;
}
LandingPageModel.java is -
public class LandingPageModel {
private LandingPage landingPage = new LandingPageImpl();
private List<LandingPageParameterImpl> landingPageParameters = new ArrayList<LandingPageParameterImpl>();
public LandingPage getLandingPage() {
return landingPage;
}
public void setLandingPage(LandingPage landingPage) {
this.landingPage = landingPage;
}
public List<LandingPageParameterImpl> getLandingPageParameters() {
return landingPageParameters;
}
public void setLandingPageParameters(List<LandingPageParameterImpl> landingPageParameters) {
this.landingPageParameters = landingPageParameters;
}
}
LandingPage.java is -
public class LandingPageImpl extends EntityImpl implements LandingPage {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
} }
So i want that i can insert many objects of landingPage (having their own url property) in landingPageModels list. That means i can have mulitple input tag having url property like this -
<sf:input path="landingPageModels.landingPage.url" class="form-control" />
<sf:input path="landingPageModels.landingPage.url" class="form-control" />
<sf:input path="landingPageModels.landingPage.url" class="form-control" />
But when executing this code, spring gives me error that landingPage property of landingPageModels has not getter setter method. How to solve it and how to take multiple value like this ?
In order to bind a list model property to multiple input fields, you need this in the rendered form:
<input type="text" name="landingPageModels[0].landingPage.url" class="form-control" />
<input type="text" name="landingPageModels[1].landingPage.url" class="form-control" />
<input type="text" name="landingPageModels[2].landingPage.url" class="form-control" />
Which is accomplished by:
<c:forEach items="${campaignModel.landingPageModels}" varStatus="s">
<sf:input path="landingPageModels[${s.index}].landingPage.url" class="form-control" />
</c:forEach>
The error you are getting is correct as landingPageModels is a list.
You would need to use index access like this landingPageModels[0].landingPage.url.
If you have dynamic number of input/url, then you can use <c:forEach> to create dynamic input variable names
<c:forEach items="${campaignModel.landingPageModels}" var="landingPage">
<sf:input path="landingPage.url" class="form- control" />
</c:forEach>
Will the above not works for you to view data? To get them in controller you may have to use dynamic table row concept in HTML or for each single LandingPage entry add to form bean object by clicking add button and render back.
In my case Person command object having List<Token> property, in order to bind the list of tokens we have designed page as attached screen shot below, clicking on Add button hits the controller and add the each token List<Token> and render back to same view and displays added token in list view, it facilitates to add multiple token for Person.
I dont know how to do it with spring form lib input but if you want to bind using simple html input than you can bind list like this
Simple.jsp
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
</head>
<body>
<form:form method="post" action="save.html" modelAttribute="contactForm">
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
<tr>
<td><input name="contacts[0].firstname" /></td>
<td><input name="contacts[0].lastname" /></td>
</tr>
<tr>
<td><input name="contacts[1].firstname" /></td>
<td><input name="contacts[1].lastname" /></td>
</tr>
</table>
<br/>
<input type="submit" value="Save" />
</form:form>
</body>
</html>
#controller :
#RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView save(#ModelAttribute("contactForm") ContactForm contactForm) {
List<Contact> contacts = contactForm.getContacts();
if(null != contacts && contacts.size() > 0) {
ContactController.contacts = contacts;
for (Contact contact : contacts) {
System.out.printf("%s \t %s \n", contact.getFirstname(), contact.getLastname());
}
}
return new ModelAndView("show_contact", "contactForm", contactForm);
}
ContactForm.java
import java.util.List;
public class ContactForm {
private List<Contact> contacts;
public List<Contact> getContacts() {
return contacts;
}
public void setContacts(List<Contact> contacts) {
this.contacts = contacts;
}
}
Contact.java
public class Contact {
private String firstname;
private String lastname;
private String email;
private String phone;
public Contact() {
}
//getters and setters
}