I don't know should i make class with only one field which l will use as #ModelAttribute to get data from jsp? for example:
public class Age {
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}}
And then using spring mvc form tag to fill this ModuleAttribute
<form:form action="processForm" modelAttribute="age">
<form:input path="number"/>
</form:form>
First of all, you need to create a controller :
#Controller
public class AgeController {
// this is to create an instance of Age and set in the model.
#RequestMapping("showForm")
public String showForm(Model model) {
model.addAttribute("age", new Age());
return "form"; // return form page.
}
// this is to print the age in the display page.
#RequestMapping("processForm")
public String processForm(#ModelAttribute("age") Age age) {
return "display"; // return display page where you will display the data populated in the form page.
}
}
Now, form page should look like :
<%#taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html>
<!-- When you enter the data here, Spring will set the data in the Age bean by calling respective setter of the data members -->
<body>
<form:form action="processForm" modelAttribute="age">
Enter an age : <form:input path="number" />
<input type="submit" value="Submit" />
</form:form>
</body>
</html>
Now, display page should look like :
<%#taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<body>
The age is : ${age.number} <!-- Here getter is called internally -->
</body>
</html>
On clicking the submit, the display page will show the data.
Related
I am simply creating a CRUD Application in Spring MVC. I want to edit Student Details. I have created one form which is used to add student. How can I use the same form to populate the student details for editing it ?
Controller
#RequestMapping(value="/add", method = RequestMethod.GET)
public String addStudent(#RequestParam("studentName") String name,#RequestParam("studentId") String studId){
System.out.println("Student Id : "+ studId);
System.out.println("Student "+name+" added");
list.add(name);
return "redirect:get";
}
#RequestMapping(value="/edit/${index}", method = RequestMethod.GET)
public String editStudent(#PathVariable("index") int index, Model model){
System.out.println("Edit Student with Index " + index);
model.addAttribute("studentId",index);
model.addAttribute("studentName",list.get(index));
return "student";
}
Form
<c:url value="/students/add" var="addStudentAction"></c:url>
<form action="${addStudentAction}" method="get">
<input type="hidden" name="studentId">
<input type="text" name="studentName"></input>
<input type="submit" name="submit" value="Add Student" />
</form>
I want to set studentId and studentName in form fields which are set in model in editStudent method.
What you're asking is a very basic question that ideally should be learned from the tutorials and documentation.
Here is a short list of the steps:
use Spring tags for rendering form (<form:form>, <form:input>, etc)
create an object that will represent form values and export it from the controller to the view
take this object as an argument in the controller's method that is handling form submission
I think you need two pages and one controller.
1. for list all students : index.jsp
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>Student List</h3>
<div>
<p>
<ul>
<c:forEach items="${requestScope.students}" var="student">
<li>
<c:out value="${student.id}"></c:out> |
<c:out value="${student.name}"></c:out> |
edit
</li>
</c:forEach>
</ul>
</p>
<p>Create Student</p>
</div>
</body>
</html>
for show or edit or create student: edit.jsp
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<c:if test="${student.id == null}">
<h3>Student Create</h3>
</c:if>
<c:if test="${student.id != null}">
<h3>Student Edit</h3>
</c:if>
<div>
<form action="${pageContext.request.contextPath}/student/" method="post">
<input type="hidden" name="id" value="<c:out value='${student.id}'/>"/>
<p>Student Name: <input type="text" name="name" value="<c:out value='${student.name}'/>"></p>
<p><input type="submit" value="submit"/></p>
</form>
</div>
</body>
</html>
student controller
package cn.kolbe.student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
#Controller
#RequestMapping("/student")
public class StudentController {
#GetMapping("")
public ModelAndView index() {
List<Student> students = new ArrayList<Student>();
studentCache.keySet().forEach(id -> {
students.add(studentCache.get(id));
});
return new ModelAndView("student/index", "students", students);
}
#GetMapping("/{id}")
public ModelAndView show(#PathVariable("id")String id) {
if (id.equals("new")) {
return new ModelAndView("student/edit");
} else {
Student student = studentCache.get(Long.valueOf(id));
return new ModelAndView("student/edit", "student", student);
}
}
#PostMapping("")
public String createOrEdit(String name, Long id) {
Student student;
if (id == null) {
id = cacheId++;
student = new Student(id, name);
studentCache.put(id, student);
} else {
student = studentCache.get(id);
student.setName(name);
}
return "redirect:/student";
}
private static ConcurrentHashMap<Long, Student> studentCache = new ConcurrentHashMap<>();
private static Long cacheId = 1L;
}
Don't use html <form> tag.
Use Spring tags for rendering form that is <form:form> ,<form:input>
I am using SpringBoot with Thymeleaf to build a trivial example to help me learn the two technologies.
I am basing my example on THIS GUIDE
The entity is a Greeting which has an Id and a Content.
I create the Greeting just fine, and I can list all the Greetings I've created.
I then wanted to add a delete option against each Greeting in the list page. When clicking delete, I want the object to be deleted and the list page served up again.
Alas, when I load the list page I get this error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'greeting' available as request attribute
Controller and respository objects
#Controller
public class GreetingController {
#Autowired
GreetingRepo gr;
#RequestMapping(value="/greeting/list", method=RequestMethod.GET)
public String greetingsForm(Model model) {
model.addAttribute("greetings", gr.findAll());
return "greeting/list";
}
#RequestMapping(value="/greeting/delete", method=RequestMethod.POST)
public String deleteGreeting(#ModelAttribute("greeting") Greeting greeting) {
gr.delete(greeting);
return "redirect:/greeting/list";
}
}
#RepositoryRestResource
interface GreetingRepo extends JpaRepository<Greeting, Long> {
}
List.html page:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Result</h1>
<div th:each="greeting : ${greetings}">
<p th:text="'id: ' + ${greeting.id}" />
<p th:text="'content: ' + ${greeting.content}" />
<form action="#" th:action="#{/greeting/delete}" th:object="${greeting}" method="post">
<input type="hidden" th:field="*{id}"/>
<input type="hidden" th:field="*{content}"/>
<input type="Submit" value="Delete"/>
</form>
</div>
Add another
Show All
</body>
</html>
Would appreciate a push in the right direction here :-)
I think you miss this line .
model.addAttribute("greeting", new Greeting());
#RequestMapping(value="/greeting/list", method=RequestMethod.GET)
public String greetingsForm(Model model) {
model.addAttribute("greeting", new Greeting());
model.addAttribute("greetings", gr.findAll());
return "greeting/list";
}
you dont have to use a form to delete the greeting you can do it very easily with this approach. hide the id of the greeting within the url. so you dont need to use a form and hidden tags. and annotate the controller method with following approach, to accept incoming id of the greeting.
replace the current form with given html code and replace the delete method in the controller as well.
<a th:href="#{/greeting/{id}/delete(id=${greeting.id})}" th:text="delete"></a>
#RequestMapping(value="/greeting/{id}/delete",method=RequestMethod.GET)
public String deleteGreeting(#PathVariable int id) {
gr.delete(id);
return "redirect:/greeting/list";
}
edit:- since you need the object to be present within the controller
you can use findOne method to fetch the object from the given id.check out the following example.
<a th:href="#{/greeting/{id}/edit(id=${greeting.id})}" th:text="edit"></a>
#RequestMapping(value="/greeting/{id}/edit",method=RequestMethod.GET)
public String Edit(#PathVariable int id){
greeting greetingob = gr.findOne(id);
return "edit";
}
I have text area that is in bullet format and i want to display that data in html in bullet format via session.
so how to send that data ?? every user data contain and length is different for example ( 1 user use 3 bullets points ans 2 user use 5 bullet points ) how to send that dynamic data on html page and Display. DATABASE IS NOT USED ..
Identify and parse bullet points from each text area(lets say post)
class BulletPoint{
String content;
public BulletPoint(String content){
super();
this.content=content;
}
//put public getters and setters here
}
class Post{
List<BulletPoint> bulletPoints=new ArrayList<>();
//put public getters and setters here
}
#WebServlet("/posts")
class DisplayPostServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
//parse and prepare text area contents to list
//for each text area
//new Post p
//create bulletin list for post
//set bulletins to post
//end for
//simple example
List<Post> posts=new ArrayList<>();
for(int i=1;i<5;i++){
Post newPost=new Post();
for(int j=1;j<=i;j++){
Bulletin bulletin=new Bulletin("bulletin content"+j);
newPost.getBulletins().add(bulletin);
}
posts.add(newPost);
}
//add posts in request scope, and forward to jsp page
request.setAttribute("posts",posts);
request.getRequestDispatcher("/displayPosts.jsp").forward(request, response);
}
}
displayPosts.jsp
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>
<c:forEach var="singlePost" items="${posts}" varStatus="loopIndex">
<ul> Post ${loopIndex}
<c:forEach var="singleBulletin" items="${singlePost.bulletins}">
<li><c:out value="${singleBulletin.content}"/>
</li>
</c:forEach>
</ul>
</c:forEach>
</div>
</body>
</html>
In one of my JSPs I get the user to enter some details about themselves. When they click submit I want the page that it redirects to, to remember the variable and then print it out.
e.g.
(register.jsp) Username: Barney
(welcome.jsp) Welcome Barney
(register.jsp) Username: Vernon
(welcome.jsp) Hello Vernon
Current code:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(ModelMap map, HttpSession session,
#RequestParam(value="givenUser") String givenUser) {
session.setAttribute("ans", givenUser);
map.addAttribute("displayAnswer", givenUser);
map.put("givenUser", givenUser);
return "register";
}
}
Request mapping for welcome:
#RequestMapping(value = "/welcome", method = RequestMethod.GET)
public String welcome() {
return "welcome";
}
and then in welcome.jsp I have: Hello ${givenAnswer}
register.jsp which once the link inside is clicked, goes to welcome.jsp
this is register.jsp
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%# page isELIgnored="false" %>
<html>
<head>
<title>SPRING ####### WEB</title>
</head>
<body>
<h2>SUCCESSFUL REDIRECTION!!!</h2>
<p>Register</p>
<form action="/HelloSpring/welcome" method="post">
Username: <input type="text" name="givenUser" >
Firstname: <input type="text" name="givenPassword">
Surname: <input type="text" name="givenSurname" >
Password: <input type="password" name="givenPassword">
<input type="submit" value="Submit">
</form>
</body>
</html>
welcome.jsp
<%#taglib uri="http://www.springframework.org/tags/form" prefix="spring"%>
<%# page isELIgnored="false" %>
<html>
<head>
<title>SPRING ####### WEB</title>
</head>
<body>
<h2>Spring Page Redirection</h2>
<p>Welcome ${givenUser}, your password is ${givenPassword}</p>
</body>
</html>
so my question is what have i not included because it currently just fires a blank when the user submits details
You can post the userinput attribute to the Controller and then send the value back in ModelMap.
Example:
#RequestMapping("/hello")
public String hello1(ModelMap map,#RequestParam String userInput){
/*your code goes here*/
map.put("userInput",userInput);
return "jsp1"; //first jsp name
}
and use it in jsp using expression language.
Hello {userInput}!!
The name in a <input> tag is what is used as request parameter name on form submission.
You should at least change your controller code to use correct parameter name :
#RequestMapping(value = "/answer", method = RequestMethod.GET)
public String answer(ModelMap map, HttpSession session,
#RequestParam("howareyou") String givenAnswer) {
session.setAttribute("ans", givenAnswer);
map.addAttribute("displayAnswer", givenAnswer);
map.put("givenAnswer", givenAnswer);
return "answer";
}
}
I also removed unused parameter req since Spring gives you directly the HttpSession. And it is normally no use to add the same object twice in the model. But I assume you were not sure of what was correct and tried both - any of them is correct and is enough :-)
SUCCESS! apparently it needed ${ param.elc }. why is this not on any tutorials and how come no one knew about it? im a bit skeptical still but it's doing as required.
thanks to all who helped!
I have a Person model attribute that contains a list of emails.
I've created some JavaScript code that deletes elements from an HTML list of emails. This is pure JavaScript client side code, no AJAX call.
After submitting, I don't understand why I get all the emails in the corresponding #Controller method, even the ones that were deleted in the HTML.
Can anyone please explain?
JSP
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<link rel="stylesheet" href="<c:url value="/styles/resume.css"/>" type="text/css"></link>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"></link>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script src="/resume/js/jquery.editable-1.0.1.js"></script>
<title>Resumes manager</title>
<script>
$(document).ready(function() {
$('.trash').click(function() {
$(this.parentNode).remove();
});
});
</script>
</head>
<body>
<h1>Personal data</h1>
<form:form modelAttribute="person" action="/resume/person/edit/save" id="personForm" method="post" >
<table>
<tr>
<td>Email addresses:</td>
<td colspan="4">
<ol id="emails">
<c:forEach items="${person.emails}" varStatus="status">
<li><form:hidden path="emails[${status.index}].order" class="emailsDisplayOrder"></form:hidden><form:input path="emails[${status.index}].label"></form:input><form:input type="email" path="emails[${status.index}].value"></form:input><input type="image" src="/resume/images/trash.png" class="trash" value="${status.index}"></input></li>
</c:forEach>
</ol>
</td>
</tr>
</table>
</form:form>
</body>
</html>
Controller
#Controller
#SessionAttributes(types={Person.class}, value={"person"})
public class PersonController {
private final static String PERSON_VIEW_NAME = "person-form";
private ResumeManager resumeManager;
#Autowired()
public PersonController(ResumeManager resume) {
this.resumeManager = resume;
}
#InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
#RequestMapping(value="/person/edit/save")
public String save(#ModelAttribute(value="person") Person p, BindingResult result, SessionStatus status) {
new PersonValidator().validate(p, result);
Collections.sort(p.getEmails()); //this collection still contains client-side dropped objects
this.resumeManager.savePerson(p);
return PERSON_VIEW_NAME;
}
}
Explanation
When you load a page with <form:form modelAttribute="person" ...>, there are two cases :
case 1 : if person doesn't exist, it creates an empty Person
case 2 : if person already exists, it uses it
In all cases, when a page is loaded, there is an existing person.
When you submit a form, Spring MVC updates this existing person only with the submitted information.
So in case 1, if you submit email 1, 2, 3 and 4, Spring MVC will add 4 emails to the empty person. No problem for you in this case.
But in case 2 (for example when you edit an existing person in session), if you submit email 1 and 2, but person has already 4 emails, then Spring MVC will just replace email 1 and 2. Email 3 and 4 still exist.
A possible solution
Probably not the best one, but it should work.
Add a remove boolean to the Email class :
...
public class Email {
...
private boolean remove; // Set this flag to true to indicate that
// you want to remove the person.
...
}
In the save method of your controller, remove the emails that have remove set to true.
Finally, in your JSP, add this hidden field :
<form:hidden path="emails[${status.index}].remove" />
And tell your Javascript to set the input value to true when the user clicks to delete the email.
Alternate solution to Jerome Dalbert one
Jerome's solution should work (thanks again for clear answer and solution), but I didn't want to modify business model.
So here is the way I found out: mark HTML elements to remove using java-script and actually remove it using ajax calls at form submit (I initially avoided ajax to keep model unchanged until user submits, but this solutions preserves that requirement).
JSP:
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<link rel="stylesheet" href="<c:url value="/styles/resume.css"/>" type="text/css"></link>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"></link>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script src="/resume/js/jquery.editable-1.0.1.js"></script>
<title>Resumes manager</title>
<script>
$('.sortable').sortable({
update: function(event,ui) {
var liElements = this.getElementsByTagName('li');
$(liElements).each(function(i, liElement) {
var orderElements = liElement.getElementsByClassName('order');
$(orderElements).each(function (j, orderElement) {
orderElement.value = i;
});
});
}
});
$('.trashable').click(function() {
$(this.parentNode.childNodes).each(function(index, element) {
if(element.src.match(/trash.png/) != null) {
element.src = '/resume/images/back.png';
this.parentNode.className = 'trashed';
} else if(element.src.match(/back.png/) != null) {
element.src = '/resume/images/trash.png';
this.parentNode.className = '';
} else {
element.disabled = !element.disabled;
}
});
});
function trash(element) {
var sfx = element.alt;
var lnk = ('/resume/person/edit/').concat(sfx);
$.ajax({
url: lnk
});
}
$('#personForm').submit(function() {
var trashed = $(this).find('.trashed');
$(trashed).each(function(index, element) {
var img = $(element).find('.trashable');
var tmp = $(img)[0];
trash(tmp);
});
});
});
</script>
</head>
<body>
<h1>Personal data</h1>
<form:form modelAttribute="person" action="/resume/person/edit/save" id="personForm" method="post" >
<table>
<tr>
<td>Email addresses:</td>
<td colspan="4">
<ol class="sortable">
<c:forEach items="${person.emails}" varStatus="status">
<li><form:hidden path="emails[${status.index}].order" class="order"></form:hidden><form:input path="emails[${status.index}].label"></form:input><form:input type="email" path="emails[${status.index}].value"></form:input><img src="/resume/images/trash.png" class="trashable" alt="dropEmail/${person.emails[status.index].id}"></img></li>
</c:forEach>
</ol>
</td>
</tr>
<tr><td colspan="5"><form:button name="save" value="${person.id}">${person.id == 0 ? 'save' : 'update'}</form:button></td></tr>
</table>
</form:form>
</body>
</html>
Controller:
#Controller
#SessionAttributes(types={Person.class}, value={"person"})
public class PersonController {
private final static String PERSON_VIEW_NAME = "person-form";
private ResumeManager resumeManager;
#Autowired()
public PersonController(ResumeManager resume) {
this.resumeManager = resume;
}
#InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
#RequestMapping(value="/person/edit/{id}")
public String edit(#PathVariable("id") long personId, Model model) {
Person p = this.resumeManager.getPersonById(personId);
if(p != null) {
model.addAttribute("person", p);
return PERSON_VIEW_NAME;
} else {
return "redirect:/";
}
}
#RequestMapping(value="/person/edit/save")
public String save(#ModelAttribute(value="person") Person p, BindingResult result, SessionStatus status) {
new PersonValidator().validate(p, result);
Collections.sort(p.getEmails());
this.resumeManager.savePerson(p);
return PERSON_VIEW_NAME;
}
#RequestMapping(value="/person/edit/dropEmail/{id}")
#ResponseBody
public void dropEmail(#ModelAttribute(value="person") Person p, #PathVariable("id") long id) {
int i = 0;
for(Email e : p.getEmails()) {
if(e.getId() == id) {
p.getEmails().remove(i);
break;
}
i++;
}
}
}