Send values get from append forms to spring controller - spring

I was creating a model where the structure as follows:
public class A {
private Long id;
private Type type;
private Set<String> names;
private Set<Long> ids;
private Set<String> subnames;
private Set<Long> subids;
...........
}
I would like to create the model, with multiple fields as many as I like.
so the I have created the form as follows to add new rows dynamically.
Creation Form: One of the fields-->
<form>
<div id="addNewname" class="form-group">
<label>name</label>
<div class="col-lg-4">
<input type="text" name="name_1" id="name" readonly>
</div>
<button id="btnAddname" type="button"
type="hidden"
value="btnAddName" name="btnAddName">Add
New</button>
</div></form>
With the Script to add new as follows:
int count = 1;
$(document).on("click", "#btnAddNew", function(){
count++;
$('#addNewNew').after(
'<div>' +
'<label> New Name</label>'+
'<div >' +
'<select name="name_'+ count +'">'+
'<option value="0">None</option>' +
'<c:forEach items="${names}" var="name">' +
'<option value="${name.id}">${name.name}</option> '+
'</c:forEach>'+
'</select>'+
'</div>'+
'</div>');
});
I was able to send the value to the controller of the value name="name_1" where the form been defined, but I could not do the same for the values created from the append form--script.
Any idea or suggestion to work out this, I have tried many methods but ...

You may use LazyList to add as many names you want.
In your command class, instead of using Set to define your names, try this,
import org.apache.commons.collections.FactoryUtils;
import org.apache.commons.collections.list.LazyList;
...
private List names = LazyList.decorate( new ArrayList(),
FactoryUtils.instantiateFactory(String.class));
...
In the Jsp page, all that are needed is using the arrayIndex to access or bind values. i.e.
<form:input path="names[0]" cssErrorClass="errorInput" maxlength="30"/>
In the javascript, do the same, use the arrayIndex when your create more text input on the fly.

Related

How to create dynamic url in Spring Thymeleaf

#GetMapping("/deposit")
public String deposit(#RequestParam("amount") double amount,#RequestParam ("id") int id) {
if (amount > 0) {
accountService.deposit(id, amount);
}
return "redirect:/account";
}
I have two parameters that I need to send from my html file, my problem is that 'amount' parameter should be coming from html file. How can I dynamically do that?
<input type="number" id="amount">
<a th:href="#{/account/deposit(id=${account.id}, amount=????)}">Deposit</a>
I want to put the input value into amount in th:href would appreciate any help.
As it is mentioned by #andrewJames it would be mush easier to submit this value using form. For example:
In your HTML
<form th:action="#{/account/deposit(id=${account.id})}" method="post">
<input type="number" id="amount" name="amount">
<button type="submit">Deposit</button>
</form>
In your Controller
#PostMapping( "/deposit" )
public String onDepositSubmit( #RequestParam Long id, #RequestParam Integer amount ) {
if (amount > 0) {
accountService.deposit(id, amount);
}
return "redirect:/account";
}
This would be the easiest solution. However, it is possible to dinamically alter a link as at the client-side the link is already rendered to a normal link (e.g. /account/deposit?id=12345), so you can manipulate it using JS as you wish, for example something like this (using JQuery):
<input type="number" id="amount">
<a th:href="#{/account/deposit(id=${account.id},amount=0)}" id="amount_link">Deposit</a>
<script>
let amountInput = $( '#amount' )
let amountLink = $( '#amount_link' )
amountInput.keyup( function() {
let url = new URL( amountLink.attr( 'href' ) );
url.searchParams.set( 'amount', amountInput.val() )
amountLink.attr( 'href', url.toString() )
} )
</script>
Which would create a keyup event listener on input and update link every time the character is typed or deleted. However, this is needlessly complicated and as such is considered a bad practice.

Spring Boot error while submitting form

I'm trying to add a table to the database via a form. The entity being created is called Album and it has 2 fields, Artist and Genre. Each of these two are separate entities. These 2 fields are annotated with #ManyToOne
#ManyToOne
private Artist artist;
#ManyToOne
private Genre genre;
When I submit the form, this is the error im getting:
There was an unexpected error (type=Internal Server Error, status=500).
Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringOptionFieldAttrProcessor' (album/add:52)
The following code is part of my controller:
#RequestMapping({"/add", "/add/"})
public String adminAlbumAdd(Model model) {
model.addAttribute("album", new Album());
model.addAttribute("artists", artistService.list());
model.addAttribute("genres", genreService.list());
return "album/add";
}
#RequestMapping( value = "/save", method = RequestMethod.POST )
public String save(#Valid Album album, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()) {
model.addAttribute("artists", artistService.list());
model.addAttribute("genres", genreService.list());
return "album/add";
} else {
Album savedAlbum = albumService.save(album);
return "redirect:/album/view/" + savedAlbum.getAlbumId();
}
}
And the following code is part of the thymeleaf template:
<div th:class="form-group" th:classappend="${#fields.hasErrors('artist')}? 'has-error'">
<label class="col-sm-2 control-label">Artist <span class="required">*</span></label>
<div class="col-md-10">
<select class="form-control" th:field="*{artist}">
<option value="">Select Artist</option>
<option th:each="artist : ${artists}" th:value="${artist.artistId}" th:text="${artist.artistFirstName + ' ' + artist.artistFirstName}">Artists</option>
</select>
<span th:if="${#fields.hasErrors('artist')}" th:errors="*{artist}" th:class="help-block">Artist Errors</span>
</div>
</div>
<div th:class="form-group" th:classappend="${#fields.hasErrors('genre')}? 'has-error'">
<label class="col-sm-2 control-label">Genre <span class="required">*</span></label>
<div class="col-md-10">
<select class="form-control" th:field="*{genre}">
<option value="">Select Genre</option>
<option th:each="genre : ${genres}" th:value="${genre.genreName}" th:text="${genre.genreName}">Genres</option>
</select>
<span th:if="${#fields.hasErrors('genre')}" th:errors="*{genre}" th:class="help-block">Genre Errors</span>
</div>
</div>
What is causing this error ?
The issue turned out to be related to the repository. I was extending CrudRepository, but the id was of type int. Once i changed that, it worked.
Firstly, you might consider using same mapping for GET/POST requests as a standard like:
#GetMapping("/new")
...
#PostMapping("/new")
Also #Valid Album album parameter should be annotated as #ModelAttribute.
You should not add model attributes if binding result has errors. (Actually, you should not add any model attribute for a POST method.)
You should not create that savedAlbum object with albumService.save().
That method should be void.
I will advise against posting directly to your database object. You should rather create a DTO class, say AlbumDto, that will map the classes like so:
public class AlbumDto {
...
private long genreId;
private long artistId;
// Getters & Setters
}
You can then convert it to your Album object, lookup the corresponding Genre and Artist in your controller, set them on the Album object and then save.

ASP.Net Core 2.0 MVC dynamic model updating

I'm trying to build a dynamic view, where I can pass a list of properties that need to be filled in by the user.
The collection of properties is dynamic, so I can't build the view that displays specific properties.
I've been able to display the property names and their initial values, and the user can change the values on the screen, but those updated values don't make it to the controller action that would update the model.
I've tried using a dynamic model, as well as a list of key/value pairs.
I'm thinking that it has something to do with the over-posting protection. Since the properties are dynamic, I can't list them in the Bind attribute in the update action.
Here's the controller action methods:
public IActionResult Test()
{
dynamic testObj = new ExpandoObject();
testObj.IntProperty = 100;
testObj.StringProperty = "A String Value";
return View(testObj);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Test(ExpandoObject model)
{
return Ok();
}
Here's the view:
#model dynamic
#{
ViewData["Title"] = "Test";
}
<form asp-action="Test" method="post">
<div class="form-horizontal">
#foreach (var propertyName in ((System.Collections.Generic.IDictionary<string, object>)Model).Keys)
{
<div class="form-group">
<label class="col-md-2 control-label">#propertyName</label>
<div class="col-md-10">
#Html.TextBox(propertyName, ((System.Collections.Generic.IDictionary<string, object>)Model)[propertyName])
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
This is a new application, and I'm doing code-first - so the model can be changed somewhat. All I really need to do is to be able to have different properties that can be updated.
Thanks in advance.
I recommend that you do not rely on the IModelBinder for this purpose at all and why I recommend this is because the form data that is passed between the controller and view is dynamic in terms of structure. A better, yet more troublesome, solution would be to get the form data directly out of the HttpContext.Request.Form. This type has an indexer which allows you to access the posted values by their names. For example
var name = HttpContext.Request.Form["name"].FirstOrDefault();
The .FirstOrDefault() (or SingleOrDefault(), this throws an exception when finding more than a value that meets a condition in a collection) is called assuming that there would be a single value (or a single value that meets a condition) for the "Name" input. However, when you have an array of those, you can get the values either in a foreach loop, or using linq methods, or directly by an index. For example:
var name = HttpContext.Request.Form["name"].FirstOrDefault(x=> x.ToString().StartsWith("x"));
var name = HttpContext.Request.Form["name"][0];

How do I iterate over all my model attributes on my JSP page?

I'm using Spring 3.2.11.RELEASE with JBoss 7.1.3.Final and Java 6. I have this method in a controller
#RequestMapping(value = "/method", method = RequestMethod.GET)
public String myMethod(final Model model,
final HttpServletRequest request,
final HttpServletResponse response,
final Principal principal)
...
model.addAttribute("paramName", "paramValue");
Notice how I add attributes into my model. My question is, on the JSP page that this page serves, how do I iterate over all the attributes in my model and output them as HIDDEN input fields with the name of the INPUT being the attribute name and the value being what I inserted in the model using that attribute?
Edit: In response to the answer given, here was the output to the JSP solution. Note there are no model attributes in there.
<input type='hidden' name='javax.servlet.jsp.jspRequest' value='org.springframework.web.context.support.ContextExposingHttpServletRequest#7a0a4c3f'>
<input type='hidden' name='javax.servlet.jsp.jspPageContext' value='org.apache.jasper.runtime.PageContextImpl#3939794a'>
<input type='hidden' name='appVersion' value='???application.version???'>
<input type='hidden' name='javax.servlet.jsp.jspResponse' value='org.owasp.csrfguard.http.InterceptRedirectResponse#722033be'>
<input type='hidden' name='javax.servlet.jsp.jspApplication' value='io.undertow.servlet.spec.ServletContextImpl#14c1252c'>
<input type='hidden' name='org.apache.taglibs.standard.jsp.ImplicitObjects' value='javax.servlet.jsp.el.ImplicitObjectELResolver$ImplicitObjects#23c27a49'>
<input type='hidden' name='javax.servlet.jsp.jspOut' value='org.apache.jasper.runtime.JspWriterImpl#b01a1ba'>
<input type='hidden' name='javax.servlet.jsp.jspPage' value='org.apache.jsp.WEB_002dINF.views.lti.launch_jsp#1dcc48bf'>
<input type='hidden' name='javax.servlet.jsp.jspConfig' value='io.undertow.servlet.spec.ServletConfigImpl#3fd40806'>
Model attributes are "request scope" objects
you can do the following (I use JSTL):
<c:forEach items="${requestScope}" var="par">
<c:if test="${par.key.indexOf('attrName_') > -1}">
<li>${par.key} - ${par.value}</li>
</c:if>
</c:forEach>
Since with no filter you will have all the request scope objects, I filtered by the model attributes we wanted to check
I tested by using this code:
#RequestMapping(method = { RequestMethod.GET }, value = { "/*" })
public String renderPage(Model model) throws Exception
{
String requestedUrl = req.getRequestURI();
int indice = requestedUrl.lastIndexOf('/');
String pagina = requestedUrl.substring(indice + 1);
try
{
String usernameUtente = "default username utente";
if (StringUtils.hasText(getPrincipal()))
{
usernameUtente = getPrincipal();
}
model.addAttribute("usernameUtente", usernameUtente);
model.addAttribute("webDebug", webDebug);
for(int i = 0; i<10; i++)
{
model.addAttribute("attrName_"+i, "attrValue_"+i);
}
return pagina;
}
catch (Exception e)
{
String message = "Errore nell'erogazione della pagina " + pagina;
logger.error(message, e);
return "genericError";
}
}
And this is what I see as output (I omitted the not relevant prints but please note you'll print ALL the request scope objects:
attrName_0 - attrValue_0
attrName_1 - attrValue_1
attrName_2 - attrValue_2
attrName_3 - attrValue_3
attrName_4 - attrValue_4
attrName_5 - attrValue_5
attrName_6 - attrValue_6
attrName_7 - attrValue_7
attrName_8 - attrValue_8
attrName_9 - attrValue_9
I hope this can help
Angelo
For avoid headache with parameters added by Spring and Servlet container, it is better to use separate map for pass values into the model. Just use #ModelAttribute and Spring will create and add it to the model automatically:
#RequestMapping(value = "/method", method = RequestMethod.GET)
public String myMethod(final Model model, #ModelAttribute("map") HashMap<String, Object> map) {
map.put("paramName1", "value1");
map.put("paramName2", "value2");
//...and so on
}
Now you can iterate this map in JSP:
<c:forEach items="${map.keySet()}" var="key">
<input type="hidden" name="${key}" value="${map[key]}"/>
</c:forEach>
Also you can access to every item of map next way:
<c:out value="${map.paramName1}"/>
<c:out value="${map.paramName2}"/>
...
If you don't need some parameter to be iterable, add it into the original ModelMap istead of separate map.
In essence all you need is to itterate on all the page attributes. Depending on what you use on your jsp (scriptlets, jstl, or smthing like thymeleaf for html):
Scriptlet:
<form>
<% Session session = request.getSession();
Enumeration attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
String value = session.getAttribute(name);
%>
<input type='hidden' name="<% name %>" value="<% value %>">
<%
}
%>
</form>
JSTL:
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<h3>Page attributes:</h3>
<form>
<c:forEach items="${pageScope}" var="p">
<input type='hidden' name='${p.key}' value='${p.value}'>
</c:forEach>
</form>
Thymeleaf:
<form>
<input th:each="var : ${#vars}" type='hidden' name="${var.key}" value="${var.value}">
</form>
Simply you can iterate using foreach tag of Jstl.
<c:forEach items="${requestScope}" var="var">
<c:if test="${ !var.key.startsWith('javax.') && !var.key.startsWith('org.springframework')}">
<input type="hidden" name="${var.key}" value="${var.value}" />
</c:if>
</c:forEach>
Request attributes from spring framework and from Servlet do have prefixes, you don't need to add prefix to your request attributes.
Rather you can ignore all those attributes which have prefix "org.springframework" or "javax.".
You can try this:
#RequestMapping(value = "/method", method = RequestMethod.GET)
public String myMethod(final Model model,
final HttpServletRequest request,
final HttpServletResponse response,
final Principal principal)
...
//Create list for param names and another list for param values
List<String> paramNames = new ArrayList();
List<String> paramValues = new ArrayList();
paramNames.add("paramName1");
paramValues.add("paramValue1");
paramNames.add("paramName2");
paramValues.add("paramValue2");
//paramValue1 is the value corresponding to paramName1 and so on...
//add as many param names and values as you need
...
//Then add both lists to the model
model.addAttribute("paramNames", paramNames);
model.addAttribute("paramValues", paramValues);
Then in the JSP, you can iterate over paramNames list, and use the varStatus.index to get the index of current round of iteration and use it to pull the value of corresponding param value from the paramValues list. Like this -
<form id='f' name='myform' method='POST' action='/path/to/servlet'>
<c:forEach items="${paramNames}" var="paramName" varStatus="status">
<input type='hidden' name='${paramName}' value='${paramValues[status.index]}'>
</c:forEach>
</form>
You can add other input elements to the form as needed but the above should generate all the hidden input elements for each of your parameter that you set in the Model.

How to create AJAX edit form in scala lift for one to many relationship?

I'm trying to create form to edit one to many relationship. I've created two classes:
class StudentGroup extends LongKeyedMapper[StudentGroup] with IdPK with OneToMany[Long, StudentGroup] {
object groupName extends MappedString(this, 20)
object students extends MappedOneToMany(Student,Student.studentGroup)
}
class Student extends LongKeyedMapper[Student] with IdPK {
object firstName extends MappedString(this,35)
object lastName extends MappedString(this,35)
object studentGroup extends MappedLongForeignKey(this, StudentGroup)
}
Then I created a snippet to display a group with following code:
val addStudentContent: NodeSeq = <div>
<input type="text" placeholder="First Name"></input>
<input type="text" placeholder="Last Name"></input>
</div>
def addStudent = AppendHtml("studentlist", addStudentContent)
def render =
"#addstudent" #> SHtml.ajaxButton("Add student", () => addStudent)
Now after clicking 'Add student' button two new fields appeared and I can put first and last name there. Problem is - how to store those data in database? Any hint?
Thanks
There are several ways you can begin to solve this problem. I never use Mapper myself, so there may be a much better way than what I am suggesting. However, something like the Ajax example in Simply Lift should get you started. Applying it to your code, we would get something like the following.
object StudentSnippets {
val addStudentContent: NodeSeq = <form data-lift="form.ajax">
<div data-lift="StudentSnippets.ajaxSubmit"> // Calls ajaxSubmit below
<input name="first" type="text" placeholder="First Name"></input>
<input name="last" type="text" placeholder="Last Name"></input>
<input type="submit" value="Submit"></input>
</div>
</form>
def addStudent = AppendHtml("studentlist", addStudentContent)
def render =
"#addstudent" #> SHtml.ajaxButton("Add student", () => addStudent)
private object first extends RequestVar("")
private object last extends RequestVar("")
def ajaxSumbit = {
"name=first" #> SHtml.text(first.is, first(_), "id" -> "the_name") &
"name=last" #> (SHtml.text(last.is, last(_)) ++
SHtml.hidden(ajaxProcess)) // Calls ajaxProcess below
}
def ajaxProcess = {
println("First is "+first.is)
println("Last is "+last.is)
Noop // This is the JS that will run after submit.
}
}
You can view another approach here on the wiki.
For more help with Lift, I strongly recommend posting your questions to the Lift Google Group. You will find that you get swarmed with Lift help more so than other forums such as SO.

Resources