Drop down list not populating from List MVC - c#

I am confused on an issue with the DropDownListFor helper.
I am trying to assign a list of values to the drop down as follows:
Model
public Int32 TestID {get;set;}
public List<Int32> ListOfID {get;set;}
public IEnumerable<SelectListItem> TimeDD {get;set;}
Controller
public ActionResult Manage(int id){
MyModel model = new MyModel();
model.TimeDD = DropDownManager.TimeDD;
model.TestID = 12;
model.ListOfID = new List<Int32>{ 1,2,3,4,5,6,7};
return View(model);
}
In the view I have the following:
#for(int i = 0; i< ListoFID.Count; i++){
<div>#Html.DropDownListFor(m=> m.TestID, Model.TimeDD)</div>
<div>#Html.DropDownListFor(m=> m.ListoFID[i], Model.TimeDD)</div>
}
The problem I am having is that the TestID drop down is working correctly however the ListoFID[i] is not selecting the values from the drop down. The TimeDD is a list of times as follows:
/// <summary>
/// The Time dropdown
/// </summary>
/// <param name="userID"></param>
/// <returns></returns>
public static IEnumerable<SelectListItem> TimeDD()
{
// new ctl
TimeControl ctl = new TimeControl();
// get drop down
IEnumerable<SelectListItem> result = ctl.Select().Select(m => new SelectListItem { Text = m.Time1, Value = m.TimeID.ToString() }).ToList();
// clean up
ctl.Dispose();
return result;
}
Where Time1 is '09:30' and TimeID is 1 - 48. I cannot figure out how this is happening!! As soon as I reference an object it fails to select the drop down at the correct point.
Edit
Also I have a property in my model called OpeningTimes - this is the list of opening times saved against a company as below:
ComapnyID DayID StartTime EndTime
1 1 19 32 -- e.g. Monday 09:00 - 17:30
When I loop through the Opening times:
#for(int i=0; i< Model.OpeningTimes.Count; i++)
{
<tr>
<td>
#Html.DropDownListFor(m => m.OpeningTimes[i].StartTime, Model.TimeDD)
</td>
<td>
#Html.DropDownListFor(m => m.OpeningTimes[i].EndTime, Model.TimeDD)
</td>
</tr>
}
The drop downs are still not being selected. I can confirm the StartTime and EndTime do have values and are property int

DropDownListFor requires a property to map to, not a value. So
#Html.DropDownListFor(m=> m.ListoFID[i], Model.TimeDD) /* the [i] kills it */
will not work.

instead of doing a list of int build it like the other
List<SelectListItem> ls = new List<SelectListItem>();
foreach(var temp in Model.ListOfId){
ls.Add(new SelectListItem() { Text = temp, Value = temp});
}

Related

Loop through Request.Forms to get Field Values for Bulk Update

I have a list of records generated from a search query in my View. There's certain fields that can be edited and next step is do update those fields with one button/action.
The yellow fields are the ones that have been edited, while the white fields still match what is in the database table. Now when I click update all I first get the values of sellprice and casecost from the DB, then I get the values from the form. If the values match then move on, if the values from the form have been changed then update. I have datareader that reads the values from the table/database perfectly fine for each line of records on page.
NpgsqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
var prod = new ProductViewModel();
prod.q_guid = Guid.Parse(dr["q_guid"].ToString());
prod.q_sellprice = Convert.ToDecimal(dr["q_sellprice"]);
prod.q_casecost = Convert.ToDecimal(dr["q_casecost"]);
/*
At this point
Need to compare dr.Read q_sellprice and q_casecost
with changed values in the fields
if != then update for that record
*/
/*Lets assign previous values (values in db) to variables*/
var previousSellprice = prod.q_sellprice;
var previousCasecost = prod.q_casecost;
var thatId = prod.q_guid;
/*Lets get current values from form/list*/
var priceList = Request.Form["item.q_sellprice"];
var costList = Request.Form["item.q_casecost"];
/*eg*/
if (previousSellprice != currentSellprice || previousCasecost != currentCasecost)
{
//lets update record with new values from view
}
-> loop move on to next row in view
My DataReader while loop can get the value of each row no problemo. What I am trying to achieve when it gets the values of the first row from the db, then
I need to check what the current values in the form for that record are
if they are different then update for that current row
move on to next row in the view/on page
I have managed to get the array of values for these fields with these variables with the following code. This has the edited/changed fields from the list/form.
var priceList = Request.Form["item.q_sellprice"];
var costList = Request.Form["item.q_casecost"];
On my first run through the loop, I would like to get the values 10.00 and 8.50, do my check, update if necessary.. then move on the next row which will get 3.33 and 8.88, do my check, and update if necessary and so on for the rest of the records on that page.
So how can I loop through Request.Forms in the instance, and get my individual values for one record at a time?
cshtml on view for the fields is
#foreach (var item in Model)
{
<td>
€ #Html.EditorFor(modelItem => item.q_sellprice, new { name="q_sellprice" })
</td>
<td>
€ #Html.EditorFor(modelItem => item.q_casecost, new { name="q_casecost"})
</td>
Addition: Updating isnt the issue, getting the values of each record from the array while looping through the form fields is.
It is a long description of the problem - but from my understanding, your only problem is, that you want to have some data, which right now is two strings to be as List of operations (data) to perform? Is that correct?
If so - you can have such data in List using Zip method:
void Main()
{
string priceList = "1,2,3,4";
string costList = "2,3,4,5";
var prices = priceList.Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries);
var costs = costList.Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries);
var collectionToUpdate = prices.Zip(costs, (price, cost) => new PriceToUpdate(price, cost));
//do update in database with collectionToUpdate
}
public class PriceToUpdate
{
public PriceToUpdate(string oldPrice, string newPrice)
{
decimal priceTmp;
if (decimal.TryParse(oldPrice, out priceTmp))
{
OldPrice = priceTmp;
}
if (decimal.TryParse(newPrice, out priceTmp))
{
NewPrice = priceTmp;
}
}
public decimal OldPrice { get; set; }
public decimal NewPrice { get; set; }
}
My suggestion would be to re-organise your HTML a bit more and modify the method for getting the fields parsed out. What I have done in the past is include the Key Id (in your case the Guid) as part of the output. So the result in a basic view looks like:
If you notice the name attribute (and Id) are prefixed with the q_guid field. Here is my basic model.
public class ProductViewModelItems
{
public IList<ProductViewModel> items { get; set; } = new List<ProductViewModel>();
}
public class ProductViewModel
{
public Guid q_guid { get; set; }
public decimal q_sellprice { get; set; }
public decimal q_casecost { get; set; }
//other properties
}
And my controller just has a simple static model. Of course yours is built from your database.
static ProductViewModelItems viewModel = new ProductViewModelItems()
{
items = new[]
{
new ProductViewModel { q_casecost = 8.50M, q_sellprice = 10M, q_guid = Guid.NewGuid() },
new ProductViewModel { q_casecost = 8.88M, q_sellprice = 3.33M, q_guid = Guid.NewGuid() },
new ProductViewModel { q_casecost = 9.60M, q_sellprice = 3.00M, q_guid = Guid.NewGuid() },
new ProductViewModel { q_casecost = 9.00M, q_sellprice = 5.00M, q_guid = Guid.NewGuid() },
new ProductViewModel { q_casecost = 10M, q_sellprice = 2.99M, q_guid = Guid.NewGuid() },
}
};
[HttpGet]
public ActionResult Index()
{
//load your view model from database (note mine is just static)
return View(viewModel);
}
Now we construct our form so that we can pull it back in our post method. So I have chosen the format of {q_guid}_{field_name} as
q_casecost = {q_guid}_q_casecost
q_sellprice = {q_guid}_q_sellprice
The form construction now looks like.
#foreach (var item in Model.items)
{
<tr>
<td>
€ #Html.TextBoxFor(modelItem => item.q_sellprice, new { Name = string.Format("{0}_q_sellprice", item.q_guid), id = string.Format("{0}_q_sellprice", item.q_guid) })
</td>
<td>
€ #Html.TextBoxFor(modelItem => item.q_casecost, new { Name = string.Format("{0}_q_casecost", item.q_guid), id = string.Format("{0}_q_casecost", item.q_guid) })
</td>
</tr>
}
Note there a few key items here. First off you cant modify the Name attribute using EditorFor() so I have swapped this out to a TextBoxFor() method.
Next I am overriding the Name attribute (note it must be Name not name [lower case ignored]).
Finally the POST action runs much differently.
[HttpPost]
public ActionResult Index(FormCollection form)
{
IList<ProductViewModel> updateItems = new List<ProductViewModel>();
// form key formats
// q_casecost = {q_guid}_q_casecost
// q_sellprice = {q_guid}_q_sellprice
//load your view model from database (note mine is just static)
foreach(var item in viewModel.items)
{
var caseCostStr = form.Get(string.Format("{0}_q_casecost", item.q_guid)) ?? "";
var sellPriceStr = form.Get(string.Format("{0}_q_sellprice", item.q_guid)) ?? "";
decimal caseCost = decimal.Zero,
sellPrice = decimal.Zero;
bool hasChanges = false;
if (decimal.TryParse(caseCostStr, out caseCost) && caseCost != item.q_casecost)
{
item.q_casecost = caseCost;
hasChanges = true;
}
if(decimal.TryParse(sellPriceStr, out sellPrice) && sellPrice != item.q_sellprice)
{
item.q_sellprice = sellPrice;
hasChanges = true;
}
if (hasChanges)
updateItems.Add(item);
}
//now updateItems contains only the items that have changes.
return View();
}
So there is alot going on in here but if we break it down its quite simple. First off the Action is accepting a FormCollection object which is the raw form as a NameValuePairCollection which will contain all the keys\values of the form.
public ActionResult Index(FormCollection form)
The next step is to load your view model from your database as you have done before. The order you are loading is not important as we will interate it again. (Note i am just using the static one as before).
Then we iterate over each item in the viewmodel you loaded and now are parsing the form values out of the FormCollection.
var caseCostStr = form.Get(string.Format("{0}_q_casecost", item.q_guid)) ?? "";
var sellPriceStr = form.Get(string.Format("{0}_q_sellprice", item.q_guid)) ?? "";
This will capture the value from the form based on the q_guid again looking back at the formats we used before.
Next you parse the string values to a decimal and compare them to the original values. If either value (q_sellprice or q_casecost) are different we flag as changed and add them to the updateItems collection.
Finally our updateItems variable now contains all the elements that have a change and you can commit those back to your database.
I hope this helps.

ASP.net MVC 5 Razor dropdown box

Hey all I am new to Razor MVC and wanted to make a select box that has the past 10 years listed inside it (2016, 2015, 2014, etc....).
This is my current Controllers code:
public ActionResult loadPast10Years()
{
List<int> last10Years = new List<int>();
int currentYear = DateTime.Now.Year;
for (int i = currentYear - 10; i < currentYear; i++)
{
last10Years.Add(i);
}
ViewBag["last10Years"] = last10Years;
return View();
}
And my Razor code:
#Html.DropDownList("last10Years", (SelectList)ViewBag["last10Years"], "--Select One--")
But I have an error when loading the page that says:
InvalidOperationException: There is no ViewData item of type 'IEnumerable' that has the key 'last10Years'.
So... What am I missing?
This is how I would do it in the view
#Html.DropDownList("Last Ten Years", (IEnumerable<SelectListItem>)ViewBag.LastTenYears, "Select A Year")
and in your Action
List<int> last10Years = new List<int>();
int currentYear = DateTime.Now.Year;
for (int i = currentYear - 10; i < currentYear; i++)
{
last10Years.Add(i);
}
ViewBag.LastTenYears = new SelectList(last10Years);
You can see a demo here
Following from your comment please find below my updated answer.
I would first create a Model class which we will be using in our view. In this model class you can have your appropriate properties. For now we're only going to be using SelectlistItem
so our class will look like
public class ViewModel
{
public IEnumerable<SelectListItem> LastTenYears { get; set; }
}
Then in our controller we can create a method which will provide us the information for our drop down.
public IEnumerable<SelectListItem> GetLastTenYears()
{
List<SelectListItem> ddl = new List<SelectListItem>();
int currentYear = DateTime.Now.Year;
for (int i = currentYear - 10; i < currentYear; i++)
{
ddl.Add(new SelectListItem { Text = i.ToString(), Value = i.ToString() });
}
IEnumerable<SelectListItem> lastTenYears = ddl;
return lastTenYears;
}
Now we want to pass this data to the view. For argument sake I will use Index as a view but you can pass it to whatever view you like. So we will change our Index action to
public ActionResult Index()
{
ViewModel viewModel = new ViewModel();
viewModel.LastTenYears = GetLastTenYears(); //get the drop down list
return View(viewModel); //we're passing our Model to the view
}
Finally, we want to make sure our view knows which Model to use so we will do the following the beging of the Index.cshtml file
#model YourNameSpace.ViewModel
and our DropDownList helper method will now change to point to the property in our Model class as
#Html.DropDownList("Last Ten Years", Model.LastTenYears, "Please select a year")
There are few issue in your code right now.
Don't name the ViewBag key same as the DropDownList name, they don't
work well together, you can change the ViewBag Key name to something like last10YearsOptionsinstead oflast10Years`, so that it is different than control name.
You have not create SelectList in controller before adding to ViewBag, but you are casting it to SelectList in View.
In ViewBag values are stored like ViewBag.SomeKey= "SomeValue", but you are doing it wrong way.
After Fixing the above problems your code will look like :
ViewBag.last10YearsOptions = last10Years.Select(x => new SelectListItem()
{
Text = x.ToString(),
Value = x.ToString()
}
);
and then in View:
#Html.DropDownList("last10Years",
"--Select One--",
ViewBag.last10YearsOptions as IEnumerable<SelectListItem>,
null )
ViewBag is not a dictionary with key value pairs.
also DropDownList works with ICollection ithink
so this code works form
#Html.DropDownList("last10Years", new SelectList(ViewBag.last10YearsOptions), "Select one from here")
Controller is here;
public ActionResult loadPast10Years()
{
List<int> last10Years = new List<int>();
int currentYear = DateTime.Now.Year;
for (int i = currentYear - 10; i < currentYear; i++)
{
last10Years.Add(i);
}
ViewBag.last10Years = new SelectList(last10Years);
return View();
}
And View
#Html.DropDownList("last10Years", "--Select One--")

Invalid Operation Exception passing to View

I am trying, to no avail to display a dropdown list of all units a user doesnt already have. So i have List A with all Units and List B with all Units the user has. What i want is List C which is basically List A with List B removed from it. I have so far managed to filter out the data but i cant seem to display it in my View. All i get is a blank dropdown list. Can anyone see where im going wrong??
public ActionResult AddUnit(String usrCode)
{
var units = unitsClient.GetAllunits();
var allunitsCode = (from s in units select s.unitCode).ToList();
var thisUnitCode = (from s in db.Units
where s.UsrCode == usrCode
select s.UnitCode).ToList();
var notGot = allunitsCode.Except(thisUnitCode);
List<unitsummaryDTO> list = UnitList(units, notGot);
ViewBag.unitCode = new SelectList(list, "unitCode", "unitTitle");
var model = new UserUnit { UsrCode = usrCode };
return View("AddUnit", model);
}
private List<unitsummaryDTO> UnitList(unitsService.unitsDTO[] units, IEnumerable<string> notGot)
{
var allunits = unitsClient.GetAllunits();
var allunitsCode = (from s in allunits select s.unitCode).ToList();
IEnumerable<String> list1 = allunitsCode;
IEnumerable<String> list2 = notGot;
var listFinal = list1.Union(list2).toList;
return listFinal.Select(x => new unitsummaryDTO(){unitCode = x}).ToList();
}
This is my View model. But all i get is a blank drop down?? Can anyone help me out.
#model Projv1.UserUnit
#Html.HiddenFor(model => model.unitCode)
#Html.DropDownList("UnitCode")
It would be blank because #Html.DropDownList("UnitCode") doesn't have a source. If you look at MSDN for Html.DropDownList, the one your most likely trying to use is DropDownList(String, IEnumerable<SelectListItem>).
Your putting your select list into the ViewBag as unitCode so try:
#Html.DropDownList("Unit Code", ViewBag.unitCode);
A much easier way of handling this is to extend UserUnit as a ViewModel (or create something) to have the items needed by the SelectList on it and let MVC do the heavy lifting in the binding.
public class UserUnit
{
// ... other properties
IEnumerable<unitsummaryDTO> UnitCodes { get; set; }
public string MyUnitCode { get; set; }
}
Then
#Html.DropDownListFor(n => n.MyUnitCode,
new SelectList(Model.UnitCodes, "unitCode", "unitTitle"))

"Cannot convert type 'ViewModels.DropDownVM' to 'System.Web.Mvc.SelectList'" when Populating DropDown with MySQL Query

I have loosely followed: specify name and id to Drop down razor technique asp dot net mvc4
What my desired end result is is that I have a view which has two dropdowns which are populated with values from the 'ww' column in a DB. Once a user selects a start and end and clicks a submit button, it would then pass the selected values to another ActionMethod that then takes those values and performs a similar query and directs to a different view with a different set of controls.
However, I'm getting a
"Cannot convert type 'ViewModels.DropDownVM' to
'System.Web.Mvc.SelectList'"
error at the following line in my code and haven't been able to figure out how to fix the issue so that everything runs smoothly.
#Html.DropDownList("WWStart", (SelectList)ViewBag.DDLWWStart, " -- Select Starting Work Week -- ")
I have the following:
ViewModels/DropDownVM.cs snippet:
public class DropDownVM
{
public int SelectedCategory { get; set; }
//public string CategoryName { get; set; }
public SelectList Categories { get; set; }
}
IndicatorController Index():
public ActionResult Index()
{
using (var context = new taskDBContext())
{
var DDLWWQuery = (from wq in context.taskSet
select new
{
wq.ww
}).Distinct().OrderByDescending(x => x.ww);
// The controls have a Work Week Range so we need both a start and end control
var DDLWWStartVM = new DropDownVM();
DDLWWStartVM.Categories = new SelectList(DDLWWQuery, "ww", "ww");
ViewBag.DDLWWStart = DDLWWStartVM;
//ViewBag.DDLWWStart = new SelectList(DDLWWQuery.AsEnumerable(), "ww", "ww", "-- Select Starting WW --");
//ViewBag.DDLWWEnd = new SelectList(DDLWWQuery.AsEnumerable(), "ww", "ww", "-- Select Ending WW --");
var DDLWWEndVM = new DropDownVM();
DDLWWEndVM.Categories = new SelectList(DDLWWQuery, "ww", "ww");
ViewBag.DDLWWEnd = DDLWWEndVM;
}
return View();
}
Index View Snippet:
#model IEnumerable<TaskTracker.ViewModels.DropDownVM>
#{
ViewBag.Title = "Create Indicator: Step One of Two";
}
<h2>Indicators</h2>
<p>This is currently being refined.</p>
<div>Create Indicator: Step One of Two - Select Work Week Range</div>
using (Html.BeginForm("StepTwo", "Indicator", new { wwStartSelect = WWStart.Categories.Text, wwEndSelect = WWEnd.Categories.Text }))
{
<div>Select Starting Work Week</div>
#Html.DropDownList("WWStart", (SelectList)ViewBag.DDLWWStart, " -- Select Starting Work Week -- ")
<div>Select Ending Work Week</div>
#Html.DropDownList("WWEnd", (SelectList)ViewBag.DDLWWEnd, " -- Select Ending Work Week -- ")
#Html.AntiForgeryToken()
<input type="submit" title="Next Step" value="Next Step" />
}
How can I resolve this error by fixing my code to get the desired result?
You are not casting the right object. You should cast it to the Categories Property ... see below
#Html.DropDownList("WWStart", (SelectList)ViewBag.DDLWWStart.Categories, " -- Select Starting Work Week -- ")
#Html.DropDownList("WWEnd", (SelectList)ViewBag.DDLWWEnd.Categories, " -- Select Ending Work Week -- ")

Populating Dropdown using Reflection

I am using MVC4 and what I want to do is use a dropdown connected to my search box to search for the selected property. How would I am stuck on the Text= prop.Name. How could I go through and access all of the properties using this.
My Controller
public ActionResult SearchIndex(string searchString)
{
var selectListItems = new List<SelectListItem>();
var first = db.BloodStored.First();
foreach(var item in first.GetType().GetProperties())
{
selectListItems.Add(new SelectListItem(){ Text = item.Name, Value = selectListItems.Count.ToString()});
}
IEnumerable<SelectListItem> enumSelectList = selectListItems;
ViewBag.SearchFields = enumSelectList;
var bloodSearch = from m in db.BloodStored
select m;
if (!String.IsNullOrEmpty(searchString))
{
bloodSearch = bloodSearch.Where(s => string.Compare(GetValue(s, propertyName), searchString) == 0);
}
return View(bloodSearch);
}
The selectlist is working now I just need to go over my searchstring and how to pass two parameters now.
I'm not quite sure what you're asking. If you want to create a list of objects with the property Text set to the property name of the object, you could get the first object in the BloodStored enumerable and create a list of anonymous types:
// Get one instance and then iterate all the properties
var selectListItems = new List<object>();
var first = db.BloodStore.First();
foreach(var item in first.GetType().GetProperties()){
selectListItems.Add(new (){ Text = item.Name});
}
ViewBag.SearchFields = selectListItems;

Categories

Resources