Submit a List from a form generated with JavaScript with a Spring controller [duplicate] - spring

This question already has answers here:
Passing a list generated with Javascript to a Spring controller
(3 answers)
Closed 5 years ago.
I'm building an app with Spring Boot, the MVC pattern, and Thymeleaf as template engine. I have a form that generates a list with JavaScript, and a controller with a #ModelAttribute expecting a list, to be saved into the database.
At the JavaScript side I collect the items with this array:
groupList = [];
groupList.push(inputValue);
function getGroupList(){
return groupList;
}
In the html I'm trying to assign the array value to a hidden input field:
<input type="hidden" class="invisible" id="groupList" th:field="*{groupList}"/>
With this inline function:
<script th:inline="javascript" type="text/javascript">
$(function() {
var groupListCollected = getGroupList();
$("#groupList").val(groupListCollected);
});
</script>
My problem at this point, is that I don't find the way to collect the array, and pass it to the Controller as a list.
This is the value that the console shows, for the input field:
<input type="hidden" class="invisible" id="groupList" name="groupList"
value="[object HTMLInputElement]">
Any advice to face this issue, would be much appreciated. Thanks in advance.

You can proceed like this :
Create a Model, for exemple:
public class User {
private String username;
public User() {
}
public User(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Create a FormModel:
public class FormUsers {
private List<User> listUsers;
public FormUsers(List<User> listUsers) {
this.listUsers = listUsers;
}
public FormUsers() {
}
public List<User> getListUsers() {
return listUsers;
}
public void setListUsers(List<User> listUsers) {
this.listUsers = listUsers;
}
#Override
public String toString() {
return "FormUsers{" + "listUsers=" + listUsers + '}';
}
}
in your controller (Get and Post) :
#RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(ModelMap model) {
FormUsers formUsers = new FormUsers();
List<User> list = new ArrayList<>();
list.add(new User("Wassim"));
formUsers.setListUsers(list);
logger.debug("--> " + formUsers.toString());
model.addAttribute("formUsers", formUsers);
return "hello";
}
#RequestMapping(value = "/hello", method = RequestMethod.POST)
public String helloPost(#ModelAttribute FormUsers formUsers, ModelMap model) {
logger.debug("Get List of new users --> " + formUsers.toString());
return "hello";
}
In my view (hello.html) : for listing all users :
<button id="addAction">ADD</button>
<form action="#" th:action="#{/hello}" th:object="${formUsers}" method="post">
<ul id="myUl">
<input type="text" th:field="*{listUsers[0].username}" />
</ul>
<input type="submit" value="Submit" />
</form>
Now if I want to add another user with javascript when I clic in the button 'ADD, I will append() a new 'li' element containing the new user :
<script type="text/javascript">
$(document).ready(function () {
console.log("ready!");
$("#addAction").click(function () {
console.log("FUNCTION");
$("#myUl").append('<li><input id="listUsers1.username" name="listUsers[1].username" value="Rafik" type="text"/></li>');
});
});
</script>
When I Submit, I will get a list with 2 User : 'Wassim' & 'Rafik'.
You can manage this exemple (also the index of the list must managed properly) to append your list with the needed DATA.

Related

Sending user to a new page after submission

After submitting a new Episode entity I need my user to be sent back to the dashboard - ie /dashboard
I have tried
return /dashboard
but that simply sent the template to the browser without any data.
My controller looks like this:
public class formController {
private EpisodeRepository episodeRepository;
#Autowired
public void setEpisodeRepository(EpisodeRepository episodeRepository) {
this.episodeRepository = episodeRepository;
}
#GetMapping("/newEpisode")
public String episodeForm(Model model) {
model.addAttribute("episode", new Episode());
return "episode";
}
#PostMapping("/newEpisode")
public String episodeSubmit(#ModelAttribute Episode episode) {
episodeRepository.save(episode);
return "dashboard";
}
#GetMapping("/dashboard")
public String dashboard(Model model) {
List<Episode>listEpisodes = new ArrayList<>();
episodeRepository.findAll().forEach((episode) -> {
listEpisodes.add(episode);
});
System.out.println(listEpisodes.size());
model.addAttribute("episodes", listEpisodes);
return "dashboard";
}
#GetMapping("/event/{eventno}")
public String findEvent(#PathVariable String eventno, Model model) {
Episode episode = new Episode();
episode = episodeRepository.findByeventno(eventno);
System.out.println("Printing the values held in episode for one get: " +eventno);
model.addAttribute("episode", episode);
return "episode";
}
}
And the relevant part of the html
<form id="form" class="form-horizontal" method="post" action="#" th:action="#{/newEpisode}" th:object="${episode}">
And the answer is:
#PostMapping("newEpisode")
public String episodeSubmit(#ModelAttribute Episode episode) {
episodeRepository.save(episode);
return "redirect:/dashboard";
}

Spring + Thymeleaf type Converter in a form

I'm trying to use a type converter in a Spring boot app and using Thymeleaf but I can't get it working. I've put some code on Github so you can see exactly what I'm trying to do. This is Spring 1.5.1 and Thymeleaf 3.0.3. https://github.com/matthewsommer/spring-thymeleaf-simple-converter
Basically this code is just trying to add a person to a comment object. The person object is null when it gets posted and I don't understand why.
Something that's odd is that the ID of the person isn't being added to the value attribute but it is if th:field="*{body}" is removed. I think it has to do with this: https://github.com/thymeleaf/thymeleaf/issues/495 but I'm currently trying to add BindingResult and it's not working...
My HTML is:
<body>
<div th:if="${personObject != null}" th:text="${personObject.name}"></div>
<form th:action="#{/}" th:object="${comment}" method="post">
<input type="hidden" th:if="${personObject != null}" th:value="${personObject.id}" th:field="*{person}" />
<textarea id="comment" placeholder="Comment..." th:field="*{body}"></textarea>
<button id="comment_submit" type="submit">Comment</button>
</form>
<div th:text="${comment.body}"></div>
</body>
My controller:
#Controller
public class HomeWebController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHome(final HttpServletRequest request, final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
model.put("personObject", new Person(1, "John Smith"));
return "Home";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String postHome(final HttpServletRequest request, final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
model.put("commentBody", comment.getBody());
model.put("person", comment.getPerson());
return "Home";
}
}
And the converter:
#Component
public class StringToPersonConverter implements Converter<String, Person> {
#Autowired
public StringToPersonConverter() { }
#Override
public Person convert(String id) {
if(id == "1") {
Person person = new Person(1, "John Smith");
return person;
}
return null;
}
}
Hi finally I had to do some changes to make it work, but this is the result class by class.
ConvertorApplication:
#SpringBootApplication
#Configuration
#EnableWebMvc
public class ConvertorApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(ConvertorApplication.class, args);
}
//Add converter and configuration annotation
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToPersonConverter());
}
}
StringToPersonConverter:
#Override
public Person convert(String id) {
//Never compare String with == use equals, the "==" compares memory space not the values
if(id.equals("1")) {
Person person = new Person(1, "John Smith");
return person;
}
return null;
}
HomeWebController
#Controller
public class HomeWebController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHome(final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
//Initialize the comment with the person inside, no need of personObject object
model.put("comment", new Comment(new Person(1, "John Smith")));
return "Home";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String postHome(final Map<String, Object> model,
#ModelAttribute(value = "comment") Comment comment,
#RequestParam(value = "person.id") Person person) {
//from the view retrieve the value person.id which will be used by the converter to build the Person entity
comment.setPerson(person);
model.put("comment", comment);
return "Home";
}
}
Comment (Add empty constructor)
public Comment(){}
Person (Add empty constructor)
public Person(){}
Home.jsp (Basically remove personObject, not need)
<!DOCTYPE html>
<html xmlns:th="//www.thymeleaf.org">
<body>
<div th:text="${comment.person.name}"></div>
<form th:action="#{/}" th:object="${comment}" method="post">
<input type="hidden" th:field="*{person.id}" />
<textarea id="comment" placeholder="Comment..." th:field="*{body}"></textarea>
<button id="comment_submit" type="submit">Comment</button>
</form>
<div th:text="${comment.body}"></div>
</body>
</html>
That's would be everything to make it work.

Can JSP file return a json object for a ajax call?

I want to call a jsp file to return a json object to my ajax call. is it possible?
if it is possible, could you please share some example code of both the jsp file and jquery ajax code? Thanks in advance.
Lots and lots of example is in web , guess you are Little lazy in searching let me share an example, And i am using a servlet for the server side
Create a bean,
public class Countries {
public Countries(String code,String name)
{
this.setCode(code);
this.setName(name);
}
public Countries() {}
private String code;
private String name;
public void setCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
And DAO class to add the data from the database or even hardcode the data,
ArrayList<Countries> countryList = new ArrayList<Countries>();
while(rs.next()) {
Countries country=new Countries();
country.setCode(rs.getString("Code"));
country.setName(rs.getString("Name"));
countryList.add(country);
}
After that create a servlet , which gets the data from the DAO and sends it to the JSP ,
ArrayList<Countries> country=new ArrayList<Countries>();
country=FetchData.getAllCountries();
Gson gson = new Gson();
JsonElement element = gson.toJsonTree(country, new TypeToken<List<Countries>>() {}.getType());
JsonArray jsonArray = element.getAsJsonArray();
response.setContentType("application/json");
response.getWriter().print(jsonArray);
}
And finally a JSP to view ,
<script type="text/javascript">
$(document).ready(function() {
$("#tablediv").hide();
$("#showTable").click(function(event){
$.get('PopulateTable',function(responseJson) {
if(responseJson!=null){
$("#countrytable").find("tr:gt(0)").remove();
var table1 = $("#countrytable");
$.each(responseJson, function(key,value) {
var rowNew = $("<tr><td></td><td></td></tr>");
rowNew.children().eq(0).text(value['code']);
rowNew.children().eq(1).text(value['name']);
});
}
});
$("#tablediv").show();
});
});
</script>
<input type="button" value="Show Table" id="showTable"/>
<div id="tablediv">
<table cellspacing="0" id="countrytable">
<tr>
<th scope="col">Code</th>
<th scope="col">Name</th>
</tr>
</table>
</div>
Hope it helps !!

NullPointer on Multipartfile with Spring MVC

It seems my case is simple, so I'm guessing the problem is stupid. Nonetheless...I don't see it.
I'm trying to accept the upload of a collection of files and have been looking at this article as a guide:
http://viralpatel.net/blogs/spring-mvc-multiple-file-upload-example/
Here's the form upload client side:
<form:form method="post" action="save.html" modelAttribute="uploadArtifact" enctype="multipart/form-data">
<div class="controls" id="artifacts">
<input type="file" id="uploader" class="multi"/>
</div>
<div>
<input type="submit" value="Upload" />
</div>
</form:form>
Here's the Model/Bean code:
public class ArtifactModel {
private List<MultipartFile> artifacts;
public List<MultipartFile> getArtifacts() {
return artifacts;
}
public void setArtifacts(List<MultipartFile> artifacts) {
this.artifacts = artifacts;
}
}
...and the controller code:
#Controller
public class ArtifactController {
private static Log logger = LogFactory.getLog(ArtifactController.class);
// #Autowired
// ArtifactService artifactService;
#RequestMapping(value = "/getUploadDialog", method = RequestMethod.GET)
public String displayForm() {
return "uploadArtifacts";
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public void save(
#ModelAttribute("uploadArtifact") ArtifactModel uploadArtifact,
Model map) {
logger.info("Received file upload request with "+uploadArtifact.getArtifacts().size()+" files");
List<MultipartFile> files = uploadArtifact.getArtifacts();
List<String> fileNames = new ArrayList<String>();
if (null != files && files.size() > 0) {
for (MultipartFile multipartFile : files) {
String fileName = multipartFile.getOriginalFilename();
fileNames.add(fileName);
// Handle file content - multipartFile.getInputStream()
logger.info("Received file with filename: " + fileName);
}
}
map.addAttribute("files", fileNames);
}
}
I'm getting a NullPointerException when I attempt to call the getter on the ArtifactModel object which should have come from the uploadArtifact #ModelAttribute parameter...
I've looked in the browser debugger and seen that the client is sending the data...
Suggestions?
I think you're missing an attribute from your file input element.
Try adding:
name='artifacts[0]'

List<Foo> as form backing object using Spring 3 MVC, correct syntax?

I want to do something like this, where Foo is a class with one String field name, and getter/setter:
<form:form id="frmFoo" modelAttribute="foos">
<c:forEach items="${foos}" var="foo">
<form:input path="${foo.name}" type="text"/>
And then submit the complete list of Foos with updated names?
My controller looks like this:
#RequestMapping(value = "/FOO", method = RequestMethod.POST)
public String getSendEmail(List<Foo> foos, Model model) {
// ...
}
Maybe this answersyour question:
CONTROLLER :
#Controller("/")
public class FooController{
//returns the ModelAttribute fooListWrapper with the view fooForm
#RequestMapping(value = "/FOO", method = RequestMethod.GET)
public String getFooForm(Model model) {
FooListWrapper fooListWrapper = new FooListWrapper();
fooListWrapper.add(new Foo());
fooListWrapper.add(new Foo());
//add as many FOO you need
model.addAttribute("fooListWrapper", fooListWrapper);
return "fooForm";
}
#RequestMapping(value = "/FOO", method = RequestMethod.POST)
public String postFooList(#ModelAttribute("fooListWrapper")FooListWrapper fooListWrapper, Model model) {
//...........
}
}
FOO LIST WRAPPER :
public class FooListWrapper {
private List<Foo> fooList;
public FooListWrapper() {
this.fooList = new ArrayList<Foo>();
}
public List<Foo> getFooList() {
return fooList;
}
public void setFooList(List<Foo> fooList) {
this.fooList = fooList;
}
public void add(Foo foo) {
this.fooList.add(foo);
}
}
FOO CLASS :
public class Foo {
private String name;
public Foo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
JSP VIEW (name = fooForm):
<c:url var="fooUrl" value="/FOO"/>
<form:form id="frmFoo" action="${fooUrl}" method="POST" modelAttribute="fooListWrapper">
<c:forEach items="${fooListWrapper.fooList}" varStatus="i">
<form:input path="fooList[${i.index}].name" type="text"/>
</c:forEach>
<button>submit</button>
</form:form>
Although the above answer works, here's an alternate that does not require you to create a wrapper class/ form class.
Model And Controller
public class Foo {
private String name;
private List<Foo> fooList; //**must create this list, also getter and setter**
public Foo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getFooList() {
return fooList;
}
public void setFooList(String fooList) {
this.fooList = fooList;
}
}
#Controller("/")
public class FooController{
//returns the ModelAttribute fooListWrapper with the view fooForm
#RequestMapping(value = "/FOO", method = RequestMethod.GET)
public String getFooList(Model model) {
List<Foo> fooList = service.getFooList();
model.addAttribute("fooList", fooList);
return "list_foo"; //name of the view
}
#RequestMapping(value = "/FOO", method = RequestMethod.POST)
public String postFooList(#ModelAttribute("foo")Foo foo, Model model) {
List<Foo> list = foo.getFooList(); // **This is your desired object.
//If you debug this code, you can easily find this is the list of
//all the foo objects that you wanted, provided you pass them properly.
//Check the jsp file to see one of the ways of passing such a list of objects**
//Rest of the code
}
}
JSP View
<form:form id="form" action="<paste-target-url-here>" method="POST" modelAttribute="fooList">
<c:forEach items="${fooList}" varStatus="i">
<form:input path="fooList[${i.index}].name" type="text"/>
<!-- Here you are setting the data in the appropriate index which will be caught in the controller -->
</c:forEach>
<button>submit</button>
</form:form>

Resources