MVC3 - Accessing contents of dropdownlist in Controller - asp.net-mvc-2

Im new to MVC2/3 so keep that in mind. Also, using Ajax or jQuery is NOT an option.
I have a web page where the user must choose an item out of a dropdown list and then hit a "Filter" button. (Clicking this button will simply trigger the default POST action in my controller and return a filtered list of results.
I have everything working except I am running into one issue. When the Filter action is complete and returns control back to my view, the dropdown list contents are lost (ie, null). The results are returned no problem, it's just that my dropdown list is blank - thus preventing user from selecting another item out of the list.
Am I supposed to re-fill the dropdown list on the Filter action or is there a cleaner way to do this?
Here is a snapshot of my code:
My ViewModel
public class MyViewModel {
[DisplayName("Store")]
public IEnumerable<Store> StoreList { get; set; }
public string SelectedStore { get; set; }
}
My View (Index.cshtml)
#using (Html.BeginForm()) {
<h2>Search</h2>
#Html.LabelFor(m => m.StoreList)
#Html.DropDownListFor(m => m.SelectedStore, new SelectList(Model.StoreList, "StoreCode", "StoreCode"), "Select Store")
<input type="submit" value="Filter" />
}
My Controller:
public class MyController : Controller
{
public ActionResult Index() {
MyViewModel vm = new MyViewModel();
var storelist = new List<Store>();
storelist.Add(new Store { StoreCode = "XX" });
storelist.Add(new Store { StoreCode = "YY" });
storelist.Add(new Store { StoreCode = "ZZ" });
vm.StoreList = storelist;
return View(vm);
}
[HttpPost]
public ActionResult Index(MyViewModel model, string SelectedStore, FormCollection collection) {
if (ModelState.IsValid) {
/* this works, model state is valid */
/* BUT, the contents of model.StoreList is null */
}
return View( model);
}
}

Yes, you have to repopulate any models (including ViewData) that are passed to the view. Remember, it's a stateless system, your controller is re-instantiated with every call and starts effectively from scratch.
I would do it thus:
public class MyController : Controller
{
private List<Store> GetStoreList()
{
List<Store> StoreList = new List<Store>();
// ... Do work to populate store list
return StoreList;
}
public ActionResult Index() {
MyViewModel vm = new MyViewModel();
vm.StoreList = GetStoreList();
return View(vm);
}
[HttpPost]
public ActionResult Index(MyViewModel model, string SelectedStore, FormCollection collection) {
if (ModelState.IsValid) {
/* this works, model state is valid */
/* BUT, the contents of model.StoreList is null */
}
model.StoreList = GetStoreList();
return View( model);
}
}

Short answer is yes, you need to refill the drop-down list in the Filter action. ASP.NET MVC isn't WebForms - there is no ViewState to preserve the contents of your list.

Again fill the dropdown as mvc don't have View state
[HttpPost]
public ActionResult Index(MyViewModel model, string SelectedStore, FormCollection collection) {
if (ModelState.IsValid) {
/* this works, model state is valid */
/* BUT, the contents of model.StoreList is null */
}
model.StoreList = GetStoreList();
return View( model);
}

Related

Creating a record with values passed by query string

I am creating a record in a table that has foriegn key in it. The foriegn key is getting passed in the query string and I have set the value in the ViewBag. I have added this to the form but it will not work.
Here is the code from the controller:
public ActionResult Create(int propertyId)
{
ViewBag.storagePropertyId = propertyId;
return View();
}
Here is the code from the view.
#Html.HiddenFor(model => model.propertyId, new { value =ViewBag.propertyId })
Is this how I should be doing this? If so, is there a problem with that form
Why not just set the value of the Model.PropertyID in the controller, or better yet in a viewmodel.
ViewModel
public class MyViewModel()
{
public int PropertyID { get; set; }
public MyViewModel() { }
public MyViewModel(int propertyID)
{
this.PropertyID = propertyID;
}
}
Action Result
public ActionResult Create(int propertyId)
{
return View(new MyViewModel(propertyId));
}
View
#model MyViewModel
#Html.HiddenFor(m => m.PropertyID)
Then the value of the Hidden field will contain the value of the property ID.

How to bind a model property with DefaultModelBinder - ASP.NET MVC2

I have the following scenario.
I have the Edit/Employee view populated with a model from an Entity Framework entity (Employee)
I post from Edit/Employee to the Save/Employee controller action. The Save/Employee action expect another type (EmployeeSave) which has Employee as property
This is the Edit/Employee method
public ActionResult Edit(EmployeesEdit command)
{
var employee = command.Execute();
if (employee != null)
{
return View(employee);
}
return View("Index");
}
This is the Save/Employee method
public ActionResult Save(EmployeesSave command)
{
var result = command.Execute();
if (result)
{
return View(command.Employee);
}
return View("Error");
}
This is the EmployeeSave class
public class EmployeesSave
{
public bool Execute()
{
// ... save the employee
return true;
}
//I want this prop populated by my model binder
public Employee Employee { get; set; }
}
The MVC DefaultModelBinder is able to resolve both Employee and EmployeeSave classes.
You might need to use BindAttribute here. If your view contains the properties of the EmployeeSaveViewModel and Employee named like this (I made up property names)
<input type="text" name="EmployeeSaveViewModel.Property1" />
<input type="text" name="EmployeeSaveViewModel.Employee.Name" />
<input type="text" name="EmployeeSaveViewModel.Employee.SomeProperty" />
Then, your action could look like this:
[HttpPost]
public ActionResult Save([Bind(Prefix="EmployeeSaveViewModel")]
EmployeeSaveViewModel vm)
{
if(ModelState.IsValid)
{
// do something fancy
}
// go back to Edit to correct errors
return View("Edit", vm);
}
You could resolve it by passing the edited data back to Edit action that handles HttpPost. Inside create EmployeeSave object and assign its Employee property the value of Employee returned to yout Edit action. Call Save action by passing EmployeeSave object.
[HttpGet]
public ActionResult Edit()
{
return View();
}
[HttpPost]
public ActionResult Edit(Employee employee)
{
EmployeeSave employeeSave = new EmployeeSave { Employee = employee };
return View("Save", employeeSave);
}
Another method would be to use EmployeeSave instead of Employee as your model.

MVC2 SelectedListItem not binding to ViewModel

I am having a problem with a selectlistitem, the values of which are being retrieved from a database.
It is displaying the list of items in the view, but it is not passing through (POSTing) the selected value into the model.
So when the user submits, or the page reloads due to validation, the select value (PositionApplied) is empty.
Can anyone give me some pointers as to where I am going wrong?
In my controller:
[HttpGet]
public ActionResult Index()
{
PopulateJobsDropdown();
return View();
}
private void PopulateJobsDropdown()
{
IEnumerable<SelectListItem> items = _service.GetJobs()
.Select(c => new SelectListItem
{
Value = c.JobID.ToString(),
Text = c.JobTitle
});
ViewData["PositionApplied"] = items;
}
In my ViewModel
public IEnumerable<SelectListItem> PositionApplied { get; set; }
In my View
<%=Html.DropDownList("PositionApplied")%>
Thanks in advance for any pointers!
So, where is the code line that get's the
ViewData["PositionApplied"] = items;
into
public IEnumerable<SelectListItem> PositionApplied { get; set; }
something like:
this.PositionApplied = ViewData["PositionApplied"] as IEnumerable<SelectListItem>;
and you can simple use in your View:
<%
IEnumerable<SelectListItem> PositionApplied =
ViewData["PositionApplied"] as IEnumerable<SelectListItem>;
%>
...
<%= Html.DropDownList("myDropDOwnId", PositionApplied) %>
or is there some of automagical happening under MVC2 that I'm not aware about? As I use the example I give you, all the time.
Added
in order to avoid Linq to Entities error (if you are using it) change your method to
private void PopulateJobsDropdown()
{
IQueryble<Your_Table> jobs = _service.GetJobs();
List<SelectListItem> items = new List<SelectListItem>();
foreach(var job in jobs)
items.add(new SelectListItem
{
Value = c.JobID.ToString(),
Text = c.JobTitle
});
ViewData["PositionApplied"] = items;
}
and all will work fine.

ModelState.IsValid works on a "new" form, but not on an "edit" form

I have a ASP.NET MVC 2.0 application using Entity Framework. All my views use view models, most of them complex. Meaning...the object to be edited is a property of the view model, and not the view model itself.
I am using partial classes with data annotations, and checking ModelState.IsValid inside the POST actions in the controller.
I have a "NEW" form and an "EDIT" form for a simple object with 3 fields!
The ModelState.IsValid check works on the NEW form, and shows the correct "required field" errors, if I try to submit a blank form.
But if I load an EDIT form, and clear the values from some textboxes that are required, and submit the form, I do NOT get validation errors, I just get an exception:
Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerWrapper'.
So my question is, does ModelState.IsValid not work with an EDIT form, since perhaps it's looking at the values from the view model object that were loaded, instead of the FormCollection?
// this one does not validate
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int accountStatusKey, AccountStatusEditViewModel model, FormCollection values)
{
if (ModelState.IsValid)
{
db.UpdateAccountStatus(accountStatusKey, values);
return RedirectToAction("States");
}
else
{
return View("Edit", model);
}
}
// this one does validate
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult New(AccountStatusNewViewModel model, FormCollection values)
{
if (ModelState.IsValid)
{
db.AddAccountStatus(values);
return View("States", new AccountStatusStatesViewModel());
}
else
{
return View("New", model);
}
}
// how I arrive AT the edit form
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Edit(int accountStatusKey)
{
return View("Edit", new AccountStatusEditViewModel(accountStatusKey));
}
// and finally, the view model code
public class AccountStatusEditViewModel : ViewModelBase
{
public AccountStatus AccountStatus { get; private set; }
public IEnumerable States { get; private set; }
public List StatusTypes { get; private set; }
public AccountStatusEditViewModel(int accountStatusKey)
{
AccountStatus = db.GetAccountStatusByKey(accountStatusKey);
States = db.GetAllStates();
StatusTypes = new List();
StatusTypes.Add("Primary Status");
StatusTypes.Add("Secondary Status");
StatusTypes.Add("External Status");
}
public AccountStatusEditViewModel()
{
}
}
// this action method does not work at all either - no db updating, no validation
// the page simply redirects to "States" view, which should only happen if the db
// was being updated, right? But nothing is changing in the DB, and the validation
// never happens.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(AccountStatusEditViewModel model)
{
if (ModelState.IsValid)
{
if (TryUpdateModel(model, "AccountStatus"))
{
return RedirectToAction("States");
}
else
{
return View("Edit", model);
}
}
else
{
return View("Edit", model);
}
}
Since the 2.0 version of MVC i'm not using the formcollection anymore.
I only use the viewmodel in the action's parameter when i have a post, like this:
[HttpPost]
public ActionResult Activatecard(ActivateCardViewModel model)
{
When 'it' can't create my model (entered blabla for a datetime field, or when the validations are not met (i use the validation attributes from the System.ComponentModel.DataAnnotations namespace) i get the ModelState.IsValid equals to false.
I created a blank asp.net mvc2 app, and this was the model that the standard template used for the logon action.
Please eliminate the FromCollection parameters. You can use the default ModelBinders for your view model. Asp.net MVC try to maps the values from your form into your model.
Can you please post the action method, which leads you to the edit form, too?

ASP.NET MVC : Sending information to make a RedirectToAction

I need to send data before making a "RedirectToAction" to the new view, and do not want the data being sent by "GET".
The only thing I can think of is to keep this information in session before redirecting to the new view, but I prefer to do otherwise.
Thanks.
Edit width example
public class AccountController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Login()
{
return View(new LoginViewModel());
}
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (LoginModel.Login(model)){
UserData ud = UserData(model.IdUser);
return RedirectToAction("Index", "Information");
}
// code
}
}
//
public class InformationController : Controller
{
public ActionResult Index()
{
//receives "ud" information
// ...
return View();
}
}
You could pass the data as a request parameter:
return RedirectToAction("Foo", new { param1 = "value1", param2 = "value2" });
I'm not sure what you are trying to achieve but TempData["yourkey"] might be what you want to use. it's not best practice though. but if you want to redirect to an action, where do you want the data to be sent to?