Blazor select dropdown set active value by code - drop-down-menu

I am dynamicaly filling the values of a dropdown list based of the content of a List. when a item in the dropdown is selected an option to remove this item is shown. When the item is removed, it is first removed from the list and then the dropdown list is rebuilt, this is where I run in to problems. Instead of returning the dropdown to its default value when it is rebuilt the value just below the removed one is shown as selected (this happens whitout the #onchange value being triggered). How can i make the dropdown list return to its default value when it is being rebuilt?
here is some code, Razor code:
<select class="form-control" #onchange="selectedValue">
<option value="">select one</option>
#foreach (var mod in values)
{
<option value="#mod.Id">#mod.Name</option>
}
</select>
the class named Items that is populationg the list:
public class Items
{
public string Name { get; set; }
public int Id { get; set; }
public Items(string name, int id)
{
Name = name;
Id = id;
}
}
the list itself (before it is populated):
public List<Items> itm = new List<Items>();
the function thats called onchange:
public string SelectedValue = "";
public void selectedValue(ChangeEventArgs selectEvent)
{
SelectedValue = selectEvent.Value.ToString();
}
so to sum it up, this is what's happening:
the list is pupolated with Items.
the select list is built of the items in the list using a #foreach loop (se razor code).
an item is selected from the dropdown, this runs the selectedValue() function and changes the value ud the SelectedValue string.
the selected item is deleted from the list of items
the dropdown is rebuilt using the modefied list
the selected item is now set to the one directly below the deleted item, this is happening without onchange being run. this is the problem
Now the selected value of the dropdown don't correspond to the one of the SelectedValue string.
this can probably be solved by setting the element to its default option/value, but i can't figure out how to do this.
How can I change the selected value to be the default option (in my case the "select one" option with value "") of my dropdown list?

I was having a similar issue. I solved it by putting an if statement in the loop that generates the option elements and from there conditionally add the selected attribute, i.e.;
<select #onchange="listChanged">
#foreach (var item in PageModel.Lists)
{
if(item.Id == currListId)
{
<option value="#item.Id" selected>#item.Name</option>
}
else
{
<option value="#item.Id">#item.Name</option>
}
}
</select>
Sorry for not taking the time to adapt my code to your situation, but you've already solved your problem. This is for anyone else that comes across this problem (prolly me next time i need to do something like this, lol).

How can I change the selected value to be the default option (in my case the "select one" option with value "") of my dropdown list?
I'm afraid you can do it only with JSInterop as follows:
Generally speaking, the selectedIndex property on the select element is set to 0 after you select an option in the select element. This is done in the selectedValue method...
This is a complete working code snippet. Run it and test it...
#page "/"
<select #ref="myselect" id="myselect" class="form-control"
#onchange="selectedValue">
<option selected value="-1">select one</option>
#foreach (var item in items)
{
<option value="#item.ID">#item.Name</option>
}
</select>
<p>#SelectedValue</p>
#code {
[Inject] IJSRuntime JSRuntime { get; set; }
private ElementReference myselect;
List<Item> items = Enumerable.Range(1, 10).Select(i => new Item {
Name = $"Name {i.ToString()}", ID = i }).ToList();
public string SelectedValue = "";
public void selectedValue(ChangeEventArgs args)
{
SelectedValue = args.Value.ToString();
var item = items.Single(item => item.ID == Convert.ToInt32(SelectedValue));
items.Remove(item);
JSRuntime.InvokeVoidAsync("exampleJsFunctions.selectElement", myselect);
}
public class Item
{
public string Name { get; set; }
public int ID { get; set; }
}
}
Put the following JS code in the _Host.cshtml file:
<script src="_framework/blazor.server.js"></script>
<script>
window.exampleJsFunctions =
{
selectElement: function (element) {
element.selectedIndex = 0;
}
};
</script>

A similar problem confronted me in a .NET MAUI Blazor project. In the project I'm working on a view model manages most of the form behavior. In addition, CommunityToolkit.Mvvm is used to manage the property changed management. So this answer will base itself on that configuration adjusted for using bindings. It also follows answer given by #MikeT.
The assumption is that another element is effecting the selection decision. In my case if there is only one element in values to choose from, that element would become selected automatically. Otherwise, the user makes a selection.
<select class="form-control" #Bind="vm.selectedValueId"
disabled="#(vm.IsValuesDataLoaded == false)">
<option value="">select one</option>
#foreach (var mod in values)
{
#if(#mod.Id == #vm.selectedValueId)
{
<option value="#mod.Id" selected>#mod.Name</option>
}
else
{
<option value="#mod.Id">#mod.Name</option>
}
}
</select>
#code{
ValueViewModel vm; //instantiated inside OnInitialized
// more ...
}
As the view model is handling construction of the element list in values, all of the activity takes place there. So this section of view model code would be --
partial void OnSomeOtherPreliminaryPropertyChanged(string? value)
{
// code for processing this event
// condition test could also be for an entry previously selected
if(values.Count == 1)
{
SelectedValueId = whatever_Value_Should_Be_Default_For_Selection;
OnModuleIdChanged(selectedValueId);
}
IsValuesDataLoaded = true;
}
[ObservableProperty]
private int selectedValueId;
[ObservableProperty]
private string selectedValue;
//OnSelected... is called when selectedValueId changes. In this case
// the code is changing the selection
partial void OnSelectedValueIdChanged(int? value)
{
SelectedValue = Values[value].Name
}
public bool IsValuesDataLoaded //controls the enabled state of the select element
{
get;
private set;
} = false;

Related

How to bind a list of objects in ASP.NET Core MVC when the posted list is smaller than original?

Using "disabled" attribute on inputs on form does not post them, which is expected and wanted. However, if you prepare a form of 3 objects in a list, disable the first and third, and submit, the 2nd object appears in post header, but does not bind to the list correctly, because it has an index [1] instead of [0].
I understand how model binding works and why it does not bind the posted object that I want, but I don't know how else to describe the problem to get specific results that would lead me to my solution. Anything I search for leads to basic post and binding examples.
List inside the model I'm using:
public IList<_Result> Results { get; set; }
Class _Result has one of the properties:
public string Value { get; set; }
I fill up the list and use it in view like so:
#for (int i = 0; i < Model.Results.Count; i++)
{
...
<td>
<input asp-for="Results[i].Value" disabled />
</td>
...
}
I have checkboxes on form, which remove (with javascript) the "disabled" attribute from the inputs and thus allow them to be posted.
When I fill up the said list with 3 _Result objects, they are shown on form and all have the "disabled" attribute. If I remove the "disabled" attribute from the first two objects and click on submit button, I receive the Results list with first 2 _Result objects, which is as expected.
However, if I remove the "disabled" attribute only from the second _Result object (the first _Result object still has "disabled" attribute), the Results list comes back empty in my Controller method.
In my Form Data Header, I see this: "Results[1].Value: Value that I want posted", which means that post occurs, but list does not bind the object due to the index.
Any idea on how I can achieve that proper binding? Also, the reason I'm using "disabled" attribute is because I'm showing many results on a single page and want to only post those that are selected.
For getting selected items, you could try checkbox with View Model instead of using jquery to control the disable property.
Change ViewModel
public class ModelBindVM
{
public IList<_ResultVM> Results { get; set; }
}
public class _ResultVM
{
public bool IsSelected { get; set; }
public string Value { get; set; }
}
Controller
[HttpGet]
public IActionResult ModelBindTest()
{
ModelBindVM model = new ModelBindVM
{
Results = new List<_ResultVM>() {
new _ResultVM{ Value = "T1" },
new _ResultVM{ Value = "T2" },
new _ResultVM{ Value = "T3" }
}
};
return View(model);
}
[HttpPost]
public IActionResult ModelBindTest(ModelBindVM modelBind)
{
return View();
}
View
<div class="row">
<div class="col-md-4">
<form asp-action="ModelBindTest">
#for (int i = 0; i < Model.Results.Count; i++)
{
<input type="checkbox" asp-for="Results[i].IsSelected" />
<label asp-for="#Model.Results[i].IsSelected">#Model.Results[i].Value</label>
<input type="hidden" asp-for="#Model.Results[i].Value" />
}
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>

MVC Validate At Least One Checkbox Or a Textbox is Selected

I have a form where either at least one checkbox must be checked OR a textbox is filled in.
I have a ViewModel that populates the CheckboxList and takes the selected values plus the textbox (other) value when required to a SelectedWasteTypes property within the ViewModel. I think my problem is I can't validate against this property as there is no form element on the view that directly relates to it. I've very new to MVC and this one has stumped me.
From the ViewModel
public List<tblWasteTypeWeb> WasteTypeWebs { get; set; }
public string WasteTypeWebOther { get; set; }
public string SelectedWasteTypes { get; set; }
View (segment)
#using (Html.BeginForm("OrderComplete", "Home"))
{
//Lots of other form elements
#for (var i = 0; i < Model.WasteTypeWebs.Count; i++)
{
var wt = Model.WasteTypeWebs[i];
#Html.LabelFor(x => x.WasteTypeWebs[i].WasteTypeWeb, wt.WasteTypeWeb)
#Html.HiddenFor(x => x.WasteTypeWebs[i].WasteTypeWebId)
#Html.HiddenFor(x => x.WasteTypeWebs[i].WasteTypeWeb)
#Html.CheckBoxFor(x => x.WasteTypeWebs[i].WasteTypeWebCb)
}
<br />
<span>
#Html.Label("Other")
#Html.TextBoxFor(x => x.WasteTypeWebOther, new { #class = "form-control input-sm" })
</span>
//More form elements
<input type="submit" value="submit" />
}
Controller Logic (if you can call it that)
[HttpPost]
public ActionResult OrderComplete(OrderViewModel model)
{
var sb = new StringBuilder();
if (model.WasteTypeWebs.Count(x => x.WasteTypeWebCb) != 0)
{
foreach (var cb in model.WasteTypeWebs)
{
if (cb.WasteTypeWebCb)
{
sb.Append(cb.WasteTypeWeb + ", ");
}
}
sb.Remove(sb.ToString().LastIndexOf(",", StringComparison.Ordinal), 1);
}
model.SelectedWasteTypes = sb.ToString();
if (!string.IsNullOrEmpty(model.WasteTypeWebOther))
{
if (!string.IsNullOrEmpty(model.SelectedWasteTypes))
{
model.SelectedWasteTypes = model.SelectedWasteTypes.TrimEnd() + ", " + model.WasteTypeWebOther;
}
else
{
model.SelectedWasteTypes = model.WasteTypeWebOther;
}
}
return View(model);
}
I very much feel I'm up a certain creek... I've thought about using JQuery, but ideally I'd like server side validation to be sure this info is captured (its a legal requirement). However, if this can only be achieved client side, I will live with it.
Any suggestions?
Take a look at the MVC Foolproof Validation Library. It has validation attributes for what you are trying to accomplish: [RequiredIfEmpty] and [RequiredIfNotEmpty]. You can also take a look at my previous SO answer about Conditional Validation.
I would suggest you to implement IValidatableObject in your ViewModel.
Inside Validate( ValidationContext validationContext) method you can check weather your conditions are met. For example:
if(string.IsNullOrWhiteSpace(WasteTypeWebOther))
yield return new ValidationResult("Your validation error here.");

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" })

dropdownlist set selected value in MVC3 Razor

Here is my model:
public class NewsCategoriesModel {
public int NewsCategoriesID { get; set; }
public string NewsCategoriesName { get; set; }
}
My controller:
public ActionResult NewsEdit(int ID, dms_New dsn) {
dsn = (from a in dc.dms_News where a.NewsID == ID select a).FirstOrDefault();
var categories = (from b in dc.dms_NewsCategories select b).ToList();
var selectedValue = dsn.NewsCategoriesID;
SelectList ListCategories = new SelectList(categories, "NewsCategoriesID", "NewsCategoriesName",selectedValue);
// ViewBag.NewsCategoriesID = new SelectList(categories as IEnumerable<dms_NewsCategory>, "NewsCategoriesID", "NewsCategoriesName", dsn.NewsCategoriesID);
ViewBag.NewsCategoriesID = ListCategories;
return View(dsn);
}
And then my view:
#Html.DropDownList("NewsCategoriesID", (SelectList)ViewBag.NewsCategoriesID)
When i run, the DropDownList does not select the value I set.. It is always selecting the first option.
You should use view models and forget about ViewBag Think of it as if it didn't exist. You will see how easier things will become. So define a view model:
public class MyViewModel
{
public int SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
and then populate this view model from the controller:
public ActionResult NewsEdit(int ID, dms_New dsn)
{
var dsn = (from a in dc.dms_News where a.NewsID == ID select a).FirstOrDefault();
var categories = (from b in dc.dms_NewsCategories select b).ToList();
var model = new MyViewModel
{
SelectedCategoryId = dsn.NewsCategoriesID,
Categories = categories.Select(x => new SelectListItem
{
Value = x.NewsCategoriesID.ToString(),
Text = x.NewsCategoriesName
})
};
return View(model);
}
and finally in your view use the strongly typed DropDownListFor helper:
#model MyViewModel
#Html.DropDownListFor(
x => x.SelectedCategoryId,
Model.Categories
)
just in case someone comes with this question, this is how I do it, please forget about the repository object, I'm using the Repository Pattern, you can use your object context to retrieve the entities. And also don't pay attention to my entity names, my entity type Action has nothing to do with an MVC Action.
Controller:
ViewBag.ActionStatusId = new SelectList(repository.GetAll<ActionStatus>(), "ActionStatusId", "Name", myAction.ActionStatusId);
Pay attention that the last variable of the SelectList constructor is the selected value (object selectedValue)
Then this is my view to render it:
<div class="editor-label">
#Html.LabelFor(model => model.ActionStatusId, "ActionStatus")
</div>
<div class="editor-field">
#Html.DropDownList("ActionStatusId")
#Html.ValidationMessageFor(model => model.ActionStatusId)
</div>
I think it is pretty simple, I hope this helps! :)
I drilled down the formation of the drop down list instead of using #Html.DropDownList(). This is useful if you have to set the value of the dropdown list at runtime in razor instead of controller:
<select id="NewsCategoriesID" name="NewsCategoriesID">
#foreach (SelectListItem option in ViewBag.NewsCategoriesID)
{
<option value="#option.Value" #(option.Value == ViewBag.ValueToSet ? "selected='selected'" : "")>#option.Text</option>
}
</select>
Well its very simple in controller you have somthing like this:
-- Controller
ViewBag.Profile_Id = new SelectList(db.Profiles, "Id", "Name", model.Profile_Id);
--View (Option A)
#Html.DropDownList("Profile_Id")
--View (Option B) --> Send a null value to the list
#Html.DropDownList("Profile_Id", null, "-- Choose --", new { #class = "input-large" })
Replace below line with new updated working code:
#Html.DropDownList("NewsCategoriesID", (SelectList)ViewBag.NewsCategoriesID)
Now Implement new updated working code:
#Html.DropDownListFor(model => model.NewsCategoriesID, ViewBag.NewsCategoriesID as List<SelectListItem>, new {name = "NewsCategoriesID", id = "NewsCategoriesID" })
I want to put the correct answer in here, just in case others are having this problem like I was. If you hate the ViewBag, fine don't use it, but the real problem with the code in the question is that the same name is being used for both the model property and the selectlist as was pointed out by #RickAndMSFT
Simply changing the name of the DropDownList control should resolve the issue, like so:
#Html.DropDownList("NewsCategoriesSelection", (SelectList)ViewBag.NewsCategoriesID)
It doesn't really have anything to do with using the ViewBag or not using the ViewBag as you can have a name collision with the control regardless.
I prefer the lambda form of the DropDownList helper - see MVC 3 Layout Page, Razor Template, and DropdownList
If you want to use the SelectList, then I think this bug report might assist - http://aspnet.codeplex.com/workitem/4932
code bellow, get from, goes
Controller:
int DefaultId = 1;
ViewBag.Person = db.XXXX
.ToList()
.Select(x => new SelectListItem {
Value = x.Id.ToString(),
Text = x.Name,
Selected = (x.Id == DefaultId)
});
View:
#Html.DropDownList("Person")
Note:
ViewBag.Person and #Html.DropDownList("Person") name should be as in view model
To have the IT department selected, when the departments are loaded from tblDepartment table, use the following overloaded constructor of SelectList class. Notice that we are passing a value of 1 for selectedValue parameter.
ViewBag.Departments = new SelectList(db.Departments, "Id", "Name", "1");
For anyone that dont want to or dont make sense to use dropdownlistfor, here is how I did it in jQuery with .NET MVC set up.
Front end Javascript -> getting data from model:
var settings = #Html.Raw(Json.Encode(Model.GlobalSetting.NotificationFrequencySettings));
SelectNotificationSettings(settings);
function SelectNotificationSettings(settings) {
$.each(settings, function (i, value) {
$("#" + value.NotificationItemTypeId + " option[value=" + value.NotificationFrequencyTypeId + "]").prop("selected", true);
});
}
In razor html, you going to have few dropdownlist
#Html.DropDownList(NotificationItemTypeEnum.GenerateSubscriptionNotification.ToString,
notificationFrequencyOptions, optionLabel:=DbRes.T("Default", "CommonLabels"),
htmlAttributes:=New With {.class = "form-control notification-item-type", .id = Convert.ToInt32(NotificationItemTypeEnum.GenerateSubscriptionNotification)})
And when page load, you js function is going to set the selected option based on value that's stored in #model.
Cheers.

ASP.NET MVC2 validation not working with drop down list in IE <8

I have a form with a dropdownlist rendered using Html.DropDownListFor(...). The view model field that corresponds with the dropdown list has a [Required(...)] attribute attached to it. This works fine on my local machine, but as soon as I publish to our development server, the drop down lists keep displaying the required error message, even when a value is selected in the list. This only happens in IE - Firefox submits just fine.
Any thoughts?
Relevant code
View:
<ol class="form">
<li>
<%= Html.LabelFor(x => x.ContactTitle) %>
<%= Html.DropDownListFor(x=>x.ContactTitle, Model.GetTitleOptions()) %>
<%= Html.ValidationMessageFor(x => x.ContactTitle) %>
</li>
<!-- more fields... -->
</ol>
View Model:
[Required(ErrorMessage = "Title is required")]
[DisplayName("Title")]
public string ContactTitle { get; set; }
// ...
public SelectList GetTitleOptions()
{
return new SelectList(new string[]
{
"","Dr.", "Mr.", "Ms.", "Mrs.", "Miss"
});
}
It's all pretty basic stuff... I'm at a loss.
Edit: Just discovered this bug is limited to IE 8 compatibility view (and maybe prior versions). IE 8 in standards mode works as expected...
Chalk this one up to stupidity. The code in the example produces output similar to the following:
<select>
<option></option>
<option>Dr.</option>
<option>Mr.</option>
<option>Ms.</option>
<option>Mrs.</option>
<option>Miss</option>
</select>
And the relevant MVC validation function (when a RequiredAttribute is applied to a property that corresponds to a drop down list) is:
Sys.Mvc.RequiredValidator._validateSelectInput = function Sys_Mvc_RequiredValidator$_validateSelectInput(optionElements) {
/// <param name="optionElements" type="DOMElementCollection">
/// </param>
/// <returns type="Object"></returns>
for (var i = 0; i < optionElements.length; i++) {
var element = optionElements[i];
if (element.selected) {
if (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(element.value)) {
return true;
}
}
}
return false;
}
Notice the function checks element.value. In the case of the html above, the value attribute is empty because there is no value attribute on the option elements. Therefore, the validation function returns false and the error occurs. This only appears to happen in IE <8, presumably because other browsers by default assign an option element's text to the value attribute if none is specified.
The solution was to modify the way I was returning the select list items from which the drop down list was built like so:
public IEnumerable<SelectListItem> GetTitleOptions()
{
return BuildSelectListItems(new string[]
{
"","Dr.", "Mr.", "Ms.", "Mrs.", "Miss"
});
}
private List<SelectListItem> BuildSelectListItems(IEnumerable<string> values) {
return (from v in values
select new SelectListItem()
{
Text = v,
Value = v
}).ToList();
}
This results in the much more predictable HTML output:
<select>
<option value=""></option>
<option value="Dr.">Dr.</option>
<option value="Mr.">Mr.</option>
<option value="Ms.">Ms.</option>
<option value="Mrs.">Mrs.</option>
<option value="Miss">Miss</option>
</select>
which of course the function validates properly.

Resources