MVC SelectList vs MultiSelectList - asp.net-mvc-2

In MVC, SelectList derives from MultiSelectList. I can't tell a difference between them though. For both, I have to tell it to select multiple items (I expected to not have to do this since the name has "multi" in it).
If you substitute "SelectList" with "MultiSelectList" in the code below, it will generate the same HTML:
<%
var leftSelectList = new SelectList(Model.LeftSide,"Key","Value");
var attrs = new SortedDictionary<string, object> {{"class", "ui-widget"}};
MvcHtmlString disabledStyle = MvcHtmlString.Create(Html.Encode("'width:50px;'"));
attrs.Add("style", disabledStyle);
attrs.Add("multiple", "multiple");
attrs.Add("size", "5"); /*-- how many items to show--*/
var leftItems = Html.DropDownList("ddlLeftItems", leftSelectList, attrs); %>
<%= leftItems.ToHtmlString()%>
The generated HTML is:
<select class="ui-widget" id="ddlLeftItems" multiple="multiple" name="ddlLeftItems" size="5" style="&#39;width:50px;&#39;">
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
<option value="5">E</option>
<option value="9">I</option>
</select>
So, which one should I use? Thank you.

The SelectList object doesn't generate HTML. It creates a list of items that can then be used elsewhere. The DropDownList helper function is what generates the HTML used on the page, which is why you see the exact same HTML.
The SelectList object is derived from MultiSelectList, but provides you with a property to get the single selected value (called SelectedValue). The MultiSelectList just has a property called SelectedValues that holds all selected values.
So if you are doing a multi-select list, then it doesn't really matter which one you use, but if you are doing a single-select list, then you should use SelectList because it makes it easier to get the selected value.

MultiSelectList is used with Html.ListBox and Html.ListBoxFor methods. These helpers generate this: HTML select multiple Attribute

Related

Blazor: How to use the onchange event in <select> when using #bind also?

I need to be able to run a function after a selection in made in a <select>. The issue is I'm also binding with #bind and I get a error when I try to use #onchange stating that it is already in use by the #bind. I tried using #onselectionchange, but that does nothing(doesn't run the function). I could forget the #bind and just assign #onchange to a function, but I'm not sure how to pass the selected value to the function.
I have the following code:
<select #bind="#SelectedCustID" # #onchange="#CustChanged" class="form-control">
#foreach (KeyGuidPair i in CustList)
{
<option value="#i.Value">#i.Text</option>
}
</select>
Thanks.
#bind is essentially equivalent to the having both value and #onchange, e.g.:
<input #bind="CurrentValue" />
Is equivalent to:
<input value="#CurrentValue" #onchange="#((ChangeEventArgs e) => CurrentValue = e.Value.ToString())" />
Since you've already defined #onchange, instead of also adding #bind, just add value to prevent the clash:
<select value="#SelectedCustID" #onchange="#CustChanged" class="form-control">
#foreach (KeyGuidPair i in CustList)
{
<option value="#i.Value">#i.Text</option>
}
</select>
Source: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-3.1
<select #bind="MyProperty">
<option>Your Option<option>
</select>
#code {
private string myVar;
public string MyProperty
{
get { return myVar; }
set
{
myVar = value;
SomeMethod();
}
}
private void SomeMethod()
{
//Do something
}
}
Just add #bind-value and #bind-value:event="oninput" with #onchange as below. Note the lower case letters. This works for me without any issue.
<select #bind-value="variablenametokeepselectedvalue" #onchange="yourmethodname" #bind-value:event="oninput">
<option value="1">Test</option>
</select>
This seems to be a popular confusion. Firstly you cant use #onchange since it would internally be used by #bind. You should be able to access the selected value from the setter of your CustChanged property. Based on what you are trying to do with your CustChanged, you may not even need to manually check when this value is updated. For instance, if your intent is to use CustChanged in your UI directly or indirectly (within Linq or something), the UI would automatically update with CustChanged value when your <select> is changed. Hence, for most use cases I don't see the need to check when it was updated.
To use #onchange, you can bind it to a function something like this:
public void OnUpdated(ChangeEventArgs e)
{
var selected = e.Value;
}
You can avoid #bind altogether (if you're using a foreach):
<select #onchange=#(handleChange)>
#foreach (var option in _options){
<option value=#option.Id selected=#(SelectedId == option.Id)>#option.Name</option>
}
</select>
#code {
public record Person(int Id, string Name);
public int SelectedId { get; set; }
public List<Person> _options = new List<Person>() {
new Person(1,"A"),
new Person(2,"B"),
new Person(3,"C")
};
public void handleChange(ChangeEventArgs args) {
Console.WriteLine(args.Value);
SelectedId = Int32.Parse(args.Value.ToString());
}
}
Some sordid details: I was getting some weird behavior when trying to use F# with server-side Blazor. In short, setting the List of options to the result of an Entity Framework query (mapped to a list of records) wouldn't #bind properly, but using a dummy list of options that were C# classes and not F# records did work. It wasn't due to it being records though, because if I set the list to the EF query and then immediately set it to a dummy list of records, it still didn't #bind properly - but it did work if I commented out the EF line.
Please check this example. It is using #bind but upon setting the value it triggers #onchange event
<div class="form-group">
<label for="client">Client Name</label>
<select id="client" #bind="CheckSelected" class="form-control">
<option value="selected1">selected1</option>
<option value="selected2">selected2</option>
</select>
</div>
#code {
private string selectedItem {get; set;}
private string CheckSelected
{
get
{
return selectedItem;
}
set
{
ChangeEventArgs selectedEventArgs = new ChangeEventArgs();
selectedEventArgs.Value = value;
OnChangeSelected(selectedEventArgs);
}
}
private void OnChangeSelected(ChangeEventArgs e)
{
if (e.Value.ToString() != string.Empty)
{
selectedItem = e.Value.ToString();
}
}
}
Please check below code for reference how we use select tag with bind value and call function onselection value change in blazor
<InputSelect class="form-control mb-0" ValueExpression="#(()=>request.Id)" Value="request.Id"
ValueChanged="#((string value) => SelectedValueChange(value))">
#if (dropdownResponses != null)
{
#foreach (var item in dropdownResponses)
{
<option value="#item.Id.ToString()">#item.Name</option>
}
}
</InputSelect>
use <InputSelect> tag instead of <select> tag and use ValueChanged method for getting call on select value change
here is the code of ValueChanged function
internal void SelectedValueChange(string value)
{
string NewValue = value;
}
Starting from .NET 7 Preview 7, the recommended way to handle this issue is to use the bind:after modifier.
Here is a small example (partially borrowed from the docs):
<p>
<label>
Select one or more cities:
<select #bind="SelectedCities" multiple #bind:after="DoSomething">
<option value="bal">Baltimore</option>
<option value="la">Los Angeles</option>
<option value="pdx">Portland</option>
<option value="sf">San Francisco</option>
<option value="sea">Seattle</option>
</select>
</label>
</p>
<span>
Selected Cities: #string.Join(", ", SelectedCities)
</span>
#code {
public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };
// Run your logic here after a binding event
private void DoSomething()
{
Console.WriteLine(string.Join(',', SelectedCities));
}
}
My recommendation is, if possible, use an EditForm wrapper around your form elements. Then you can detect a change of any of the form elements, in one place. This is good for, for example, a bunch of search filters. Any change in any of the filters should trigger another query of the data, etc.
Example of how to trigger event on form changes is here:
blazor editform change events
According to Microsoft's documentation, this is their preferred way of handling this problem:
<InputText Value="#NewPaymentAmount" class="mdl-textfield__input"
ValueExpression="() => NewPaymentAmount"
ValueChanged="(string value) => ValidateAmount(value)" />
private void ValidateAmount(string amount)
{
NewPaymentAmount = amount;
// Do validation or whatever
}
Or the async way:
<InputText Value="#NewPaymentAmount" class="mdl-textfield__input"
ValueExpression="() => NewPaymentAmount"
ValueChanged="async (string value) => await ValidateAmountAsync(value)" />
private async Task ValidateAmountAsync(string amount)
{
NewPaymentAmount = amount;
// Do validation or whatever
}
<div class="form-group">
<label for="client">Client Name</label>
<select class="form-control" #onchange="#((e) => { myVar = e.Value.ToString(); MyMethod(); })">
<option value="val1">val1</option>
<option value="val2">val2</option>
</select>
</div>
This is how I was able to set the property while calling for a method in the #onchange event.
-Blazor Server
-Dotnet Core 3.1
I use oninput to essentially have bind and onchange.
oninput triggers when the input value is changed whereas bind/onchange triggers when the element loses focus and the input has changed.
This might not fit every scenario however as it wll trigger on every input while typing, depending on your need or for inputs such as selects, radios, etc. it should be suitable.

The foreach tag in JSP and how to write the code in the Java controller

I have one controller (RegisteredController.java) , and I want that the output of the controller is displayed in the JSP (its name is commment_form.jsp). So I use a forEach tag in the jsp to display a list of comments (the comments which the user has inserted about a given resource). For "resource" I usually mean an image. So there are a list of comments about an "image" and I want that all the comments are all displayed in the bottom page, when a comment is going to be inserted into the comment form. My question is how must be written the code into the controller in order to set the output for the jsp ? Should I use a #ModelAttribute , a put-attribute or something else ? Here is the code of the controller and of the jsp :
The comment_form.jsp is:
<form:form modelAttribute="comments">
<table class="commento">
<tr>
<th/>
<th>ID</th>
<th>Contenuto</th>
</tr>
<c:forEach items = "${comments}" var="comment">
<tr>
<td><c:out value="${comments.content}"></c:out></td>
<td><c:out value="${comments.content}</c:out></td>
</c:forEach>
</table>
</form:form>
The RegisteredController.java is:
#RequestMapping("/comment.do")
public String comment(#ModelAttribute Comment comment, BindingResult
bindingResult, Model model, Locale locale) {
User user=userService.getUserCurrent();
comment.setDatePubblication(SQLUtility.getCurrentDate());
comment.setIdUser(user.getId());
commentService.create(comment);
Object[] args = { comment.getId() };
String message = messageSource.getMessage("message.update", args,locale);
List<Comment> comments =
commentService.findAllCommentByResource(comment.getIdResource());
model.addAttribute("comments", comments);
model.addAttribute("id",comment.getIdResource());
model.addAttribute("message", message);
model.addAttribute("comment", comment);
return "redirect:/registered/comment_start.do";
}
Please any help ? I will appreciate . Thanks you.
In case of redirect pass additional data as redirect attributes.
To carry data across a redirect use RedirectAttributes#addFlashAttribute(key, value).
What Java doc says:
A RedirectAttributes model is empty when the method is called and is never used unless the method returns a redirect view name or a RedirectView.
After the redirect, flash attributes are automatically added to the model of the controller that serves the target URL.
Read more...
One extra note :
In JSP it should be ${comment.content} instead of ${comments.content}

Passing values from a form to a new action

Ok guys i have a question, if this is my Form , and is generated for every item in the DB, i want to send the item with the quantity specified.
to send the quantity , from this razor view
#using (Html.BeginForm("AddToCart", "Prices"))
{
string qtname = "qt" + #item.id;
<div>
<input id="#qtname" name="#qtname" class="quantity" type="text" value="0" readonly="readonly" />
</div>
<input type="submit" value="Adauga" class="addToCart" />`
}
i need just these?
[HttpPost]
public ActionResult AddToCart(ProductsModel Products, string qtname)
{ }
and do i need some html.hidden for passing along the item.id too?
In the form you are sending only the qtname parameter as input field, whereas your controller action also expects a ProductsModel parameter which is never sent. If you want to bind it you will have to create input fields for all properties of this view model.
But in your case a better solution would be to simply include the id of the product as hidden field and then fetch the corresponding product from your datastore given this id:
[HttpPost]
public ActionResult AddToCart(string id, string qtname)
{
ProductsModel products = _repository.GetProduct(id);
...
}

Spring MVC - bind form elements to a List<Long>

Is it possible to bind a form element to a List<Long>?
ie. <form:input path="formValues[0]" /> binding to an element in List<Long> formValues; in the form backing object?
When I try this, it fails because Long does not have a default constructor new Long().
I've worked around it by creating a dummy holder class
class DummyLong {
private Long value;
...
}
making the list in the formbacking object a List<DummyLong> and changing the form tag to <form:input path="formValues[0].value" /> but this seems unnecessarily hideous and I'm sure there must be a better way. Haven't been able to find it though.
Use List<Long> formValues with <form:input path="formValues" />

ASP.Net MVC 2 - ModelBinding and Dictionary<int, int>

In my interface I have a list of text boxes, something like this :
http://screencast.com/t/YjIxNjUyNmU
The number of textboxes is unknown as each of them is associated with a Template.
In my page, the goal is to associate a number to some of those templates.
Here is a sample HTML code :
<% // loop on the templates
foreach(ITemplate template in templates)
{
// get the content from the input dictionary
int val;
content.TryGetValue(template.Id, out val);
// convert it as a string
string value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name/id (for dictionary binding)
string id = ??????????
string name = ??????????????
%>
<label for="<%= name %>"><%= template.Name %></label>
<input type="text" id="<%= id %>" name="<%= name %>" value="<%= value %>" />
<br />
<% }
%>
What I expect, in my controller, is to get a IDictionary where the first int is the template ID , and the other is the count given by the user.
Here is what I want :
public ActionResult Save(int? id, Dictionary<int, int> countByTemplate)
I tried a lot of things but nothing worked. I tried to read the sources but it's a maze, and I'm getting a headhache trying to get information about model binding.
Questions :
is there a good ressource on how the modelbinding works ?
I'd like someting exhaustive, I'm tired of the 84093043 blogs that talk about a given specific example.
how can I build my HTML, using to get a IDictionary (or even a IDictionary in my controller's action ?
Thanks a lot for your help
Information on how to write your input elements for binding to arrays, dictionaries, and other collections can be found at http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx.
Ok... Thanks to Levi I was able to get on a solution.
Not the cleaner one, but it works.
The HTML should be written this way:
<%
int counter = 0;
// loop on the templates
foreach(ITemplate template in templates)
{
// get the value as text
int val;
content.TryGetValue(template.Id, out val);
var value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name (for dictionary binding)
string id = "cbts_{0}".FormatMe(template.Id);
string dictKey = "cbts[{0}].Key".FormatMe(counter);
string dictValue = "cbts[{0}].Value".FormatMe(counter++);
%>
<input type="hidden" name="<%= dictKey %>" value="<%= template.Id %>" />
<input type="text" id="<%= id %>" name="<%= dictValue %>" value="<%= value %>" />
<label for="<%= id %>"><%= template.Name %></label>
<br />
<% }
%>
I had to add a hidden field to store the value.
I introduced a 'fake' counter just to loop over the dictionary the way ASP.Net MVC wants it.
As a result I got a dictionary filled with my values and '0' when the textbox is empty.
Another problem appeared: the ModelState was considered not valid because "a value is required". I don't want my values to be required, but looking at the modelbinder code, I did not found a way to tell the binder that a value is NOT required.
So I tricked the ModelState in my controller, removing all error, like this:
public ActionResult Save(int? id, Dictionary<int, int> cbts)
{
// clear all errors from the modelstate
foreach(var value in this.ModelState.Values)
value.Errors.Clear();
Well... I effectively got a solution, but the HTML is kind of ugly now, and counterintuitive (using an index to loop over a non-indexed collection ??).
And I need to trick each time I'll use this kind of binding to have it all work properly.
So I will now open a new post to make dictionary binder something better.
Here it is: ASP.Net MVC 2 - better ModelBinding for Dictionary<int, int>
EDIT - There is a cleaner solution, thanks to Pavel Chuchuva.
In the controller code, use a nullable int as value for the dictionary.
A bit more code to add, but much cleaner.
public ActionResult Save(int? id, Dictionary<int, int?> cbts)
{
// this is our final dictionary<int, int>
Dictionary<int, int> cbtsFinal = new Dictionary<int, int>();
// loop on the dicitonary with nullable values
foreach(var key in cbts.Keys)
{
// if we have a value
if(cbts[key].HasValue)
// then put it in the final dictionary
cbtsFinal.Add(key, cbts[key].Value);
}