Spring Boot dropdown menu with options select validation - spring

I have ENUM where I saved types of Income Categories, than I use that ENUM and map it inside my Entity, later I populate my dropdown list with that ENUM and show to user. I want to create a validation so user cant submit form until he choose one option from the dropdown list. Below is code that I think is useful to solve this and also the way I tried to show a error to the user if he didn't select any option from dropdown menu.
I have this ENUM:
public enum IncomeCategories {
SALARY("Salary"),
BUSINESS("Business"),
GIFTS("Gifts"),
EXTRA_INCOME("Extra income"),
LOAN("Loan"),
PARENTAL_LEAVE("Parental Leave"),
INSURANCE_PAYOUT("Insurance payout"),
OTHER("Other");
private final String displayName;
IncomeCategories(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
This is ENUM inside entity:
#Enumerated(EnumType.STRING)
#NotNull(message = "Please, select option from list.")
#Column(name = "income_categories", columnDefinition = "ENUM('SALARY', 'BUSINESS', 'GIFTS', 'EXTRA_INCOME', 'LOAN', 'PARENTAL_LEAVE', 'INSURANCE_PAYOUT', 'OTHER')")
private IncomeCategories incomeCategories;
And this is how I tried to show error if nothing is selected from dropdown menu:
<div class="form-group col-md-8">
<select th:field="${transaction.incomeCategories}">
<option value="0">Select income category</option>
<option
th:each="incomeCategories : ${incomeCategories}"
th:value="${incomeCategories}"
th:text="${incomeCategories.displayName}"
></option>
</select>
<span
th:if="${#fields.hasErrors('${transaction.incomeCategories}')}" th:errors="${transaction.incomeCategories}"
class="text-danger"></span>
</div>
But I'm getting this:

Related

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.

Generating a selected attribute with DropDownListFor (not DropDownList), by using strongly type model with SelectList

I've tried to populate a dropdownlist with a viewModel passed from a controller with the main goal of setting a selected attribute tag, so that when the dropDown-list loads a specific item in the dropdown-list is selected. I'm using MVC 5 with Razor.
There are many question related to DropDownListFor but unfortunately I haven't found what I'm looking for.
I've found many solutions that work with viewbags, but I this is not
what I'm looking for, I want to populate the dropdown-list through a
strongly typed model.
The Html helper I want to use is #Html.DropDownListFor (not #Html.DropDownList). From what I know helpers postfixed with For are overloaded with linq expressions, they are used to deal with models and are strongly typed, while the non postfixed For are used with viewbags.
This is what it should look like, the default city selected should be "Monza"
At the moment I've managed to achieve this result only with #Html.DropDownList, as said before this is not my intention I want to achieve the same result with #Html.DropDownListFor. For the sake of documentation this is the code:
This approach uses #Html.DropDownList
MODEL
public class City
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CityId { get; set; }
public int ProvinceId { get; set; }
[Display(Name = "Città")]
public string Name { get; set; }
[Display(Name = "CAP")]
public string ZipCode { get; set; }
public int Dispose { get; set; }
public virtual Province Province { get; set; }
}
VIEW MODEL
public class AccountIndexGetVM
{
public UserData userData = new UserData();
public AddressUser addressUser = new AddressUser();
public IEnumerable<SelectListItem> cities { get; set; } //<--Cities to DDL
}
CONTROLLER ACTION
//Populating data to the view model and send to view
accountIndexGetVM.userData = userData;
accountIndexGetVM.addressUser = addressUser;
accountIndexGetVM.cities = new SelectList(db.Cities, "CityId", "Name", addressUser.City.CityId.ToString());
return View(accountIndexGetVM);
VIEW
creating dropdown-list in
#using MyProject.ViewModels.Account
#model AccountIndexGetVM
//some code...
<td>#Html.LabelFor(m => m.addressUser.City.Name)</td>
<td>#Html.DropDownList("CityAttributeIDVAlue", Model.cities, "Please Select a City")</td>
HTML SOURCE CODE RESULT
<td><select id="CityAttributeIDVAlue" name="CityAttributeIDVAlue"><option value="">Please Select a City</option>
<option value="277">Aosta</option>
<option value="4156">Meda</option>
<option value="4175">Melegnano</option>
<option value="4310">Milano</option>
<option selected="selected" value="4750">Monza</option> <!--Selected is PRESENT-->
</select></td>
This approach uses #Html.DropDownListFor
As you can see the default selected ddl item is not select, instead the optional ("Please select a city") is selected. This is not my intention, "Monza" should be selected when the ddl is loaded and a selected attribute should be present in the HTML option tag.
The only change I've made is in the view, this is the code, and used the DropDownListFor helper:
VIEW
<td>#Html.LabelFor(m => m.addressUser.City.Name)</td>
<td>#Html.DropDownListFor(m => m.cities, (IEnumerable<SelectListItem>)Model.cities, "Please Select a City")</td>
HTML GENERATED
<td><select id="cities" name="cities"><option value="">Please Select a City</option>
<option value="277">Aosta</option>
<option value="4156">Meda</option>
<option value="4175">Melegnano</option>
<option value="4310">Milano</option>
<option value="4750">Monza</option> <!--No selected attribute present-->
</select></td>
Question:
Is there a way to use DropDownListFor Html helper to generated a slected tag in the html ddl or the only way to go is DropDownList?
I'm not sure how Select helpers differ. I passed this list along with the view model.
List<SelectListItem> citySelectList = new List<SelectListItem>();
cityList = cities.GetAll().OrderBy(x => x.DESCRIPTION).ToList();
foreach (var city in cityList)
{
citySelectList .Add(new SelectListItem()
{
Value = city.ID,
Text = city.Desc,
Selected = true
});
}
Then on the view:
#Html.DropDownListFor(x => x.City, Model.CitySelectList, "", new { id = "citySelectID" })

Data annotation on int[]

In a ASP.NET MVC5, I'm using the chosen JS library for a multi-dropdown select. How Can I do to use Data Annotation to validate the field?
Actually I use [Required] on all fields, this multi-dropdown select too, but it isn't working.
Code:
[MinLength(1)]
public int[] fields{ get; set; }
Here is my Code in the cshtml:
#Html.ListBoxFor(x => x.fields, Model.fieldsSelect, new { data_placeholder = "pholder" })
#Html.ValidationMessageFor(model => model.fields, "", new { #class = "text-danger" })
Without the plugin I currently use (chosen) there is no validation , Here is the HTML rendered without chosen:
<div class="col-md-10">
<select data-placeholder="Enter multiple fields" data-val="true" data-val-minlength="The field fieldsmust be a string or array type with a minimum length of '1'." data-val-minlength-min="1" id="fields" multiple="multiple" name="fields">
<option value="944454">WARUYFJGHIE</option>
<option value="33033095">WEBJKHGJHGVHGAN</option>
</select>
<span class="field-validation-valid text-danger" data-valmsg-for="fields" data-valmsg-replace="true"></span>
</div>
Validation works for all my string but not this one: when I select nothing on the form, all [Required] for strings works: an error message is apparing and submit is not hitting the controller/server, but not the [MinLength(1)]... No error message and when I fill all except [MinLength(1)], the form is submitting and error occurs in the controller/server because of null.
Versions of JS validations:
jQuery Validation Plugin - v1.11.1 - 3/22/2013
jquery.validate.unobtrusive.min.js : no version (neither in the
jquery.validate.unobtrusive..js)
You can use the MinLengthAttribute
[MinLength(1)]
public int[] fields{ get; set; }
Edit
Based on additional comments, a jquery plugin is being used that hides the <select>. By default hidden fields are not validated. To include hidden fields, add the following
$.validator.setDefaults({
ignore: []
});

DropDown list issue in knockout js asp.net mvc 3

I have the following problem. I'm developing web application on asp.net mvc and using KnockoutJS in one of views. I have the following viewmodel
public class ExampleViewModel
{
public IEnumerable<Element> ElementsList { get; set; }
}
class Element
{
public bool Required {get;set;}
}
option Required must be set with dropdown list. I have the following block code in view
<div data-bind="foreach: ElementsList">
<select data-bind="attr: { name: 'ElementsList[' + $index() + '].Required' }, value: Required">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
when I select Yes or No from drop down and submit form I have appropriate value saved in database, but when I open this view in browser after that all values in drop down list are 'Yes'. Despite the fact that when I open view and debug it I can see with Quick Watch, that each value from ElementsList has correct value of Required option ('Yes' or 'No'), all dropdown lists have a value 'Yes'.

Spring 3: Select value to enum value mapping

I have a very simple scenario to handle. An enum is created to represent a set of options for select control. The select control needs to have a prompt mapped to '-' as the prompt value. The corresponding enum does not have this dash. When page is submitted with select control still sitting at the prompt, exception is thrown. How do you handle such cases?
Page:
<select id="filterUserAccessLevel" name="filterUserAccessLevel">
<option value="-">Select Value</option>
<option value="DEPOSITOR">Depositor</option>
<option value="READER">Reader</option>
<option value="AUTHOR">Author</option>
<option value="EDITOR">Editor</option>
<option value="ADMINISTRATOR">Administrator</option>
</select>
<input type="submit" name="resetFilter" value="<spring:message code="common.filterResetButtonLabel" />" />
UserAccessLevel enum:
public enum UserAccessLevel {
DEPOSITOR("DEPOSITOR"),
READER("READER"),
AUTHOR("AUTHOR"),
EDITOR("EDITOR"),
ADMINISTRATOR("ADMINISTRATOR");
private String code;
private UserAccessLevel(String code) {
this.code=code;
}
public String getCode() {
return this.code;
}
}
Controller:
#RequestMapping(value="/userIndex/", method=RequestMethod.POST, params="resetFilter")
public void resetFilter(#ModelAttribute("userIndexBean") UserIndexBean bean, Model model) {
System.out.println("resetFilter()");
bean.resetFilterSection();
loadBean(1, bean, model);
}
Exception:
Field error in object 'userIndexBean' on field 'filterUserAccessLevel': rejected value [-];
Why is necessary an option mapped to "-"? Can't it be just an empty String?
In this case, I think that the simplest solution is:
<option value="">Select Value</option>
.
#RequestMapping("userIndex")
public void resetFilter(#RequestParam(required = false) UserAccessLevel filterUserAccessLevel) {
...
}

Resources