I have a drop down on my site that has a number of languages in it. When a user selects a different language, the page text reloads to that language. I would like the language drop down to change to reflect that by specifying the selected index of the selected language. However when ever I do this, the drop down in HTML does not change and always displays the first item in the list.
I have the following ViewModel :
public class BaseViewModel
{
public BaseViewModel()
{
}
public IEnumerable<SelectListItem> LanguageSelectListItems { get; set; }
}
The LanguageSelectListItems property is populated successfully when I construct a list of SelectListItems. When this is passed into the view, the selected language SelectListItem is set to true - in this case Swedish. All good so far.
But when the view calls Html.DropDownList() the selected languages' selected property changes to false. A couple of pictures might explain this better.
Before calling #Html.DropDownList("language", Model.LanguageSelectListItems)
Immediately after #Html.DropDownList("language", Model.LanguageSelectListItems)
You can see that the Selected property for the Swedish language has changed to false on the SelectListItem, and this is the resultant HTML :
<select name="language" id="language">
<option value="2">English</option>
<option value="3">Finnish</option>
<option value="4">Swedish</option>
</select>
Any ideas why this might be happening ?
Edit: LanguageSelectListItems is being populated and the selected language being set using this function (where languages is an enumerable of language) :
public static IEnumerable<SelectListItem> GetLanguageList(int selectedIndex)
{
var selectListItems = new List<SelectListItem>();
foreach (var language in languages)
{
selectListItems.Add(new SelectListItem() { Text = language.Name, Value = language.Id });
}
if (selectedIndex >= 0)
{
selectListItems[selectedIndex].Selected = true;
}
return selectedListItems;
}
Well if anyone else finds themselves in this strange situation, the solution to my problem was this.
In the controller action method before returning the view with the SelectList drop down, there was this line of code :
ViewBag.language = Model.Language;
This seemed to be interfering with the way the SelectList was being constructed in the view :
#Html.DropDownList("language", Model.LanguageSelectListItems)
After removing this line from the controller the SelectList started defaulting to the correct value. This fixed both Html.DropDownList and Html.DropDownListFor. I also discovered this in another area of the site where the behaviour was the same, but this time a ViewData[""] was being used.
So if you do run into this problem, check your local areas for any uses of ViewBag and ViewData that use the same name as the property you are trying to construct the drop down for.
Related
I have a list of a certain object, each object contains fields. I need to do a DropDownListFor or DropDownList for that field within that object but its not binding
I initially tried this with a List and this works fine for binding to a normal object field. I then tried converting to SelectList() by doing
#Html.DropDownListFor(model => Model.MyListOfObjects[i].FieldName, new SelectList(Model.MySelectList, "Value", "Text"))
I then tried to set the "Selected" property on the SelectListItem I needed to be selected but still no luck
for (var i = 0; i < Model.MyListOfObjects.Count; i++){
#Html.DropDownListFor(model => Model.MyListOfObjects[i].FieldName, Model.MySelectList)
}
Model.MySelectList is List<SelectListItem>
public List<MyCustomObject> MyListOfObjects {get;set;} //populated
public class MyCustomObject{
public string FieldName {get;set;}
}
So in the end, I need the value of Model.MyListOfObjects[i].FieldName tp be selected from the Model.MySelectList(). The value in the select list does match so I cant see whats wrong
Thank you. Appreciate any help
EDIT: I know I can use "EditorFor" templates for the object used in Model.MyListOfObjects but I would prefer to avoid it if possible as I am using that object with other things so if I need another version of the object in editorfor then I wouldnt be able to do it. As a last resort though, I will just have to change the object to a unique one and go down the editorfor route
selectListItem contains text and value.
You need to bind value field to dropdown not text field.
i.e. model => Model.MyListOfObjects[i].FieldValue
Check This will help you
I have a form where the user can add as many rows as needed. Each time they are expected to select a different option from the dropdown list provided. At the moment there is no validation to stop them selecting the same value multiple times.
Each row is a "ResourceCount".
The ViewModel has an IList of ResourceCountViewModel, so each row is being added as an item to the list.
The ResourceCount view model consists of an "id" to store the dropdown value selected and a "quantity" for the number field.
I can't think of a way to use the Compare annotation in this scenario.
How can I implement appropriate validation for this?
EDIT:
There is lots of good advice in the accepted answer. I went with a simple check in the controller post action:
if (resourceCounts.Count != resourceCounts.Where(d => d.Id!= 0)
.Select(x => x.Id)
.Distinct()
.Count())
{
ModelState.AddModelError(string.Empty, "You can't select the same option more than once");
//etc.
}
This is simply not possible using a ValidationAttribute (either in-built or custom ones) and MVC's unobtrusive client side validation.
Validation attributes are applied to properties of a model (your class), and the context in which validation is checked is for that instance only - it has no knowledge of other instances of the model within the collection, therefore it cannot compare other instances.
Conversely if you apply a ValidationAttribute to a collection (e.g. List<T> property), unobtrusive validation will not work because the data-val-* attributes necessary to add rules to the $.validator could only be generated if you include an input for the collection property (as opposed to each property of each object in the collection) which means ModelBinding would fail.
You will need to write your own controller code and scripts to achieve your custom validation.
On the client side, you could handle the .change() event of the <select> and check if any previous selections match, and if so display an error message. You have not shown your model, or the view, but based on the following html (repeated for each object in the collection
<select class="select" .....>
<option value="">Please select</option>
<option value="1">On-call</option>
....
<select>
<div class="error"></div> // see notes below if you using ValidationMessageFor()
then the script would be
var errors = $('.error');
var selects = $('.select');
$('.select').change(function() {
var selected = $.map(selects, function(s, i) {
return $(s).val();
})
$.each(selects, function(i, s) {
var error = $(this).next('.error');
var val = $(this).val();
var index = i;
if ($(this).val()) {
var others = selected.filter(function(s, i) {
if (i != index) {
return s;
}
});
if (others.indexOf(val) > -1) {
error.text('Please make a unique selection');
} else {
error.text('');
}
} else {
error.text('');
}
})
})
Refer this fiddle for a working example.
Alternatively you could hide/show (or disable) options in each <select> to prevent the user making invalid selections in the first place, but that becomes more complex if your dynamically adding/deleting items, and/or when your view is editing existing data where the property already has a selected value (I'll leave that to you to ask a new question showing your attempt if you want to implement that).
On the server side, you can check for duplicate values, and if so, add a ModelState error and return the view, for example
var selected = new List<int>();
for (int i = 0 i < model.Count; i++)
{
if (selected.Contains(model[i].YourProperty))
{
ModelState.AddModelError("", "Please make a unique selection");
break;
}
else
{
selected.Add(model[i].YourProperty);
}
}
if (!ModelState.IsValid)
{
return View(model);
}
....
or using linq
if (model.Select(x => x.YourProperty).GroupBy(x => x).Any(g => g.Count() > 1))
{
ModelState.AddModelError("", "Please make a unique selection");
}
which would then be displayed in the views #Html.ValidationSummary().
If your using #Html.ValidationMessageFor(m => m[i].YourProperty) in your view for each dropdownlist, then the above loop can be modified to
if (selected.Contains(model[i].YourProperty))
{
var propertyName = string.Format("[{0}].yourPropertyName", i);
ModelState.AddModelError(propertyName, "Please make a unique selection");
break;
}
and modify the script to add/remove the message for the <span> element generated by ValidationMessageFor() (i.e instead of the <div class="error"> element as shown above)
As a side note, if you want to learn more about how validation attributes in conjunction with client side validation work, I recommend reading The Complete Guide To Validation In ASP.NET MVC 3 - Part 2.
As I am new to MVC framework, I have been spending a couple of hours to generate a check box list in a View file. Finally, I could figure it out well. Here is my code:
#foreach (var item in Model.GetRoleNames)
{
#Html.CheckBox("chk_" + item.Value, new { value = item.Value })#item.Text<br />
}
But, I need to detect which of them is selected and if all the ckeckboxes are left unchecked, then preventing some operations.
Question: How can I get the checked items when I am within a controller action?
As the others said, you should use a Boolean value as the second parameter to CheckBox to indicate the checked status. A bit of string manipulation should help you get the ids of the selected check boxes..
First lets change the way the checkbox helper is used
<div id='myCheckboxDiv'>
#foreach (var item in Model.GetRoleNames)
{
#Html.CheckBox("chk_" + item.Value, false)#item.Text<br />
}
</div>
As you can see, I have set the second parameter to false and wrapped the mix in a div
And, when you want to get the ‘values’ associated with the selected checkboxes
var values = $(‘# myCheckboxDiv’).find('input:checkbox:checked').map(function () {
// get the name ..
var nameOfSelectedItem = this.attr('name');
// skip the ‘chk_’ part and give me the rest
return nameOfSelectedItem.substr(4);
}).get();
I am assuming item.Value to be a number. If its is not, please remove the white spaces using C#
Hey,
I Have three IQueryable lists which i concat together into one list to be displayed in the dropdown box. But now I want to get the id of what the user selected since there are 3 lists to choose from. Thanks
Example:
IQueryable<Store> stores= _storeRepository.FindAll().OrderBy(c => c.Description);
var storeList = stores.ToSelectList("StoreId", "Description", viewModel.StoreId.ToString());
IQueryable<Product> products = _productRepository.FindAll().OrderBy(j => j.Name);
var productList = products.ToSelectList("ProductId", "Name", viewModel.ProductId.ToString());
var viewList = storeList.Concat(productList).ToList();
viewModel.Lookups = viewList; //display in dropdown
if you question was i have 3 lists and the user choose one how can i know which one he chosen
Answer :
you give all three the same name and the value will change depend on which one the user have chosen
if your question was i want to send the id value with the selected list
Answer :
the server doesn't have any info about your html attributes
just the name attribute which maps to the action method parameter name and the value which maps to the parameter value , you can't know the id value and other attributes in the server
Example :
<input type="text" name="Title" id="SomeValue"/>
Will Map To :
public ActionResult Index(string Title)
the server will not recive id="SomeValue"
Solution :
What you can do is place a hidden field under every item with the value you want
instead of this way, after concatenating do this
viewdata["viewList"] = storeList.Concat(productList).ToList();
and view display items in dropdown like this
<%= Html.DropDownList("viewlist_id", (List<SelectListItem>)ViewData["viewlist"])%>
now use a submit button (if want to post the data to same action else use actionlink with routvalues if redirecting to different action,I am using here submit button)
<input type="submit" name="submit" value="submit" />
and in your action you can retrieve the posted data
var dropdown_id = Request.Form["viewlist_id"];
this way you will get the id of selected drop down. thanks
So i figured out how to have the values of different lists combined into one dropdown list will still being able to access its actual value or ID without concat or union- the code is a lot if anyone is interested i will go ahead and take the time to properly post it. Other than that, thank you everyone for offer your advise and help. Thanks
So this is how i went about my problem. In my Controller File - in my Get method after the button click this is what i did:
resultSummaryViewModel.Value = value;
resultSummaryViewModel.ReportFrame = new FramedViewModel();
if(value !="")
{
string viewValue = value.Substring(0, value.IndexOf("|"));
string viewType = value.Substring(value.IndexOf("|") + 1);
resultSummaryViewModel.ReportFrame.SourceURL =
WebPathHelper.MapUrlFromRoot(
string.Format(
"Reporting/ResultSummary.aspx?beginDate={0}&endDate={1}&Id={2}&viewType={3}",
resultSummaryViewModel.BeginDate, resultSummaryViewModel.EndDate, viewValue,
viewType));
}
var viewData = new Dictionary<string, string>();
viewData.Add("Schools", "|allschools");
viewData.Add("Classes", "|allclasses");
This is also connected to my display page aspx.cs which contains the actual lists i use to populate.
I have the following code which is meant to populate a dropdown with a bunch of integer values and make the currently selected value (in this case, 13) be the selected item.
I've used the same technique but for string values and it works great, remembering each time when I get to the view what the current value is.
In controller:
var x = new[] { 1, 2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
ViewData["Field"] = new SelectList(x, 13);
In view:
<%=Html.DropDownList("Field", (IEnumerable<SelectListItem>)ViewData["Field"])%>
When I debug and watch the ViewData["Field"] object, it does have a selectedValue of 13 and so it must be reaching the View but getting ignored there as all I see on the page is a dropdown with the values 1 to 15, but with 1 showing (none selected so shows the first one)
Is this a bug or am I doing something really stupid?
Thanks
Graeme
I seem to recall that it doesn't actually use the Selected property of the SelectList element. I usually have one ViewData item be the select list and another be the selected value.
Controller:
var x = new[] { 1, 2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
ViewData["Fields"] = new SelectList(x);
ViewData["Field"] = 13;
View
<%= Html.DropDownList("Field", (IEnumerable<SelectListItem>)ViewData["Fields"] ) %>
This was happening to me! I was pulling my hair out for hours, but I eventually figured it out. In my case I was creating a drop-down list like this:
<%= Html.DropDownList("bookId", Model.ProductMenu, new { onchange = "goToBook();" })%>
And it was not printing the selected option. But the dropdown right next to it was working fine:
<%= Html.DropDownList("segmentIndex", Model.SegmentMenu, new { onchange = "goToSegment();" })%>
They were being generated the exact same way in the controller, and the debugger would always show the properly selected value as the view was returned. So what the heck?
The difference was in the view itself. "bookId" in my app happens to be a route/querystring value and segmentIndex is not. Simply changing the name "bookId" in the view to "bookIdBLAH" fixed it!