PreSelect Value from Dropdown - spring-mvc

I was able to populate my dropdown, however I cannot preselect the value of the dropdown base on the value coming from DB.
My Thymeleaf
<select id="inputstatus" name="status" th:field="*{status}" >
<option th:each="enumStatus : ${listStatus}"
th:value="${enumStatus.code}"
th:text="#{${enumStatus.value}}" />
</select>
My Controller:
#RequestMapping(value = "userdetails/{username}")
public String getAccounts(#PathVariable String username, Model model) {
Account accountDetail = rsAccountDetailsService.loadAccountByUserName(username);
model.addAttribute(ACCNTSEARCH_MODEL_ACCNTSTATUS_KEY, AccountDetailsStatus.values());
model.addAttribute("userdetailform",accountDetail);
return "account/userdetails";
}
My ENUM
public enum AccountDetailsStatus {
ACTIVE(0, "status.active"),
EXPIRED(2, "status.expired"),
LOCKED(3, "status.locked");
private int code;
private String value;
private final static class BootstrapSingleton {
public static final Map<String, AccountDetailsStatus> lookupByValue = new HashMap<String, AccountDetailsStatus>();
public static final Map<Integer, AccountDetailsStatus> lookupByCode = new HashMap<Integer, AccountDetailsStatus>();
}
AccountDetailsStatus(int code, String value) {
this.code = code;
this.value = value;
BootstrapSingleton.lookupByValue.put(value, this);
BootstrapSingleton.lookupByCode.put(new Integer(code), this);
}
public int getCode() {
return code;
}
public String getValue() {
return value;
}
}
If the user details were loaded with for example "ACTIVE" status, the active status in the dropdown is not selected.

I think your are looking for something similar use the th:selected tag
<option th:each="i : ${#numbers.sequence(0, 23)}" th:value="${i}" th:text="${i}" th:selected="${ i==9 } ">Options</option>
i linked some similar post here
http://forum.thymeleaf.org/th-selected-not-working-on-multiple-select-td4025883.html
th:selected a number in a select/option with Thymeleaf doesn't work

Related

Populate a select list ASP.NET Core MVC

I'm busy with an ASP.NET Core MVC application, and I'm trying to populate a drop down list. I've created a view model and I have added a method to my StoresController that returns a list of stores that I want to display in a dropdown. I've been working off some online tutorials as I'm very new to asp.
View model:
public class StoreListViewModel
{
public List<StoreList> StoreList { get; set; } = new List<StoreList>();
}
public class StoreList
{
public string StoreId { get; set; } = null!;
public string StoreName { get; set; } = null!;
}
StoresController:
public IActionResult LoadStoreList()
{
if (ModelState.IsValid)
{
var storeList = new StoreListViewModel().StoreList.Select
(x => new SelectListItem { Value = x.StoreId, Text = x.StoreName }).ToList();
ViewBag.Stores = storeList;
}
return NotFound();
}
I'm trying to use ViewBag to call my LoadStoreList() method.
<select name="storeList" class="form-control" asp-items="#(new SelectList(ViewBag.Stores, "Value", "Text"))"></select>
When I load my page I get the following error
Value cannot be null. (Parameter 'items')
The page I need the dropdown list on is my CreateUser.cshtml which is bound to my UserModel and has a UsersController. The method I have created for listing the stores is in my StoresController which is bound to my StoresModel. So I'm not sure if that's causing the issue.
I've been battling with this for days, if someone could help me get this working or show me a better method, that would be great.
*Edit
The UserIndex() method is the first method that fires when my users page opens, do I call the LoadStoreList() method from there ?
UserController
public async Task<IActionResult> UsersIndex()
{
return _context.UsersView != null ?
View(await _context.UsersView.ToListAsync()) :
Problem("Entity set 'ApplicationDbContext.Users' is null.");
}
I'm trying to use ViewBag to call my LoadStoreList() method.
ViewBag cannot be used to call any method. You just need set value for ViewBag in the method which renders your show dropdownlist's page.
From your description, you said the page you need the dropdown list on is CreateUser.cshtml. Assume that you render the CreateUser.cshtml page by using CreateUser action.
CreateUser.cshtml:
<select name="storeList" class="form-control" asp-items="#(new SelectList(ViewBag.Stores, "Value", "Text"))"></select>
Controller:
public class YourController : Controller
{
private readonly YourDbcontext _context;
public YourController(YourDbcontext context)
{
_context = context;
}
[HttpGet]
public IActionResult CreateUser()
{
var storeList = _context.StoreLists.Select
(x => new SelectListItem { Value = x.StoreId , Text = x.StoreName }).ToList();
ViewBag.Stores = storeList;
return View();
}
}
YourDbcontext should be something like:
public class YourDbcontext: DbContext
{
public YourDbcontext(DbContextOptions<MvcProjContext> options)
: base(options)
{
}
public DbSet<StoreList> StoreLists{ get; set; }
}
Dont use viewbag for storing list data. Make your view page model including List, for example:
public class UserCreationViewModel{
public int Id{ get; set; }
public string Name { get; set; }
// Any other properties....
public List<StoreList> StoreList { get; set; }
}
in your controller YourController:
[HttpGet]
public IActionResult CreateUser()
{
var storeList = new StoreListViewModel().StoreList.Select
(x => new SelectListItem { Value = x.StoreId, Text = x.StoreName }).ToList();
UserCreationViewModel model=new UserCreationViewModel{
StoreList = storeList
};
return View("createUserViewName", model);
}
in createUserViewName:
#Html.DropDownList("StoreId", new SelectList(Model.StoreList, "StoreId", "StoreName"), "Select", new { #class = "form-control" })
or
<select class="form-control" asp-for="#Model.StoreId" asp-items="#(new SelectList(Model.StoreList, "StoreId", "StoreName"))">
<option value="-1">Select</option>
</select>

List parameter on child component not updating

I have a child component for filtering a search (DropdownFilter) which takes an input of a list of suggestions and a function to update that list.
For some reason DropdownFilter.Suggestions isn't being updated after it is initially set and I don't know how to update it again. Any information about how to update the property after it is initially bound would be great!
DropdownFilter.razor:
<input id="search" #onfocus="SearchFocused" #onblur="SearchUnfocused" #oninput="UpdateSearchText" />
#foreach (var suggestion in Suggestions)
{
<p>#suggestion</p>
}
#code {
[Parameter]
public Action<string> SearchFieldChanged { get; set; }
//[Parameter]
//public RenderFragment<TSuggestion> SuggestionTemplate { get; set; }
[Parameter]
public List<string> Suggestions { get; set; }
private bool searchFocus = false;
private void SearchFocused(FocusEventArgs args) {
searchFocus = true;
//UpdateSearchText();
}
private void SearchUnfocused(FocusEventArgs args) => searchFocus = false;
private void UpdateSearchText(ChangeEventArgs args)
{
SearchFieldChanged.Invoke((string)args.Value);
}
public void Refresh() {
StateHasChanged();
}
}
Index.razor:
#page "/example"
<div class="container-fluid dropdown-holder">
<DropdownFilter #ref="dropdown" Suggestions="#maskResults" SearchFieldChanged="UpdateSearchResults" />
</div>
#code {
DropdownFilter dropdown;
public class MaskResult {
public string name;
}
static readonly string[] allMasks = {
"Electric",
"Water",
"Ground",
"Fire",
"Bug"
};
public List<string> maskResults = allMasks.ToList();
private void UpdateSearchResults(string search)
{
search = search.ToLower();
maskResults = allMasks.Where((mask) =>
{
return mask.ToLower().StartsWith(search);
}).ToList();
dropdown.Refresh();
}
}
I think that you are trying to create a Datalist, please check this answer:"
datalist How to bind selected item to object
If you add a StateHasChanged() call just here it should work:
private void UpdateSearchResults(string search)
{
search = search.ToLower();
maskResults = allMasks.Where((mask) =>
{
return mask.ToLower().StartsWith(search);
}).ToList();
StateHasChanged(); // Add this line
dropdown.Refresh();
}
As I understand, if you update manually a Parameter of a component, there are some cases where Blazor does not get automatically the info that it needs to update its components. So if you call StateHasChanged, it will reevaluate all Parameters of the childreen of the component where you do the call.
I'll let someone correct me if I am wrong.
Thanks and good luck :)

Create a checkbox list from a List<SelectListItem> collection

I worked on a project where I needed to create a checkbox list from a series of model values, and then retrieve the values from the controller on post. Unfortunately, unchecked checkboxes are simply not sent to the form collection on post, and testing each for null makes for clunky and ungainly code. But it turns out that adding hidden fields for the text and value of each SelectListItem makes grabbing the selected values on post a snap, so I built upon some code I found online for creating a checkbox list via a TagHelper. Works great, and I've included comments to show how the TagHelper is used in the view, how to register it, and how to collect the selected values in the controller. Hope it helps someone else.
/// <summary>
/// Creates a checkbox list that can be easily accessed in the model.
/// <para>Example use in view: <checkboxlist asp-items="#Model.SomeSelectList" asp-model-name="SomeSelectList" asp-container="ul" asp-item="li"></checkboxlist></para>
/// <para>Example registration in _ViewImports file: #addTagHelper *, SomeNamespace</para>
/// <para>Example of retrieving selected values from model: model.SomeSelectList.Where(sl => sl.Selected == true).Select(sl => sl.Value).ToList()</para>
/// </summary>
[HtmlTargetElement("checkboxlist", Attributes = "asp-items, asp-model-name, asp-container, asp-item")]
public class CheckboxListTagHelper : TagHelper
{
[HtmlAttributeName("asp-items")]
public IEnumerable<SelectListItem> Items { get; set; }
[HtmlAttributeName("asp-model-name")]
public string ModelName { get; set; }
[HtmlAttributeName("asp-container")]
public string Container { get; set; }
private string _containerId;
[HtmlAttributeName("asp-container-id")]
public string ContainerId
{
get { return !string.IsNullOrWhiteSpace(_containerId) ? $"id=\"{_containerId}\"" : ""; }
set { _containerId = value; }
}
private string _containerName;
[HtmlAttributeName("asp-container-name")]
public string ContainerName
{
get { return !string.IsNullOrWhiteSpace(_containerName) ? $"name=\"{_containerName}\"" : ""; }
set { _containerName = value; }
}
private string _containerClass;
[HtmlAttributeName("asp-container-class")]
public string ContainerClass
{
get { return !string.IsNullOrWhiteSpace(_containerClass) ? $"class=\"{_containerClass}\"" : ""; }
set { _containerClass = value; }
}
[HtmlAttributeName("asp-item")]
public string Item { get; set; }
private string _itemId;
[HtmlAttributeName("asp-item-id")]
public string ItemId
{
get { return !string.IsNullOrWhiteSpace(_itemId) ? $"id=\"{_itemId}\"" : ""; }
set { _itemId = value; }
}
private string _itemName;
[HtmlAttributeName("asp-item-name")]
public string ItemName
{
get { return !string.IsNullOrWhiteSpace(_itemName) ? $"id=\"{_itemName}\"" : ""; }
set { _itemName = value; }
}
private string _itemClass;
[HtmlAttributeName("asp-item-class")]
public string ItemClass
{
get { return !string.IsNullOrWhiteSpace(_itemClass) ? $"id=\"{_itemClass}\"" : ""; }
set { _itemClass = value; }
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var sb = new StringBuilder();
sb.Append($#"<{Container} {ContainerId} {ContainerName} {ContainerClass}>");
var index = 0;
foreach (var item in Items)
{
var selected = item.Selected ? #"checked=""checked""" : "";
var disabled = item.Disabled ? #"disabled=""disabled""" : "";
sb.Append($#"<{Item} {ItemId} {ItemName} {ItemClass}>");
sb.Append($#"<input type=""checkbox"" {selected} {disabled} id=""{ModelName}_{index}__Selected"" name=""{ModelName}[{index}].Selected"" value=""true"" /> ");
sb.Append($#"<label for=""{ModelName}_{index}__Selected"">{item.Text}</label>");
sb.Append($#"<input type=""hidden"" id=""{ModelName}_{index}__Text"" name=""{ModelName}[{index}].Text"" value=""{item.Text}"">");
sb.Append($#"<input type=""hidden"" id=""{ModelName}_{index}__Value"" name=""{ModelName}[{index}].Value"" value=""{item.Value}"">");
sb.Append($#"</{Item}>");
index++;
}
sb.Append($#"</{Container}>");
output.Content.AppendHtml(sb.ToString());
}
}
My new project uses the full .NET Framework, which for some reason doesn't support TagHelpers, so I created an extension method for the SelectListItem collection. This is a stripped-down version of the TagHelper class I posted above, since I didn't need all the bells and whistles. I'm using Bootstrap 3.4.1 for scaffolding on the page that uses this extension method, so I used a default container of a div with class "form-check". Here's the code for the extension method:
public static string GenerateCheckboxList(this List<SelectListItem> selectListItems, string modelName)
{
var sb = new StringBuilder();
var index = 0;
foreach (var item in selectListItems)
{
var selected = item.Selected ? #"checked=""checked""" : "";
var disabled = item.Disabled ? #"disabled=""disabled""" : "";
sb.Append($#"<div class=""form-check"">");
sb.Append($#"<label for=""{modelName}_{index}__Selected"">{item.Text}</label> ");
sb.Append($#"<input type=""checkbox"" {selected} {disabled} id=""{modelName}_{index}__Selected"" name=""{modelName}[{index}].Selected"" value=""true"" class=""form-check-input"" />");
sb.Append($#"<input type=""hidden"" id=""{modelName}_{index}__Text"" name=""{modelName}[{index}].Text"" value=""{item.Text}"">");
sb.Append($#"<input type=""hidden"" id=""{modelName}_{index}__Value"" name=""{modelName}[{index}].Value"" value=""{item.Value}"">");
sb.Append($#"</div>");
index++;
}
return sb.ToString();
}
I forgot to mention... when rendering the generated string in the view, you need to include a call to render the raw content, or the rendering engine will encode the HTML and it will look bizarre. This is all that's needed in the view:
#Html.Raw(Model.CheckboxList)

Thymeleaf inserting new item in database generates error Property or field cannot be found on null

I'm in the process of learning spring. I'm using thymeleaf to create a simple web app that adds, edits and removes users from a database.
I'm using an html page to display the database and two separate pages for editing and adding new users.
Edit and remove work perfectly but whenever i try to add a user i get an error in new.html (new.html contains the form to add a new user)
Property or field xxxx cannot be found on null
The error shows up in the from at th:text="#{user.name}" .From what I've found online thymelaf does not take null values, however as this is a new object I am trying to add all values are null.
Is there any way to solve this. Code.
new.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>New User</title>
</head>
<body>
<form method="post" name="comment_form" id="comment_form" th:action="#{/create}" role="form">
Name:<br>
<input type="text" name="name" th:text="${user.name}"><br>
Age:<br>
<input type="text" name="age" th:text="${user.age}"><br>
Email: <br>
<input type="text" name="email" th:text="${user.email}"><br>
<button type="submit" id="submit" class="btn btn-primary">Submit</button>
</form>
</body>
</html>
Controller
#Autowired
UserService service;
#RequestMapping(value="user/new", method = RequestMethod.GET)
public String newUser(Long id, Model md) {
Users user = service.findOne(id);
md.addAttribute("user", user);
return "new";
}
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String create(#RequestParam("id") Long id, #RequestParam("name") String name, #RequestParam("age") int age,
#RequestParam("email") String email, Model md) {
md.addAttribute("users", service.addOrUpdate(new Users(id, name, age)));
return "redirect:/user";
}
Service class
#Autowired
JdbcTemplate template;
public Users findOne(Long id)
{
String sql = "select * from people where id=" +id;
return template.query(sql, new ResultSetExtractor<Users>() {
#Override
public Users extractData(ResultSet resultSet) throws SQLException, DataAccessException {
if (resultSet.next()) {
Users user = new Users(resultSet.getLong("id"),
resultSet.getString("name"),
resultSet.getInt("age"));
String email = resultSet.getString("email");
if (email != null) {
user.setEmail(email);
}
return user;
}
return null;
}
});
}
public int addOrUpdate(Users user){
if (user.getId() > 0) {
String sql = "UPDATE people SET name=?, age =?, email=? WHERE id=" +user.getId();
System.out.println(sql);
return template.update(sql, user.getName(), user.getAge(), user.getEmail());
} else {
String sql = "INSERT INTO people ( name, age, email) VALUES ( ?, ?, ?)";
System.out.println(sql);
return template.update(sql, user.getName(), user.getAge(), user.getEmail());
}
}
Users (Model)
package ro.database.jdbcTest.model;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
public class Users {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
private int age;
private String email;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getEmail() {
return email;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public Users(Long id, String name, int age){
this.id=id;
this.name=name;
this.age=age;
}
public void setEmail(String email){
this.email=email;
}
}
Since your user object is null you are getting that error.
All you need to do is send a new User() object every time a request with a new user comes.
#RequestMapping(value="user/new", method = RequestMethod.GET)
public String newUser(Long id, Model md) {
Users user = null;
if(id > 0) { // Id is present, fetch from the database.
user = service.findOne(id);
} else { // Create a new user.
user = new User();
}
md.addAttribute("user", user);
return "new";
}
Using the above way you will never have a null user in new.html

Spring MVC checkboxes HTTP Status 400 The request sent by the client was syntactically incorrect

I have this simple form with 2 checkboxes and a submit button. When I submit the form, I get this error
HTTP Status 400 The request sent by the client was syntactically incorrect.
This is my POJO:
public class Menu{
private String day;
private String name;
private int price;
public Menu(){
}
public Menu(String day, String name, int price) {
this.day = day;
this.name = name;
this.price = price;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDay() {
return day;
}
public void setDay(String l) {
this.day = l;
}
#Override
public int hashCode() {
int hash = 3;
hash = 7 * hash + this.day.hashCode();
hash = 7 * hash + this.name.hashCode();
return hash;
}
#Override
public boolean equals(Object object) {
boolean result = false;
System.out.println("ARE YOU EVER CALLLED HOW MANY TIMES");
if (object == null || object.getClass() != getClass()) {
result = false;
} else {
Menu sc = (Menu) object;
if (this.day == sc.getDay() && this.name == sc.getName()
&& this.price == sc.getPrice()) {
result = true;
}
}
return result;
}
This is my Order class:
public class Order {
private List<Menu> menus = new ArrayList<Menu>();
public Order(){}
public Order(ArrayList<Menu> menus){
this.menus = menus;
}
public List<Menu> getMenus() {
return menus;
}
public void setMenus(ArrayList<Menu> menus) {
this.menus = menus;
}
}
And this is my controller:
#Controller
public class RestaurantController {
#RequestMapping(value = "/menu", method = RequestMethod.GET)
public String menuPage(Model model){
Order o = new Order();
ArrayList<Menu> m = new ArrayList<Menu>();
m.add(new Menu("Sunday", "Phir Aloo", 12));
m.add(new Menu("Sunday", "Phir Cholay", 9));
model.addAttribute("today", m);
model.addAttribute("order", o);
return "/menu";
}
#RequestMapping(value = "/confirm", method = RequestMethod.POST)
public String done(#ModelAttribute(value="order") Order order, Model model){
return "/confirm";
}
And this is my menu.jsp: (http://localhost:9080/res/menu)
<form:form modelAttribute="order" method="post" action="/res/confirm">
<c:forEach items="${today}" var="r">
<form:checkbox path="menus" value="${r}" label="${r.name } ${r.price }" />
</c:forEach>
<input type="submit" value="Submit Data">
</form:form>
Now I just expect Class Order's property 'menus' to be filled with selected checkboxes. Instead I get this error "The request sent by the client was syntactically incorrect. I have looked up every possible answer on this website but nothing seems to be solving the problem.
After #R Sawant's suggestion I was able to solve the problem. Here is my Property Editor.
public class MenuTypeEditor extends PropertyEditorSupport {
public void setAsText(String text) {
setValue(new Menu(text.toUpperCase()));
}
}
I kept this class inside the same package which has Menu.java and Order.java
Now inside my controller wrote this:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Menu.class, new MenuTypeEditor());
}
And voila! Magic happened.
I hope this answer can help someone.
The problem is with the value you are posting when the check box is ticked. Look at the below code
<form:checkbox path="menus" **value="${r}"** label="${r.name } ${r.price }" />
See what have assigned to value attribute in the above line. Its whole object of menu. It will essentially post the toString() representation of the object. Since you have not implemented toString() for Menu class, something like Menu#1ed2e55e gets posted for the check box value. Spring is unable to convert this to something meaningful and hence the problem.
You have to make use of property editor support to deal with these type of situations. Property editor will help you convert string to Object and vice versa. In your case String to Menu object and vice versa. Take a look at examples of property editors. Hope this helps
Edit:- a google search got this result. Take a look at it, may help you to understand.

Resources