Drop Down List Value not passing to controller - asp.net-mvc-4

This question should be very simple.. I am trying to pass values in my drop down list in my view to the controller.. I'm not getting errors but it's sending a null value for that property. Please help..
My code is as follows:
Controller:
public ActionResult Create()
{
var list = new []
{
new Room{ RoomID = 1, Building = "FAYARD HALL"},
new Room{ RoomID = 2, Building = "WHATEVER HALL"},
new Room{ RoomID = 3, Building = "TIME SQUARE"},
new Room{ RoomID = 4, Building = "MISSISSIPPI"},
new Room{ RoomID = 5, Building = "NEW YORK"},
};
var selectList = new SelectList(list,"RoomID", "Building");
ViewData["BuildingList"] = selectList;
return View();
}
//
// POST: /Room/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Room room)
{
if (ModelState.IsValid)
{
db.Rooms.Add(room);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(room);
}
MY VIEW:
<div>
#Html.LabelFor(model => model.Building, "Building")
</div>
<div>
#Html.DropDownList("BuildingList", String.Empty)
#Html.ValidationMessageFor(model => model.Building)
</div>
Please help...
Thank you.

Is your drop down populated? Given your code I think you need the following to do so:
#Html.DropDownListFor(model => model.Building, ViewData["BuildingList"])
ie. bind the selected value to the Building property of your Room and use the drop down list from your view model to populate the list.
I'm also not sure this is what your intention is. It seems a bit fishy that you are populating a drop down list with rooms and then based on the selection you are creating a new room.
Edit
Ok I'm going to make things a lot easier for you.
I'll start with your classes. Here is the room I am assuming you're working with:
public class Room
{
public int RoomId { get; set; }
public string Building { get; set; }
}
Now let's do something a bit better than using ViewData. I've created a view model for you. You will populate this with your select list and the item you choose in the view will be bound into the SelectedRoomId when you post the form.
public class ViewModel
{
public int SelectedRoomId { get; set; }
public SelectList RoomOptions { get; set; }
}
Controller
private SelectList GetSelectList()
{
var list = new[]
{
new Room { RoomId = 1, Building = "FAYARD HALL"},
new Room { RoomId = 2, Building = "WHATEVER HALL"},
new Room { RoomId = 3, Building = "TIME SQUARE"},
new Room { RoomId = 4, Building = "MISSISSIPPI"},
new Room { RoomId = 5, Building = "NEW YORK"}
};
return new SelectList(list, "RoomId", "Building");
}
public ActionResult Create()
{
ViewModel viewModel = new ViewModel
{
RoomOptions = GetSelectList()
};
return View(viewModel);
}
[HttpPost]
public ActionResult Create(ViewModel viewModel)
{
if (ModelState.IsValid)
{
// Save here
// create a new room using the SelectedOptionId in the viewModel
return RedirectToAction("Index", "Home");
}
// repopulate the list if something failed
viewModel.RoomOptions = GetSelectList();
return View(viewModel);
}
View
#model PathToYourViewModel.ViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(model => model.SelectedRoomId, Model.RoomOptions, "-- select an option --")
<button type="submit">Submit</button>
};
Tried and tested. Good luck!

The model binding takes place with help of the names property in mvc .
In your case the name of your control is BuildingList:
#Html.DropDownList("BuildingList", (SelectList)ViewData["BuildingList"])
Therefore at your controller Action will go as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(FormCollection collection)
{
var selectedValue = collection["BuildingList"];
}

Related

MVC Razor - How to update a multiple rows in a selected column from a dropdown list SQL

I think the title says it all...
I want to update a whole table with an input filled value into a selected column (selected from a dropdown list) if the value of another column is equal to a selected value (from another dropdown list). Putting all this together seem pretty hard for me...
Like this kind of Query...
var db = Database.Open("DatabaseX");
var updateCommand = "UPDATE TableX Set SelectedColumn(dropdownlistA) = (InputA) IF ColumnX = Selected Values(dropdownlistB)";
Sorry for my english
If you are having trouble getting the selected values from the dropdown on form submit, you may try this.
Create a view model with properties for the data for the dropdown and the selected option value.
public class CreateUserVm
{
public int SelectedCam { set; get; }
public List<SelectListItem> Cams { set; get; }
public int SelectedCat { set; get; }
public List<SelectListItem> Categories { set; get; }
}
And in your GET action, create an object of this, initialize the collection properties and send tot he view.
public ActionResult Create()
{
var v = new CreateUserVm
{
Categories = new List<SelectListItem>
{
new SelectListItem {Value = "1", Text = "Photo"},
new SelectListItem {Value = "2", Text = "Video"}
},
Cams = new List<SelectListItem>
{
new SelectListItem {Value = "25", Text = "DSLR"},
new SelectListItem {Value = "28", Text = "Point and Shoot"}
}
};
return View(v);
}
Now your create view should be strongly typed to this CreateUserVm class.
#model CreateUserVm
#using(Html.BeginForm())
{
#Html.DropDownListFor(g=>g.SelectedCat, Model.Categories)
#Html.DropDownListFor(g=>g.SelectedCam, Model.Cams)
<input type="submit" />
}
And in your HttpPost action method, we will use the CreateUserVm object as the parameter so that the posted model will be mapped to it by the default model binder.
[HttpPost]
public ActionResult Create(CreateUserVm model)
{
var camId = model.SelectedCam;
var catId = model.SelectedCat;
// use camId and catId to update your db.
// to do : Save and Redirect
}

dropdownlist selection returning null # mvc4

I am trying to insert to database from view page which has dropdownlist , textbox's .. when i enter something and click on save means i am getting nothing from dropdown selection which is binded .
My code :
#model IEnumerable<iWiseapp.Models.LObmodel>
#using (#Html.BeginForm("stop", "Home", FormMethod.Post))
{
#Html.DropDownList("Data",ViewBag.Data as SelectList,"select a sdsd",new {id="LOB_ID"})
#Html.DropDownListFor("sss",new SelectList(Model,"lob_id","lob_name"))
,
#Html.DropDownList("LObmodel", new SelectList(ViewBag.data ,"Value","Text"))
#Html.DropDownListFor(model => model.lob_name, new SelectList(ViewBag.Titles,"Value","Text"))
I tried above all possibilities but nah i am confused nothing working out
ADDED MY CONTROLER CODE
[HttpGet]
public ActionResult stop()
{
ServiceReference1.Service1Client ser_obj = new ServiceReference1.Service1Client();
IEnumerable<LobList> obj = ser_obj.GetData(); //i am Getting list of data through WCF from BUSINESS LAYER WHERE i created entities via EF
List<SelectListItem> ls = new List<SelectListItem>();
foreach (var temp in obj)
{
ls.Add(new SelectListItem() { Text = temp.LOB_NAME, Value = temp.LOB_ID.ToString() });
}
//then create a view model on your controller and pass this value to it
ViewModel vm = new ViewModel();
vm.DropDown = ls; // where vm.DropDown = List<SelectListItem>();
THE COMMENTED CODE BELOW IS WHAT I AM DOING
//var mode_obj = new List<LObmodel>();
//Created LOBmodel class in model which is excat same of entities in Business class
//var jobList = new List<SelectListItem>();
//foreach (var job in obj)
//{
// var item = new SelectListItem();
// item.Value = job.LOB_ID.ToString(); //the property you want to display i.e. Title
// item.Text = job.LOB_NAME;
// jobList.Add(item);
//}
//ViewBag.Data = jobList;
return View(jobList); or return view (obj)
}
Any expert advice is appreciated
MY FIELDS , IS THESE PERFECT
public class template
{
public List<LobList> LOBs { get; set; } //LOBLIST FROM Entities in business layer
public int selectedLobId { get; set; }
public LobList SelectedLob
{
get { return LOBs.Single(u=>u.LOB_ID == selectedLobId) ;}
}
}
AND
public class LObmodel
{
public int LOB_ID { get; set; }
public string LOB_NAME { get; set; }
}
I would recommend putting the selectlist into your model instead of passing it through the view bag. The option you want is
#Html.DropDownListFor(model => model.lob_name, new SelectList(ViewBag.Titles,"Value","Text"))
you can set the selected item by setting model.lob_name on the controller and on post back that value will be set to the selected value of the dropdown
on your controller you can build the list like this
List<SelectListItem> ls = new List<SelectListItem>();
foreach(var temp in model){ //where model is your database
ls.Add(new SelectListItem() { Text = temp.Text, Value = temp.Value });
}
//then create a view model on your controller and pass this value to it
LObmodel vm = new LObmodel();
vm.DropDown = ls; // where vm.DropDown = List<SelectListItem>();
return View(vm);
then on your view you can reference that
#Html.DropDownListFor(x => x.lob_name, Model.DropDown)
with your model add the select list
public class LObmodel
{
public int LOB_ID { get; set; }
public string LOB_NAME { get; set; }
public List<SelectListItem> DropDown { get; set; }
}
then the top of your view would be
#model LObmodel
I had the same problem .
But i changed the view code of DDL using this code :
#Html.DropDownListFor(x => x.ClassID, (SelectList)ViewBag.ClassName);
The dropdownlist will bind to your model class called ClassID You will not be able to post the textual value of the ddl to the controller, only the ID behind the ddl.

How to bind a drop down with a model property while containing the list items of that drop down?

I am stuck in a problem, I have retrieved all the value for a drop down in a view bag and want to display them at run time. I have achieved it by using the following code for the controller
[HttpGet]
public ActionResult Index()
{
var categoryList = new PersonalApp();
SelectList catList = new SelectList(categoryList.GetAffinity().ToList(), "ClientName", "AffinityNum");
ViewBag.categoryList = catList;
return View();
}
and the following code for the view
#using (Html.BeginForm("index", "Home"))
{
#Html.DropDownList("categoryList", "Branch Type")
}
It really works but now I want to bind it with a model now. I have use the following code for this:
#Html.DropDownListFor(model=>model.AffinityNum, "categoryList", "BranchType")
But it gives me an error as CategoryList cannot be used as a parameter with the above code. How will I get this resolved as I can have all the values of a dropdown in the categorylist and I can bind it with a model property affinityNum with it as well.
Thanks
Your model should have an IEnumerable<SelectListItem> property that will hold the values:
public class PersonalApp
{
public string AffinityNum { get; set; }
public IEnumerable<SelectListItem> CategoryList { get; set; }
}
that you will populate in your controller action and pass to the view:
[HttpGet]
public ActionResult Index()
{
var model = new PersonalApp();
var categories = categoryList.GetAffinity().ToList();
SelectList catList = new SelectList(categories, "ClientName", "AffinityNum");
model.CategoryList = catList;
return View(model);
}
and finally in your strongly typed view you will use this property to bind the dropdown list to:
#model PersonalApp
...
#Html.DropDownListFor(x => x.AffinityNum, Model.CategoryList, "BranchType")
Please try;
#Html.DropDownListFor(model=>model.AffinityNum, (SelectList)ViewBag.categoryList)

Why won't a List of complex types bound to TextBoxes in a table show changes to the model in MVC 4?

I have run into an issue that seems pretty simple, but I have not been able to find a solution. I have created a ReportModel object that is the model in the view. The ReportModel contains a list of FinancialHistory objects. I populate the objects and display them in a table of textboxes within a form in the view using default binding (This works correctly). The user can then submit the form to refresh the FinancialHistory objects from a different datasource, replacing what was previously in the list with the new results. When the new results are returned, I can see that the model contains the expected new values, but when the HTML is rendered, the original amounts still appear. If the new results contains more objects than the original list (as shown in the example code), the added rows do appear with the correct values. So, if the original had 2 objects and the refreshed list has 3, the resulting HTML shows the first 2 rows with the old values and a 3rd row with the new values.
Here are the models:
public class ReportModel
{
public string AccountNumber { get; set; }
public IList<FinancialHistory> FinancialHistories { get; set; }
}
public class FinancialHistory
{
public FinancialHistory()
{
Id = Guid.Empty;
}
public Guid Id { get; set; }
public DateTime TransactionDate { get; set; }
public decimal TotalAmount { get; set; }
}
In the Home/Index view, I use HTML.TextBoxFor() to bind the properties of each FianancialHistory object in the list to textboxes in a table. Here is the Index view:
#model SimpleExample.Models.ReportModel
<form id="FormSave" method="post" name="FormSave" action="/Home/Refresh">
#Html.LabelFor(model => model.AccountNumber) #Html.TextBoxFor(model => model.AccountNumber)
<table class="table" style="width: 95%">
<tr>
<td >Date</td>
<td >Amount</td>
</tr>
#{
if (Model.FinancialHistories != null)
{
for (int index = 0; index <= Model.FinancialHistories.Count - 1; index++)
{
<tr>
<td>#Html.TextBoxFor(model => model.FinancialHistories [index].TransactionDate, "{0:MM/dd/yyyy}", new { #readonly = "true" })</td>
<td>#Html.TextBoxFor(model => model.FinancialHistories[index].TotalAmount, "{0:#,#.00}", new { #readonly = "true" })</td>
<td>#Html.HiddenFor(model => model.FinancialHistories[index].Id)</td>
</tr>
}
}
}
</table>
<input type="submit" id="submit" value="Refresh" class="submit" />
</form>
For this example, my action methods in the controller are very simple. Initially, the Index method populates the list with 2 FinancialHistory Objects. The Refresh method replaces the original 2 objects with 3 new objects, with different amounts.
public class HomeController : Controller
{
public ActionResult Index()
{
ReportModel reportModel = new ReportModel();
reportModel.AccountNumber = "123456789";
IList<FinancialHistory> financialHistories = new List<FinancialHistory>();
financialHistories.Add(new FinancialHistory
{
Id = Guid.NewGuid(),
TransactionDate = DateTime.Parse("3/1/2010"),
TotalAmount = 1000.00M
});
financialHistories.Add(new FinancialHistory
{
Id = Guid.NewGuid(),
TransactionDate = DateTime.Parse("4/1/2011"),
TotalAmount = 2000.00M
});
reportModel.FinancialHistories = financialHistories;
return View(reportModel);
}
public ActionResult Refresh(ReportModel reportModel)
{
FinancialHistoryRepository financialHistoryRepository = new FinancialHistoryRepository();
IList<FinancialHistory> financialHistories = new List<FinancialHistory>();
financialHistories.Add(new FinancialHistory
{
Id = Guid.Empty,
TransactionDate = DateTime.Parse("3/1/2010"),
TotalAmount = 1111.11M
});
financialHistories.Add(new FinancialHistory
{
Id = Guid.Empty,
TransactionDate = DateTime.Parse("4/1/2011"),
TotalAmount = 2222.22M
});
financialHistories.Add(new FinancialHistory
{
Id = Guid.Empty,
TransactionDate = DateTime.Parse("5/1/2012"),
TotalAmount = 3333.33M
});
reportModel.FinancialHistories = financialHistories;
return View("Index",reportModel);
}
}
That's how HTML helpers work and is by design. When rendering they are first looking in the ModelState for values and after that in the model. You are modifying the values of your model in the POST controller action, but the ModelState values still contain the old values which will be used. If you want to modify values of your model in a POST action you should remove the original values from the ModelState if you intend to redisplay the same view:
public ActionResult Refresh(ReportModel reportModel)
{
// clear the original posted values so that they don't get picked up
// by the helpers
ModelState.Clear();
FinancialHistoryRepository financialHistoryRepository = new FinancialHistoryRepository();
...
return View("Index",reportModel);
}

ASP.NET MVC 4 DropDownListFor error: Null Values

I am a beginner programmer and having trouble with the #Html.DropDownListFor helper...
I am using a General Repository and Unit of Work pattern based off of the tutorial here:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Here is my code for the Repository:
public class GenericRepository<TEntity> where TEntity : class
{
internal UsersContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(UsersContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
// Delete methods not shown
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
Here is my code for my UnitOfWork class:
public class UnitOfWork : IDisposable
{
private UsersContext context = new UsersContext();
private GenericRepository<UserProfile> userProfileRepository;
private GenericRepository<Lead> leadRepository;
private GenericRepository<UnitedStatesState> unitedStatesStateRepository;
public GenericRepository<UserProfile> UserProfileRepository
{
get
{
if (this.userProfileRepository == null)
{
this.userProfileRepository = new GenericRepository<UserProfile(context);
}
return userProfileRepository;
}
}
public GenericRepository<Lead> LeadRepository
{
get
{
if (this.leadRepository == null)
{
this.leadRepository = new GenericRepository<Lead>(context);
}
return leadRepository;
}
}
public GenericRepository<UnitedStatesState> UnitedStatesStateRepository
{
get
{
if (this.unitedStatesStateRepository == null)
{
this.unitedStatesStateRepository = new GenericRepository<UnitedStatesState>(context);
}
return unitedStatesStateRepository;
}
}
I am trying to use strongly typed views and models in order to pass the selectlist data to the view without using ViewData/ViewBag. From what I understand, the best practice is to do something similar to what I saw here:
validate a dropdownlist in asp.net mvc
I tried following that as closely as possible and this is what I came up with
My View Model looks like this:
public class Lead
{
public int LeadID { get; set; }
public int CompanyID { get; set; }
[Required(ErrorMessage = "Please enter state")]
[Display(Name = "State")]
[MaxLength(2)]
public string State { get; set; }
[Display(Name = "Assigned To")]
public string AssignedTo { get; set; }
[Timestamp]
public Byte[] Timestamp { get; set; }
public virtual Company Company { get; set; }
// IEnumerables for Dropdown Lists passed to views
public IEnumerable<UnitedStatesState> UnitedStatesStates { get; set; }
public IEnumerable<UserProfile> UserProfiles { get; set; }
// Objects passed to views
public Lead lead { get; set; }
}
These IEnumerables for my dropdown lists are then populated in my controller from my database through my repository. The odd part is that I am using these dropdown lists in two different views, Create and Edit. When I use the dropdown lists in the Create view they work perfectly both on the GET and POST ActionResults. When I try and use the same dropdown lists for my Edit view they work for the GET ActionResult (the view loads and the dropdowns work) but when I try to POST them to my Edit ActionResult I get the following error:
{"Value cannot be null.\r\nParameter name: items"} // This is the error as shown in Visual Studio 2012
System.ArgumentNullException: Value cannot be null.
Parameter name: items // This is the error shown in Google Chrome
Below is my Lead Controller with the Edit and Create ActionResults:
public class LeadController : Controller
{
// create instance of Repository Unit of Work
private UnitOfWork unitOfWork = new UnitOfWork();
public ActionResult Create()
{
// Get the current users profile
UserProfile userProfile = UserProfile.GetCurrentUserProfile();
// Creates Dropdown Lists to pass to view
var model = new Lead
{
UnitedStatesStates = unitOfWork.UnitedStatesStateRepository.Get(u => u.StateAbbreviation != null),
UserProfiles = unitOfWork.UserProfileRepository.Get(u => u.CompanyID == userProfile.CompanyID)
};
// Return View
return View(model);
}
[HttpPost]
public ActionResult Create(Lead model)
{
try
{
if (ModelState.IsValid)
{
// Call the current users profile
UserProfile userProfile = UserProfile.GetCurrentUserProfile();
// Create a new lead and apply all attirbutes that were entered
Lead lead = new Lead();
lead.CompanyID = userProfile.CompanyID;
lead.State = model.State;
lead.AssignedTo = model.AssignedTo;
// Add the lead and save the changes. Redirect to Lead Index.
unitOfWork.LeadRepository.Insert(lead);
unitOfWork.Save();
return RedirectToAction("Index");
}
}
catch (DataException)
{
ModelState.AddModelError("", "Unable to save changes. Try again and if the problem persists, see your system administrator.");
}
// Return view if ModelState is not valid
return View();
}
public ActionResult Edit(int id = 0)
{
// Get Users Profile
UserProfile userProfile = UserProfile.GetCurrentUserProfile();
// Check to see if Lead Exists
if (unitOfWork.LeadRepository.GetByID(id) == null)
{
return HttpNotFound();
}
// Creates Dropdown Lists and Gets current lead values to pass to view
var model = new Lead
{
lead = unitOfWork.LeadRepository.GetByID(id),
UnitedStatesStates = unitOfWork.UnitedStatesStateRepository.Get(u => u.StateAbbreviation != null),
UserProfiles = unitOfWork.UserProfileRepository.Get(u => u.CompanyID == userProfile.CompanyID)
};
return View(model);
}
[HttpPost]
public ActionResult Edit(Lead lead)
{
try
{
// Update lead if model state is valid
if (ModelState.IsValid)
{
unitOfWork.LeadRepository.Update(lead);
unitOfWork.Save();
return RedirectToAction("Index");
}
}
// Catch any concurrency exceptions
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var databaseValues = (Lead)entry.GetDatabaseValues().ToObject();
var clientValues = (Lead)entry.Entity;
if (databaseValues.State != clientValues.State)
ModelState.AddModelError("State", "Current value: "
+ databaseValues.State);
if (databaseValues.AssignedTo != clientValues.AssignedTo )
ModelState.AddModelError("Assigned To ", "Current value: "
+ databaseValues.AssignedTo );
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
lead.Timestamp = databaseValues.Timestamp;
}
catch (DataException)
{
//Log the error (add a variable name after Exception)
ModelState.AddModelError(string.Empty, "Unable to save changes. Try again, and if the problem persists contact your system administrator.");
}
// Return View if Model State is not valid
return View(lead);
}
The POST Edit ActionResult includes code to catch concurrencies which I created following the tutorial shown here:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application
Below is my view for Create (this works perfectly):
#model SolarToolbase.Models.Lead
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<div>
<div>
#Html.LabelFor(model => model.State)
#Html.DropDownListFor(model => model.State, new SelectList(Model.UnitedStatesStates, "StateAbbreviation", "UnitedStatesStateName"),"Choose State")<br />
#Html.ValidationMessageFor(model => model.State)
</div>
<div>
#Html.LabelFor(model => model.AssignedTo)
#Html.DropDownListFor(model => model.AssignedTo, new SelectList(Model.UserProfiles, "FullName", "FullName"),"Choose User")<br />
#Html.ValidationMessageFor(model => model.AssignedTo)
</div>
<p>
<input type="submit" value="Create" />
</p>
</div>
}
Below is my view for Edit(this throws the aforementioned errors when I hit the submit button. I inserted a comment below to show the line that the error is being thrown from):
#model SolarToolbase.Models.Lead
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.lead.LeadID)
#Html.HiddenFor(model => model.lead.Timestamp)
<div>
<div>
#Html.LabelFor(model => model.lead.State)
#Html.DropDownListFor(model => model.lead.State, new SelectList(Model.UnitedStatesStates, "StateAbbreviation", "UnitedStatesStateName"))<br /> // Error thrown from this line
#Html.ValidationMessageFor(model => model.lead.State)
</div>
<div>
#Html.LabelFor(model => model.lead.AssignedTo)
#Html.DropDownListFor(model => model.lead.AssignedTo, new SelectList(Model.UserProfiles, "FullName", "FullName"))<br />
#Html.ValidationMessageFor(model => model.lead.AssignedTo)
</div>
<p>
<input type="submit" value="Save" />
</p>
</div>
}
I apologize in advance for posting so much code, I just honestly don't know where this error is coming from and I've beat my head against the wall trying to figure it out for about 4 hours now. Free virtual high fives and good karma for anyone that can help.
Thanks!
In the case of a POST to both the Create and Edit actions, when there's an error or the ModelState is invalid, you catch any exceptions and return the default View with the constructed Lead view model, created and populated by the model binder.
In the Edit POST action though, if there is an error condition, you return the lead object to the View as its Model. Note that the UnitedStatesStates and the UserProfiles properties are not repopulated upon a POST. You populate them in the GET actions, but you have to do that in the POST actions too. You need to be careful that whatever model you are sending to the view is in proper shape, and it has all expected members populated.
Also notice your view model is of type Lead which has a property called lead. That's a code smell there; I wouldn't have a view model class having a reference to an instance of its own class. It's causing confusion for you already. I'd have Lead be LeadViewModel to be explicit and just have it hold all the properties and values it needs when going to and from the views, with no lead property.
In your Edit view, you're referencing the model's properties as model.lead.State for example, but in the Create view you're referencing the parent-level properties, as in model.State. But in the Edit view, when it comes to the SelectListItems you're using Model.UnitedStatesStates instead of Model.lead.UnitedStatesStates. As I said I'd do away with this pattern and do what the Create view does now, not having a child lead property at all. Just do model.State for example, for all properties and in both views.
So make sure your collection properties are populated whenever you pass the model to the view, as in
[HttpPost]
public ActionResult Edit(Lead lead)
{
try
{
// Update lead if model state is valid
if (ModelState.IsValid)
{
unitOfWork.LeadRepository.Update(lead);
unitOfWork.Save();
return RedirectToAction("Index");
}
}
// Catch any concurrency exceptions
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var databaseValues = (Lead)entry.GetDatabaseValues().ToObject();
var clientValues = (Lead)entry.Entity;
if (databaseValues.State != clientValues.State)
ModelState.AddModelError("State", "Current value: "
+ databaseValues.State);
if (databaseValues.AssignedTo != clientValues.AssignedTo )
ModelState.AddModelError("Assigned To ", "Current value: "
+ databaseValues.AssignedTo );
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
lead.Timestamp = databaseValues.Timestamp;
}
catch (DataException)
{
//Log the error (add a variable name after Exception)
ModelState.AddModelError(string.Empty, "Unable to save changes. Try again, and if the problem persists contact your system administrator.");
}
// Return View if Model State is not valid
/////////// CHANGES HERE
lead.UnitedStatesStates = unitOfWork.UnitedStatesStateRepository.Get(u => u.StateAbbreviation != null),
lead.UserProfiles = unitOfWork.UserProfileRepository.Get(u => u.CompanyID == userProfile.CompanyID)
return View(lead); // pass the model to the view for Create and Edit POST actions when there's an error
}
Do that in both POST actions. If there's an error, the view will be instantiated by the action with a populated model. Also change the Edit view to work just like the Create view, and not use the Lead view model's lead property. Presumably that will take care of any null reference exceptions in the views.