As the title describes, using thymeleaf I display the contents of a list and then I put "Update" buttons next to each item on the list that send the particular object to an editing form page.
Here is the controller method for adding the list to the list view:
#RequestMapping("/list")
public String list(Model model){
List<Employee> employees = repository.findAll();
model.addAttribute("employees", employees);
return "list";
}
And here is the thymeleaf html code:
<tr th:each="emp : ${employees}">
<td th:text="${emp.id}"></td>
<td th:text="${emp.name}"></td>
<td th:text="${emp.surname}"></td>
<td th:text="${emp.age}"></td>
<td th:text="${emp.department}"></td>
<td>
<form th:action="#{/update}" method = "POST" th:object="${emp}">
<input type="hidden" th:field="*{id}"></input>
<input type="hidden" th:field="*{name}" ></input>
<input type="hidden" th:field="*{surname}"></input>
<input type="hidden" th:field="*{age}"></input>
<input type="hidden" th:field="*{department}"></input>
<button type = "submit">Update</button>
</form>
</td>
</tr>
And here is the receiving method:
#RequestMapping("/update")
public String update(#ModelAttribute("emp") Employee emp){
return "update";
}
I keep getting the following exception:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'emp' available as request attribute
Please let me know if you have any ideas on accomplishing this task.
This could be help.Change your controller like this.Its work for me.
model.addAttribute("emp", new Employee());
#RequestMapping("/list")
public String list(Model model){
List<Employee> employees = repository.findAll();
model.addAttribute("emp", new Employee());
model.addAttribute("employees", employees);
return "list";
}
Related
What is want to achieve is I have a form to adds rows with data to a html table, it's like a temporary table and all the data from it will be added in just one submit button. How can I possibly do this?
This is my sample table structure, data from it must be added to db
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<table class="table">
<thead>
<tr>
<!--some other fields-->
<th>Amount</th>
<th>Check #</th>
<th>Check Date</th>
</tr>
</thead>
<tbody>
<tr>
<!--some other fields-->
<td>
<input asp-for="Amount" class="form-control" />
<span asp-validation-for="Amount" class="text-danger"></span>
</td>
<td>
<input asp-for="Check_No" class="form-control" />
<span asp-validation-for="Check_No" class="text-danger"></span>
</td>
<td>
<input asp-for="Check_Date" class="form-control" />
<span asp-validation-for="Check_Date" class="text-danger"></span>
</td>
</tr>
<!--row 2-->
<!--row 3-->
<!--row 4-->
<!--etc..-->
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
And here is my controller code so far, i don't know what do I need to change
// POST: Books/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Year,Month,Document_Code,GLA_Code,Expense_Code,Function_Code,Document_Reference_No,Document_Date,Quantity,Amount,Check_No,Check_Date,Remarks,Encoder")] Book book)
{
if (ModelState.IsValid)
{
_context.Add(book);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(book);
}
What must needed to be change in my view and controller, and help will is appreciated.
Thank you.
There's a couple of issues here.
First, if you've got a table of "books" (plural), the input names need to be indexed. You then also need to accept something like List<Book> instead of Book as the param to your action method. It's a little hard to tell with just the code provided, but I'd imagine you're repeating these inputs, all with the same names for each row. If that's the case, only the values for the last item will be posted. Making it a list of items enables you to post them all.
Simplistically, that means your inputs need to have names like [0].Amount, which Razor will generate for you if you use a for loop and render the inputs like:
<input asp-for="#Model[i].Amount" class="form-control" />
If you're adding the additional rows (and contained inputs) via JavaScript, you'll need to ensure that you're generating these indexed names properly yourself. A JS templating library may help in this regard.
Second, do not use Bind. Just don't. It's awful, horrible, and kills both puppies and kittens. For more explanation see my post, Bind is Evil. Use a view model instead. As a general rule you should never post an entity class. Your entity classes serve the database and its concerns, which are almost always different from the concerns of the view. Additionally, you should never just blindly save something posted by a user. Even if you insist on using your entity class to bind to, you can improve the security and safety of your code exponentially by literally mapping the values from the posted version of the class over to a new instance you create. Then, you know exactly what is being persisted to the database (without the godforsaken Bind) and you also have the opportunity to sanitize input as necessary.
I was facing a similar problem, but using ASP.NET Core 3.1 and Razor Pages. I was looking for a way to add and remove rows from a table with JavaScript, and then post it. My problem was to post the table without Ajax. Based in the question and in the accepted answer, I could do that.
Here is Index.cshtml.cs:
public class IndexViewModel {
public string Name { get; set; }
public IList<ResourceViewModel> Resources { get; set; }
}
public class ResourceViewModel {
public string Key { get; set; }
public string Value { get; set; }
}
public class IndexModel: PageModel {
[BindProperty]
public IndexViewModel ViewModel {get; set; }
public void OnGet() {
// You can fill your ViewModel property here.
}
public void OnPost() {
// You can read your posted ViewModel property here.
}
}
Here is Index.cshtml:
#page
#model IndexModel
#{
ViewData["Title"] = "Index";
}
<form method="post">
<div class="form-group">
<label asp-for="ViewModel.Name"></label>
<input asp-for="ViewModel.Name" class="form-control" />
</div>
<div class="form-group">
<table class="table">
<thead>
<th>Key</th>
<th>Value</th>
</thead>
<tbody>
#for(int i = 0; i < Model.ViewModel.Resources.Count; i++) {
<tr>
<td>
<input asp-for="ViewModel.Resources[i].Key" type="hidden" />
#Model.ViewModel.Resources[i].Key
</td>
<td>
<input asp-for="ViewModel.Resources[i].Value" type="hidden" />
#Model.ViewModel.Resources[i].Value
</td>
</tr>
}
</tbody>
</table>
</div>
<button type="submit" class="btn btn-primary">Send</button>
</form>
Notice I've used type="hidden" because I didn't want the user to edit the table directly.
I hope you find this useful!
I have a form ,when I am using spring form tags with the path attribute,I get the following error
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'selectedPerson' available as request attribute
However when I use the regular HTML input with the name attribute I don't see any issues.
What am I doing wrong.
Here is the controller code
#RequestMapping(value="/savePersonChanges.htm",method=RequestMethod.POST)
public ModelAndView savePersonChanges(HttpSession session,#Valid #ModelAttribute(value="selectedPerson")PersonBean editedPersonBean,BindingResult bindingResult){
ModelAndView mav=new ModelAndView();
mav.setViewName(LANDING_PAGE;
mav.addObject(TAB_SELECTOR,"EditPerson");
if(session!=null){
Rpt_date=(java.util.Date)session.getAttribute("editDate");
}
if(bindingResult.hasErrors()){
mav.addObject("errorDescription",errorDescription );
}
else{
/* Call service that updates database with table changes */
try{
PersonService.updatePerson(Rpt_date, editedPersonBean
}
catch(Exception e){
log.logError("Exception while updating Person", e.toString());
}
}
return mav;
}
The form is as follows:
<form:form modelAttribute="selectedPerson" id="editPersonForm">
<table id="selectPerson">
<tbody>
<tr>
<td>
<select id="personId" name="personId" onchange="getPersonDetails(this)">
<c:choose>
<c:when test="${empty selectedPerson}">
<option value="0" selected ="selected">Select A Person</option>
<c:forEach var="personIdItem" items="${editPersonProperties.personproperties}">
<option value="${personIdItem.personId}"><spring:escapeBody>${personIdItem.personName}</spring:escapeBody></option>
</c:forEach>
</c:when>
<c:otherwise>
<c:forEach var="personIdItem" items="${editPersonProperties.personproperties}">
<c:if test="${personIdItem.personId eq selectedPerson.personId}">
<option value="${personIdItem.personId}" selected ="selected">${personIdItem.personName}</option>
</c:if>
<c:if test="${personIdItem.personId ne selectedPerson}">
<option value="${personIdItem.personId}"><spring:escapeBody>${personIdItem.personName}</spring:escapeBody></option>
</c:if>
</c:forEach>
</c:otherwise>
</c:choose>
</select>
</td>
</tr>
</tbody>
</table>
<!-- Person Details -->
<table id="editPersonTable">
<tr>
<td>First Name</td>
<td> <form:input type ="text" path="fname" value="${selectedPerson.fname}"></form:input></td>
<td>Last Name</td>
<td> <form:input type ="text" path="lname" value="${selectedPerson.lname}"/></td>
</tr>
</table>
<div class="editcancelstyle">
<input id="savebtn" type="button" onclick="savePerson()" value="Save" />
<input id="cancelbtn" type="button" onclick="cancelPersonEdits ()"value="Cancel" />
</div>
</form:form>
I understand that the path attribute will bind the field to the form. However, I keep getting the bind error. If I replace using plain HTML, the controller sees the edited values for the fname and lname for the SelectedPerson bean.
Here is your problem, when you do :
<form:... modelAttribute="selectedPerson" ...>, the selectedPerson is the key of model object mapped from the holder of both Model & View class ( ex : ModelAndView), suppose you bind a model in your controller with new ModelAndView ("yourForm", "selectedPerson", new Person()),
#RequestMapping(value = "/form")
public ModelAndView userInput() {
ModelAndView mv = new ModelAndView("personForm", "selectedPerson", new Person());
return mv;
}
now the Person is mapped with selectedPerson so when this form returned as the response from controller, your form has already been bound to the Person model, so that you use path to refer to this Person's attributes (ex. path="name" , means it refers to Person's name attribute)
In your form, on modelAttribute="selectedPerson", the selectedPerson is not binded to any models, since "selectedPerson" is never assigned to any object because you didn't do any binding first before processing ( submitting ) the form.
this is why you got
> java.lang.IllegalStateException: Neither BindingResult nor plain
> target object for bean name 'selectedPerson' available as request
> attribute.
Note that to get this binding works, add also the following on the top of your form
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
you can then populate the fields of the form (assuming you put action="result.html"):
#RequestMapping(value = "/result")
public ModelAndView processUser( #ModelAttribute("selectedPerson") Person person, BindingResult result) {
if (result.hasErrors()) {
return new ModelAndView("errorPage","message","Something goes wrong");
}
/*System.out.println(person.getName());*/
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("personResult");
modelAndView.addObject("person", person);
return modelAndView;
}
Getting java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'loginForm' available as request attribute. My login.jsp is as below.
<form:form method="POST" action="/loginPage" commandName = "loginForm">
<tr>
<td><form:label path="name">Username</form:label></td>
<td><form:input path="name" /></td>
</tr>
<tr>
<td><form:label path="age">Password</form:label></td>
<td><form:input path="age" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit"/>
</td>
</tr>
and my controller is
#RequestMapping(value="/loginPage",method = RequestMethod.POST)
public String processForm(#ModelAttribute("loginForm") LoginForm loginForm, BindingResult result,
Map model)
can anyone suggest how can i resolve this exception.
You need to add "loginForm" key to ModelMap before you start using it..
Example:
//This code needs to be invoked first
#RequestMapping(value="/onPageLoadOfLogin",method = RequestMethod.GET)
public String onLoadOfLoginPage(ModelMap m){
m.put("loginForm",new LoginForm());
}
model.put("loginForm",new LoginForm());
This means that, your command name should be exactly the same as the modelAttribute name... so if your commandName in your jsp file is "loginForm" then you model should add the attribute with the name "loginForm".
Please add model map attribute before rendering to login page.
<form:form method="POST" action="/loginPage" modelAttribute = "loginForm">
try adding modelAttribute in
also u need to first bind the form with the controller. To do this you can first perform a GET to perform the binding.
#RequestMapping(value="/loginPage",method = RequestMethod.GET)
public String processForm(#ModelAttribute("loginForm") LoginForm loginForm, BindingResult result,
Map model)
after this do POST. And submit button will do the binding of its own in POST method calling. Your form will be like
<form:form modelAttribute = "loginForm">
In Spring MVC, I have a search form. If user submit search form, result should show in same page.
It is redirecting to same page But I'm not getting attributes in JSP from controller.
qSearch.jsp
<form:form name="quickSearchForm" id="searchFormId" method="POST" action="./searchQuick.html" modelAttribute="searchForm" onsubmit="return validateForm()">
<table>
<tr>
<th>Change/Defect ID</th><td><form:input type="text" name="identifier" path="identifier"/></td>
</tr>
</table>
<div class="icons">
<span><button style="left:0px" type="reset" name="clear" style="border-radius: 25px;">CLEAR</button></span>
<span><button style="right:0px" type="submit" name="submit" style="border-radius: 25px;" >SEARCH</button></span>
</div>
</form:form>
<hr>
<div>
<c:if test="${empty SEARCH_RESULTS_KEY}">
<table style="border-collapse: collapse;" border="1" class="showResults">
<tr>
<td colspan="7">No Results found</td>
</tr>
</table>
</c:if>
<c:if test="${! empty SEARCH_RESULTS_KEY}">
<table style="border-collapse: collapse;" border="1" class="showResults">
<tr>
<td colspan="7">Result found</td>
</tr>
</table>
</c:if>
Controller
#RequestMapping(value="/qSearch", method = RequestMethod.GET)
public String getQuickSearchmodel(Model model) {
System.out.println("Welcome to search tool\n");
ArchivalIssue archivalIssue=new ArchivalIssue();
model.addAttribute("searchForm", archivalIssue);
return "quickSearchPage";
}
#RequestMapping(value = "/searchQuick", method = RequestMethod.POST)
public ModelAndView getAllArchivalIssues(HttpServletRequest request){
String identifier = request.getParameter("identifier");
List<ArchivalIssue> archivalIssue = archivalIssueService.getAllArchivalIssue(identifier);
ModelAndView mav = new ModelAndView("redirect:/qSearch"); //Add model to display results
mav.addObject("SEARCH_RESULTS_KEY", archivalIssue); //Add result object to model
return mav;
}
please someone help me, how to get result in JSP. I'm always getting no result found.
I think you should not redirect in this case. Because redirect will create new GET request to server. The quickSearchPage and getAllArchivalIssues should return same view
#RequestMapping(value = "/searchQuick", method = RequestMethod.POST)
public ModelAndView getAllArchivalIssues(HttpServletRequest request){
String identifier = request.getParameter("identifier");
List<ArchivalIssue> archivalIssue = archivalIssueService.getAllArchivalIssue(identifier);
//return quickSearchPage, so that the client can render the list archivalIssue
ModelAndView mav = new ModelAndView("quickSearchPage");
mav.addObject("SEARCH_RESULTS_KEY", archivalIssue); //Add result object to model
return mav;
}
When the return value contains redirect: prefix, The rest of the view name will be treated as the redirect URL. And the browser will send a new request to this redirect URL. So the handler method mapped to this URL will be executed.
just return the same page as response with model attributes.
Are you using JSTL is there any changes required
${empty param.SEARCH_RESULTS_KEY}
I'm trying to access the values a user introduces in a table from my controller.
This table is NOT part of the model, and the view source code is something like:
<table id="tableSeriales" summary="Seriales" class="servicesT" cellspacing="0" style="width: 100%">
<tr>
<td class="servHd">Seriales</td>
</tr>
<tr id="t0">
<td class="servBodL">
<input id="0" type="text" value="1234" onkeypress = "return handleKeyPress(event, this.id);"/>
<input id="1" type="text" value="578" onkeypress = "return handleKeyPress(event, this.id);"/>
.
.
.
</td>
</tr>
</table>
How can I get those values (1234, 578) from the controller?
Receiving a formcollection doesn't work since it does not get the table...
Thank you.
Using the FormCollection should work unless your table is not inside of a <form> tag
On top of Lazarus's comment, you can try this, but you have to set the name attribute for each:
<input id="seriales[0]" name="seriales[0]" type="text" value="1234" onkeypress="return handleKeyPress(event, this.id);"/>
<input id="seriales[1]" name="seriales[1]" type="text" value="578" onkeypress="return handleKeyPress(event, this.id);"/>
Now in your Action method you can make your method look like this:
[HttpPost]
public ActionResult MyMethod(IList<int> seriales)
{
// seriales.Count() == 2
// seriales[0] == 1234
// seriales[1] == 578
return View();
}
and seriales will be wired up to those values.
First Option:
Using FormCollection is the simplest way to access dynamic data. It is strange that you cannot get those values from it, can you check the following?
Is the table inside the
element?
Can you add name attribute
to the input elements? Note that
form items are bound by their names,
not id.
Second Option:
The second option is to add a collection in your model, and name everything accordingly. i.e.
public class MyModel
{
...
public IList<string> MyTableItems { get; set; }
}
and in your view use following names:
<input name="MyTableItems[]" value="" />