Getting some weird behavior on a dropdownlistfor() but it's using a list so perhaps i got something wrong
the code is simple enough
#for (int i = 0; i < Model.Phones.Count; i++ )
{
<tr>
<td>#Html.TextBoxFor(m => m.Phones[i].Num)</td>
<td>#Html.DropDownListFor(m => m.Phones[i].Typ, list1 )</td>
</tr>
}
where list1 is defined in the .cshtml itself as
string[] types = new string[] { "Office", "Mobile", "Home" };
List<SelectListItem> list1 = new List<SelectListItem>();
foreach(var t in types){
list1.Add(new SelectListItem{Text = t, Value = t });
the problem is the correct values are not being selected in the dropdown
whereas the dropdowns should be Mobile, Office, Home
The code is pretty vanilla, it's the standard html.DropdownListFor() helper so looks like it's not generating the correct selected attribute on the tag !!
what gives ?
The problem is that all your DropDownListFor end up using the same list of options. And the list of options itself contains the selected option. So when the list is updated with the selected option for one list, all the previous ones are updated too.
Use something like this instead:
#Html.DropDownListFor(m => m.Phones[i].Typ,
new SelectList(list1, "DataValueFieldName", "TextValueFieldName",
m => m.Phones[i].Typ)
This will generate a new unique list for each drop down list. Replace "DataValueFieldName" and "TextValueFieldName" with the names of the properties of the objects in list1 containing the value and text to display respectfully. If list1 is IEnumerable<SelectListItem>, then you can use "Value" and "Text".
Related
I'm tinkering with a ASP.NET MVC 4 template, and I need guidance on how to design a complex view layout.
I already created a model that is getting data returned by a stored procedure in a SQL Server DB. A view is getting data from the model in an IEnumerable<> object. If you were to view the raw output of the stored procedure it would look something like this:
**Name** **Objects**
John Orange
John Banana
John Apple
I used a view template to create a simple table based on logic below, but as expected, it is rendered exactly like it is stored in the IEnumerable<> object. The name appears on each row of output in the table, like above.
#model IEnumerable<projectname.Models.ObjectsModel>
<table class="table">
...
...
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Objects)
</td>
...
Instead, I want to create a view layout that is not in table form, that would display the Name as a heading, the objects as a bulleted list, and other attributes elsewhere, with no redundant data. My problem is that if I display the items in the IEnumerable<> using #foreach (var item in Model) I end up getting three duplicate entries, since the values in the Name field are not unique.
What is the 'best practice' way to do this? Can someone please point me in the right direction?
Not sure what your controller looks like, or even your view model, but maybe i can point you in the right direction.
foreach is just that a loop of objects and should be used for just that, i have seen many people manipulate foreach loops with little or no success, You should be using a #for loop. besides The for-loop is approximately 2 times faster than the foreach-loop as it only has to call get_Item for every element in the list.
there are a few ways to acomplis what you want, one way would be what #colinD stated above by using LINQ in the controler or viewmodel to pass the proper data. the other way would be somthing similar to the following.
var models = ObjectsModel();
var grouped = models.GroupBy(s => s.ObjectsModel.Name)
.Select(x => x.Select(y => y))
.ToList();
return View(grouped);
#for(int i = 0; i < Model.Count; i++)
{
<h2>#Html.DisplayFor(model => model[i].First().ObjectsModel.Name)</h2>
<ul>
for(int j = 0; j < Model[i].Count; j++)
{
<li>#Html.DisplayFor(model => model[i][j].Object)</li>
}
</ul>
}
I haven't tested the code but I hope this helps you get the right solution you are looking for.
The main thing i'm trying to figure out is how to display this data
without having the #foreach (var item in Model) loop generate
duplicate entries for each row in the SP output.
Usually processing data is done in action, like groupping data, and then make some loop to display. As you want to avoid #foreach I thought using linq. This is just an idea but keep in mind you should separate concerns in MVC. I hope this helps ;)
IEnumerable<ObjectsModel> model = new List<ObjectsModel>()
{
new ObjectsModel(){ Name = "John", Objects = "Orange" },
new ObjectsModel(){ Name = "John", Objects = "Banana" },
new ObjectsModel(){ Name = "John", Objects = "Apple" }
};
var htmlModel = model
.GroupBy(a => a.Name)
.Select(a => new
{
Name = a.Key,
GroupObjects = string.Join("", a.Select(b => $"<li>{b.Objects}</li>"))
})
.Select(a => $"<h1>{a.Name}</h1><ul>{a.GroupObjects}</ul>")
.ToList();
var result = string.Join("", htmlModel); // <h1>John</h1><ul><li>Orange</li><li>Banana</li><li>Apple</li></ul>
Final result:
<h1>John</h1>
<ul>
<li>Orange</li>
<li>Banana</li>
<li>Apple</li>
</ul>
I would like to send the selected dropdown option into model.Feit
Assuming, i need DropDownListFor, then select table, then the dropdown..?
//here's my drop down menu in my controller
List<SelectListItem> items = new List<SelectListItem>();
items.Add(new SelectListItem { Text = "Owner", Value = "0", Selected = true });
items.Add(new SelectListItem { Text = "Leader", Value = "1" });
ViewBag.items = items;
//this works fine, but the selected option has to be inserted into my database when submitted
//the first line shows the label
<div class="editor-label">#Html.LabelFor(model => model.Feit)</div>
//the second line needs to show as a dropdownlist with the 2 options above here
<div class="editor-field">#Html.DropDownListFor(model => model.Feit, "items")</div>
//when the option is selected, and submit is pressed this has to be sent to the db
// this works for all other fields, but my syntax is wrong at , "items"
Since you've already populated an IEnumerable<SelectListItem> in your viewbag, you just need to access it and cast it to ensure the correct DropDownListFor overload is used:
#Html.DropDownListFor(model => model.Feit, (IEnumerable<SelectListItem>)ViewBag.items)
model.Feit should be of type int or something that can hold the value of the selected item.
Use this
#Html.DropDownListFor(m => m.Feit, new SelectList(ViewBag.items, "Id","Name"),"Select")
I feel that the answer for this has to be out there in several places as variations of this question seem to get asked a lot. Unfortunately I cannot grasp exactly how to do what I am trying to achieve, so your help is greatly appreciated once again.
I have a list of usergroups, which are each assigned to a staff member. This is done by iterating through the list of usergroups and showing a dropdown of available staff members beside each one. It should be possible to not assign a staff member also, so a null value select option should be available.
When a new group is being created, having the null value as the default is fine, but where I am just updating an existing record, I want the dropdown to default to the option with the matching staff member ID.
So I query for available staff members:
var rtrnStaff = (from st in db.PrmTbl_Staffs
join sal in db.PrmTbl_Salutations on st.SalutationID equals sal.ID
where st.Active == true
select new { st.ID, Name = sal.Desc + ". " + st.Name });
To insert a blank value into this array:
List<SelectListItem> staff = new SelectList(rtrnStaff, "ID", "Name").ToList();
staff.Insert(0, (new SelectListItem { Text = "None", Value = "0" })); //can value be = null?
In my view, for the form to create a new user group, I can provide a dropdown like so:
#Html.DropDownList( "staffID", (IEnumerable<SelectListItem>)ViewData["StaffwNull"])
This provides a dropdown, with the "None" option first, which is fine. However, when I try the same thing for my update form, with the addition of a default value argument, it doesn't work:
#Html.DropDownList( "staffID", (IEnumerable<SelectListItem>)ViewData["StaffwNull"], item.StaffID)
The intention being that when placed within a foreach loop, the option matching the relevant staffID would show as default. Instead, "none" is still the first option.
I did try to just query the table in my controller, not build a selectlist there but pass the results directly via ViewData to the View, and then in the View do the following:
#Html.DropDownList("staffID", new SelectList(
(System.Collections.IEnumerable) ViewData["Staff"], "ID", "Name", item.StaffID),
new { Name = "staffID" })
That works no probs, but without a "none" option. Clearly I need some middle ground! Between DropDownList, DropDownListFor, List, SelectList, etc., I'm confused.
EDIT
(To show current state of code)
Controller:
var rtrnStaff = (from st in db.PrmTbl_Staffs
join sal in db.PrmTbl_Salutations on st.SalutationID equals sal.ID
where st.Active == true
select new { st.ID, Name = sal.Desc + ". " + st.Name });
List<SelectListItem> staff = new SelectList(rtrnStaff, "ID", "Name").ToList();
ViewData["StaffwNull"] = staff;
View:
//show dropdown of all staff,
//defaulting to "None" value (works)
#Html.DropDownList("staffID", (IEnumerable<SelectListItem>)ViewData["StaffwNull"], "None")
//show dropdown of all staff,
//defaulting to value matching item.staffID (doesn't work)
//default selection is first list item
//and list doesnt include "None" option
#foreach (var item in Model)
{
...
var thisStaffID = item.StaffID;
....
#Html.DropDownList( "staffID", (IEnumerable<SelectListItem>)ViewData["StaffwNull"], thisStaffID)
}
There is no overload where you can specify a selected value. MVC searches in the ViewBag object for an item called staffID and use that as selected value. You can use this overload of the DropDownList method which allows you to specify an option label:
#Html.DropDownList("staffID", (IEnumerable<SelectListItem>)ViewData["StaffwNull"], "None")
This renders an extra option to the select list at the top so you don't have to do this manually.
Side note: you should look into MVC model binding.
You can give an ID to the dropdown list (this overload) and then use jQuery to update it.
View
#Html.DropDownList("staffID", (IEnumerable<SelectListItem>)ViewData["StaffwNull"], new { id = "dropdown1" })
jQuery
<script>
$(document).ready(function () {
$('#dropdown1').val('theValueYouWantSelected');
});
</script>
I had to change on my lines of code around. I before had something like this
// this is in a static method.
List<string> mySTring = new List<string>();
mySTring.add("one");
mySTring.add("two");
However on one of my pages I have a dropdownlist that does not require the field "two" so instead of writing duplicate code all I did was
myString.remove("two");
Now I need to change my list to a List<SelectListItem> myList = new List<SelectListItem>();
So I have it now looking like this:
List<SelectListItem> myList = new List<SelectListItem>()
{
new SelectListItem() { Text = "one", Value = "one"},
new SelectListItem() { Text = "two", Value = "two"},
};
So now how do I remove the selectListItem that contains "two"? I know I probably could use remove by index. But I might add to list in the future so I don't want to start hunting down and changing it if the index changes.
Thanks
List<T> is, by default, going to be comparing object references (unless SelectListItem implements a custom equality method). So unless you still have a reference to the second item around, you are going to have to get it either by reference, or by finding the desired item:
var item = myList.First(x=>x.Value == "two");
myList.Remove(item);
Index may be easier...
You could use the RemovalAll method:
myList.RemoveAll(i => i.Text == "two");
Obviously this will get rid of all the items whose "Text" property is "two", but since you're using it in a ComboBox I'm assuming you'll only have one item for each "Text" value.
we can make mouse selection over the autocomplete div by adding this following code
into the jquery autocomplete function
here i have used the id name as Requiredid
$("#Requiredid").autocomplete({focus:function(e,ui) {
document.getElementById('Requiredid').value = ui.item.label;
document.getElementById('hdnValue').value = ui.item.id;//If You need this value you //can add this line
}});
I have something like the following in an ASP.NET MVC application:
IEnumerable<string> list = GetTheValues();
var selectList = new SelectList(list, "SelectedValue");
And even thought the selected value is defined, it is not being selected on the view. I have this feeling I'm missing something here, so if anyone can put me out my misery!
I know I can use an annoymous type to supply the key and value, but I would rather not add the additional code if I didn't have to.
EDIT: This problem has been fixed by ASP.NET MVC RTM.
If you're just trying to to map an IEnumerable<string> to SelectList you can do it inline like this:
new SelectList(MyIEnumerablesStrings.Select(x=>new KeyValuePair<string,string>(x,x)), "Key", "Value");
Try this instead:
IDictionary<string,string> list = GetTheValues();
var selectList = new SelectList(list, "Key", "Value", "SelectedValue");
SelectList (at least in Preview 5) is not clever enough to see that elements of IEnumerable are value type and so it should use the item for both value and text. Instead it sets the value of each item to "null" or something like that. That's why the selected value has no effect.
Take a look at this: ASP.NET MVC SelectList selectedValue Gotcha
This is as good explanation of what is going on as any.
Try this
ViewBag.Items = list.Select(x => new SelectListItem()
{
Text = x.ToString()
});