Get checkbox values as array from HTML form in spring - spring

I have a form that displays all the items in my list. There is a checkbox next to each element:
<input type="checkbox" name="selected" value="${product.id}">
After I click the Continue button, I get into a controller where I want to have a list of all the checkboxes selected. I tried to do it like this:
#GetMapping
public String getOrderForm(Model model, #RequestParam(value = "selected") String[] selected){
But I am getting the error that there is no such object. How can I get this checkbox list?

The controller will accept the checked checkboxes as a comma-separated string.
#GetMapping
public String getOrderForm(Model model, #RequestParam("selected") String selected){
String[] stringArray = selected.split(",");
List<String> list = Arrays.asList(stringArray);
... <your code>
return "somePage";
}

What chandra kant said is right, but there is one more thing that you need to know to make this work. You have to keep the name of each checkbox same then only it work. It will automatically generate a string separated by comma. You can test it out by printing it in the controller only and you can also see it in the URL.
#GetMapping
public String getOrderForm(#Param("selected") String selected){
System.out.println(selected);//just to check whether we are getting comma separated value or not
String[] selectedArray = selected.split(",");
List<String> list = Arrays.asList(stringArray);
}

To receive selected checkbox value as a string array in the controller. You have to specify the value of the name attribute of all the checkbox same for the group of the checkbox. And then you will receive the selected checkbox value as an array in the controller.
For the same group of checkbox specific same value for name attribute for a group of checkbox:
<input type="checkbox" name="selected" value="${product1.id}">
<input type="checkbox" name="selected" value="${product2.id}">
<input type="checkbox" name="selected" value="${product3.id}">
In the controller:
#GetMapping
public String getOrderForm(Model model, #RequestParam(value = "selected") String[] selected){

This worked for me
#GetMapping
public String getOrderForm(Model model, #RequestParam("selected") String selected){
System.out.println(selected);//just to check whether we are getting comma separated value or not
String[] selectedArray = selected.split(",");
List<String> list = Arrays.asList(stringArray);
}

in html form need to add this:
<input type="checkbox" name="selected" th:value="${product.id}">
my controller now looks like this:
#GetMapping
public String getOrderForm(Model model, #RequestParam(value = "selected", required = false)Integer[] selected){
if no checkbox is selected then selected will be null

Related

Neither BindingResult nor plain target object for bean name error

Return List of objects
#RequestMapping("/mvt")
public String showMvt(Model model){
model.addAttribute("counts", mouvementRepository.findAll());
return"mvt_";
}
Update one line controller
#RequestMapping(value="/updateMvt/{idmvt}",method = {RequestMethod.GET})
public String updateMvt(#PathVariable(value = "idmvt") int idcpn, #Valid #ModelAttribute("mvt") Mouvement mvt,BindingResult result) {
mouvementRepository.save(mvt);
return "redirect:/mvt";
}
I tried To edit lanes inside tr th:each like this
<tr th:each="count:${counts}">
<form action="#" th:action="#{/updateMvt/{idmvt}(idmvt=${count.idmvt})}" th:object="${count}" method="put">
<table border="0" cellpadding="10">
<tr>
<td>etat</td>
<td>
<select class="select-css" th:field="*{count.etat}">
<option th:value="Enregistrement">Enregistrement</option>
<option th:value="Embarquement">Embarquement</option>
<option th:value="Decollé">Decollé</option>
<option th:value="Annulé" selected>Annulé</option>
<option th:value="null" selected>Aucun</option>
</select>
</td>
</tr>
</table>
</form>
I'm new to spring,i know this solution looks bad,but i'm trying to do my best.
so I tried to do it this way, but I get the error :
Neither BindingResult nor plain target object for bean name 'count'
available as request attribute
Update --------------------------------------------------------------------
so far I managed to create a wrapper class Like this
public class mvtForm {
private int version;
private List<Mouvement> mouvements;//Getters and setters are included
And in my controller I set the movements attribute like this :
#ModelAttribute("wrapper")
#RequestMapping(value = "/mvtall")
public String processQuery(#ModelAttribute mvtForm wrapper, Model model) {
wrapper.setMouvements(mouvementRepository.findAll());
model.addAttribute("wrapper",wrapper);
return "mvt_all";
}
Now I got the view that I needed,
but Is there a way to submit PUT to each lane using JpaRepository ?
th:object must reference an object from Model, not from iteration. So you need to pass count object to model in controller before loading the page, like for example by defining this method in your controller:
#ModelAttribute("updateCount")
public Count count() {
return new Count();
}
class Count should have a field int version that will be unique for each Count object, this is important for updating. Don't forget getters, setters and default constructor - this is important for Thymeleaf.
#ModelAttribute("listOfCounts")
public List<Count> listOfCounts() {
return listOfCounts; //has to be predefined somewhere in class
//as it will be reused throughout application.
}
When you iterate you will be iterating over a listOfCounts. However when updating and pressing update button you will be binding the updateCount object. Make sure to have a hidden field with version in thymeleaf. With this when receiving the update request in your controller, you will receive the updateCount object with the correct version, you can find the correct Count object from the listOfCounts via version and update it.

use RemoteAttribute in modelview for Edit and Create Action

i use RemoteAttribute in my modelview for Check Instantly If Username Exists .
[Remote("ValidUsername","UsersManagement",ErrorMessage ="this usernaem is duplicate")]
public string Username { get; set; }
This idea is useful when inserting a new record, but prevents the update from being edited. because the usernaem is exists. What is the solution to that proposal?
Option 1 - Use Additional fields:
You can use the AdditionalFields argument to your remote validation attribute in your model and combine that with a hidden field in your view .
In your model class :
[Remote("ValidUsername", "Home", ErrorMessage = "this usernaem is duplicate", AdditionalFields = "PageType")]
public string Username { get; set; }
In your edit/create page , add hidden field inside the same form as the field your are validating :
<label asp-for="Username">Username</label>
<input asp-for="Username" />
<span asp-validation-for="Username"></span>
<input type="hidden" name="PageType" value="Edit" />
Then on server side validation , you could get the Additional value(edit/create) and validate base on that , if it is edit ,just skip the validation :
[AcceptVerbs("Get", "Post")]
public IActionResult ValidUsername(string Username, string PageType)
{
if ("Edit".Equals(PageType))
{
return Json(true);
}
if (Username.Equals("21"))
{
return Json(false);
}
return Json(true);
}
Option 2 - Use different view model
You can also use different view model in create and edit pages.

Submit form with only selected model attributes inside table (Spring Boot + Thymeleaf)

This is what I'm trying to do:
I have a with a table where I present model attribute which is a list of objects. I present them in table rows using "th:each"
With every row, I present a checkbox, intending to mark some of the presented table rows
I want to submit the form and POST only the list of selected model attributes (only those in the selected table rows)
This is how I present the model attribute list of objects inside table rows (I skipped irrelevant parts of the code):
<form class="form-horizontal"
th:action="#{'/supplier/define'}"
th:object="${foundSuppliers}" th:method="POST">
.....
<tr th:each="supplier, iterStat : ${foundSuppliers.suppliersDTO}">
<td class="hide" th:text="${supplier.id}">ID</td>
<td th:text="${supplier.name}">Name</td>
<td><input th:id="'chk_' + ${supplier.id}"
type="checkbox" th:field="*{foundSuppliers.suppliersDTO}" th:value="${supplier}">
</td>
</tr>
When I submit this form, my "foundSuppliers.suppliersDTO" list is empty even though I check at least one row in my table.
My controller method behind specified th:action (of "form" tag) is:
public String defineSuppliers(#ModelAttribute SupplierListDTO foundSuppliers, Model model) {
...
}
What should I do to actually populate foundSuppliers.suppliersDTO with a list of objects that I marked with checkbox?
To clarify, "foundSuppliers" is a model attribute I pass from controller, and it contains a list of suppliers. The class that I pass as "foundSuppliers" looks like this:
public class SupplierListDTO {
private List<SupplierDTO> suppliersDTO;
.....
}
And I use the same one as th:object inside "form" tag, this is the list I want to pass to backend controller when form is submitted, except the list should contain only the objects inside table rows where I ticked the checkbox.
First, make sure your entire table is enclosed in the same form (mind you, none of this code has been tested):
<form action="/....." method="POST">
<tr th:each="supplier : ${foundSuppliers}">
<td class="hide" th:text="${supplier.id}">ID</td>
<td th:text="${supplier.name}">Name</td>
<td><input th:id="'chk_' + ${supplier.id}" type="checkbox"></td>
</tr>
</form>
Then, on the controller side, you need a custom data binder converting your list of IDs to your domain objects.
#Component
public class IdToSupplierConverterFactory
implements ConverterFactory<String, Supplier> {
private static class IdToSupplierConverter<Supplier>
implements Converter<String, Supplier> {
#PersistenceContext
private EntityManager em;
public Supplier convert(String id) {
return em.findById(id, Supplier.class);
}
}
#Override
public <Supplier> Converter<String, Supplier> getConverter(
Class<Supplier> targetType) {
return new IdToSupplierConverter();
}
}
Spring should do the rest and map your paramaters to:
#PostMapping("/......")
public YourType myControllerMethod(#RequestParam List<Supplier> suppliers) {
return ...;
}
You can do that either through javascript or Spring Boot. Maybe the easiest way is to to give the your checkbox field a name and then retrieve it through your controller. This way, you can easily check if the value of that name/checkbox is null or not. You should come up with something like this:
#RequestMapping(value = "/supplier" , method = RequestMethod. POST)
public void getSuppliers(#RequestParam(value = "nameOfCheckBoxInThymeleaf", required = false) String checkboxValue)
{
if(checkboxValue != null)
{
// do some logic here - get a list of selected suppliers for example
}
else
{
// do some logic here
}
}

Spring MVC forms: Dynamically add form select options in a one to many relationship

I have a spring form for a complex object that contains a List of email address objects within a One-To-One relationship object that contains a One-To-Many to email addresses. The use-case is as follows: user wants to add new email addresses to the existing ones in the form. This is done via a pop up window and a jQuery that appends a new option element inside the select statement as such:
<option value="0">new.email#address.com</option>
All the prepopulated options in the loaded form of course already have an ID set, such as:
<option value="1">existing.email#email.com</option>
In my controller, the submitted form contains the ID field (1 for the prepopulated object and 0 for the 'new' email address), but the actual value in the form is null - i.e. first#email.com is not submitted with the form and bound to the emailAddress field in the EmailAddress object:
This code:
List<EmailAddress> emailAddress = batchJob.getDestinationExtras().getMailsInternal();
for (EmailAddress e : emailAddress) {
logger.info(e.getEmailAddressId() + " "+e.getEmailAddress());
}
Results into output:
1 null
0 null
This is not a problem for the existing objects because with the ID they can simply be retrieved from the database using hibernate either in the controller or already during the parse stage (I am using a formatter implementation for the EmailAddress object). But for the 'new' objects created dynamically by the user I have a problem because they do not yet exist in the database and thus the actual email address value can not be retrieved in this way.
What could be a solution to this problem?
The JSP tag is as follows:
<form:select multiple="true" path="destinationExtras.mailsInternal" id="internalMailsList">
<form:options items="${internalMailsList}" itemValue="emailAddressId" itemLabel="emailAddress" />
</form:select>
The relevant parts of the objects involved below - omitting other fields and ID fields where not relevant:
#Entity
#Table(name="batchjob")
public class BatchJob {
(...) id + other fields
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="destinationextrasid")
private DestinationExtras destinationExtras;
(...) getters and setters
}
#Entity
#Table(name="destinationextras")
public class DestinationExtras {
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="emailaddressid")
private List<EmailAddress> mailsInternal;
}
#Entity
#Table(name="emailaddresses")
public class EmailAddress {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_EMAILADDRESSID")
#SequenceGenerator(name="SEQ_EMAILADDRESSID",sequenceName="SEQ_EMAILADDRESSID",allocationSize=1)
private int emailAddressId;
#Column(name="email_address")
private String emailAddress;
}
When it goes wrong, think about the low-level HTTP protocol ... JQuery is very clever, so is Spring, but they can only communicate through HTTP ! And when submitting a form containing a <select> field, the only thing that is sent is the value of the selected option(s).
So if you want Spring to use the text of the new email, you could try to put it in the value of the <option> field, like :
<option value="new.email#address.com">new.email#address.com</option>
but I think you will have to change all the select to systematically use the text as value and change it also in your controller, to avoid mixing integer ids and texts. Your options would then be :
<form:options items="${internalMailsList}" itemValue="emailAddress" itemLabel="emailAddress" />
You can also just add a hidden field with the help of jQuery, with the email text as value and use it if you find a value of 0, like that :
<input type="hidden" name="newmail" value="new.email#address.com"/>
<select ...>
<option value="0">new.email#address.com</option>
Personnally, I would prefere that second way, but it really depends on you controller structure.

How to use model attribute inside form tag in a jsp?

I have a jsp form which has model attribute called projectId, I want to use it inside form tag as follows.
form action="cont/upload?id='${projectId}'" name="createDocumentForm" id="createDocumentForm" enctype="multipart/form-data" method="post">
but it is not recognize by the jsp, what am I doing wrong here?
This is the controller that I set projectId attribute.
#RequestMapping(value = "/project/task/addNewDocumentSegment", method = RequestMethod.POST)
public String clinicalProjectDocument(#ModelAttribute("sessionId") String sessionId,#RequestParam("projectId") String projectId,Model model) throws SessionException {
logger.info("addNewDocumentSegment");
logger.info(projectId);
model.addAttribute(projectId, "projectId");
return "segments/task/addNewDocumentDialog";
}
found the mistake,
model.addAttribute(projectId, "projectId");
should change as
model.addAttribute( "projectId", projectId,);

Resources