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.
Related
I have a form view that submits form data to the post action on a controler and then redirects to another view that uses logic to display either a success or failure, but the new view just shows blank values for model properties. Here is the post action:
[HttpPost]
public ActionResult ContactUs(TTT.Models.ContactUsModel model)
{
logger.Info(model.URL + "Contact Us Form submitted");
var userkey = model.ValidationKey;
var sessionkey = Session["ContactUsKey"];
var lastsubmission = Session["ContactUsTime"];
model.Response = "success";
//first check if honeypot was populated via a bot and if so send it to the success page without doing anything
if (model.WorkAddress != "")
{
logger.Info("honeypot triggered");
return View("ContactUsResponse", model);
}
I'll leave out the remainder of the controler, but
And here is the view it's redirecting to:
#using TTT.Models
#using Sitecore.Mvc
#model ContactUsModel
<h1>#Model.Title</h1>
<div>#Model.Body</div>
<div>
#if (#Model.Response == "fail")
{
#Model.Failure;
} else
{
#Model.Success;
}
</div>
Instead of returning a new view, call RedirectToAction and return new view from that controller.
[HttpPost]
public ActionResult ContactUs(TTT.Models.ContactUsModel model)
{
//--- Code omitted for brevity
if (model.WorkAddress != "")
{
logger.Info("honeypot triggered");
return RedirectToAction("ContactUsResponse", new { response = model });
}
}
public ActionResult ContactUsResponse(TTT.Models.ContactUsModel response)
{
return View(model)
}
Here is the code for my Model. ListBuilder.DropDown is part of a common class of functions, which simply returns a List when provided the string name of a stored procedure that will be called on the database.
There is some more shared common class code (stored procedure related) with in the try statement, but that implementation is irrelevant to the problem I'm having. The data is successfully retrieved and stored into the model.
public class PositionViewModel
{
[Display(Name = "Series")]
public string series { get; set; }
public int seriesID { get; set; }
public List<SelectListItem> list_Series { get; set; }
}
public PositionViewModel(string id)
{
Get(id);
this.list_Series = ListBuilder.DropDown(AppConstants.StoredProc_GetSeries);
}
public Position Get(string id)
{
ExecStoredProcedure sp = null;
DataTable dt = new DataTable();
try
{
sp = new ExecStoredProcedure(AppConstants.SP_POSITION_GET, new ConnectionManager().GetConnection(), AppConstants.SP_POSITION_GET);
sp.SPAddParm("#PD_ID", SqlDbType.Char, id, ParameterDirection.Input);
dt = sp.SPselect();
if (dt.Rows.Count > 0)
{
this.pd_id = dt.Rows[0]["PD_ID"].ToString();
this.official_title = dt.Rows[0]["label"].ToString();
this.series = dt.Rows[0]["Series"].ToString();
this.grade = dt.Rows[0]["Grade"].ToString();
this.PDType = dt.Rows[0]["PDType"].ToString();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
sp.dbConnection.Close();
}
return this;
}
Here is the code for my Controller
[HttpGet]
public ActionResult PositionEdit(string id)
{
PositionViewModel model = new PositionViewModel(id);
return View("PositionEdit", model);
}
[HttpPost]
public ActionResult PositionEdit(PositionViewModel model)
{
if (ModelState.IsValid)
{
int rc = model.Update();
return RedirectToAction("PositionView");
}
else
{
return View("PositionEdit", model);
}
}
Here is the code for my view. What I'd like to have is a dropdownlist that contains the model.seriesID (a sequence number) but as the user selects an item, it will update the textbox with model.series (the name of the series)
#model Project.Models.PositionViewModel
#{
ViewBag.Title = "Edit Position Description";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.series)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => Model.seriesID, Model.list_Series, new { style = "width:550px" })
#Html.ValidationMessageFor(model => Model.seriesID)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.series, new { style = "width:250px;" })
#Html.ValidationMessageFor(model => model.series)
</div>
<div class="toppad20">
<input type="submit" value="Save" />
</div>
}
I am having trouble linking the dropdownlist with the textbox. Do I need some kind of onChange event? Thanks in advance for your help.
Your solution involves passing a string into your view model's constructor. However, on post, the model binder will be incapable of instantiating your view model with anything but the parameterless constructor. That's part of the reason, but not the only reason, that view models should not handle things like datastore access. That is the job of the controller.
On your view model, leave your list property as a auto-implemented property and then in your controller call ListBuilder.DropDown, which you can use data from your model to call, at that point.
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"];
}
I have a table called Roles with three fields
Guid RoleId
string RoleName
string Description
In my register.cshtml view I want to have a dropdownlist which shows the list of RoleName from Roles table. I also need to be able to get that value and work with it, like assigning the Role to user, which will in done in controller.
My view currently looks like the one below, i'm using model as AspNetUser but it doesn't have knowledge about Role which is what I want to show in dropdownlist.
#model Sorama.CustomAuthentiaction.Models.AspNetUser
#{
ViewBag.Title = "Register";
Layout = "~/Views/shared/_BootstrapLayout.empty.cshtml";
}
#section Styles{
<link href="#Url.Content("~/Content/bootstrap.css")" rel="stylesheet" type="text/css" />
}
<div class ="form-signin">
#using (Html.BeginForm("Register", "Account"))
{
#Html.ValidationSummary(true)
<h2 class="form-signin-heading"> Register </h2>
<div class ="input-block-level">#Html.TextBoxFor(model=>model.Email, new{#placeholder = "Email"})</div>
<div class ="input-block-level">#Html.TextBoxFor(model=>model.UserName, new{#placeholder = "UserName"})</div>
<div class ="input-block-level">#Html.PasswordFor(model=>model.Password, new{#placeholder ="Password"})</div>
<div class ="input-block-level">#Html.DropDownListFor(//don't know what to do
<button class="btn btn-large btn-primary" type="submit">Register</button>
}
</div>
My controller looks like this
public class AccountController : Controller
{
//private readonly IDbContext dbContext;
//
// GET: /Account/
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
[AllowAnonymous]
public ActionResult Login(LoginModel model)
{
if(Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
[HttpGet]
public ActionResult Register()
{
string [] roles = Roles.GetAllRoles();
return View(roles);
}
[HttpPost]
public ActionResult Register(AspNetUser model)
{
return View();
}
public ActionResult Index()
{
return View();
}
}
What do I need to do, to have that dropdownlist?
In your controller you need to pass the string[] (IEnumerable<string>) representing your roles into your view somehow...
There are many ways to achieve this, but in your AccountController you could do the following:
public class AccountController : Controller
{
private IDbContext dbContext;
public AccountController(IDbContext dbContext)
{
// Made up field that defines your GetAllRoles method
this.dbContext = dbContext;
}
public ActionResult Register()
{
// Call the GetAllRoles() and capture the result in a variable called roles
var roles = dbContext.GetAllRoles();
return View(new AspNetUser {
Roles = roles
});
}
}
Note: I do not enforce the list to be in any form in the controller (I do not specify it should be a select list), I may want to display the items as in a different way and I let the view be flexible by passing the values only and allowing the view to decide how to render the values.
In your View you can then use where you want the dropdown list to appear:
#Html.DropDownListFor(model => model.Roles, Model.Roles
.Select(role => new SelectListItem { Text = role, Value = role })
As I mentioned, there are many ways to achieve what you are wanting but almost one thing is certain, that with aspnet mvc you will most likely be using the Html helper DropDownListFor MSDN Documentation here:
http://msdn.microsoft.com/en-us/library/system.web.mvc.html.selectextensions.dropdownlistfor(v=vs.108).aspx
EDIT 1:
Create a model to hold the User and Role informations like so:
public class RegisterViewModel
{
public AspNetUser AspNetUser { get; set; }
public IEnumerable<string> Roles { get; set; }
}
In the controller it could look like so:
public class AccountController : Controller
{
private RoleProvider roleProvider;
public AccountController(RoleProvider roleProvider)
{
this.roleProvider = roleProvider;
}
public ActionResult Register()
{
// Call the GetAllRoles() and capture the result in a variable called roles
// var roles = roleProvider.GetAllRoles();
// Or, as you have specified:
var roles = Roles.GetAllRoles();
return View(new RegisterViewModel {
AspNetUser = GetTheAspNetUser(),
Roles = roles
});
}
}
In the View you need to update the model to use:
#model Sorama.CustomAuthentiaction.Models.RegisterViewModel
If you are unwilling/unable to make such a change you could add the list of Roles to the Viewbag:
ViewBag.RoleList = roleProvider.GetAllRoles();
Or as you alluded to:
ViewBag.RoleList = Roles.GetAllRoles();
Then access in the View like so:
#Html.DropDownListFor(model => model.Roles, ViewBag.RoleList
.Select(role => new SelectListItem { Text = role, Value = role })
In a similar scenario, I've done something like this:
private void BagSelectList()
{
ViewBag.List = new SelectList(
db.SetOfCandidateValues,
"KeyPropertyOfTheSet",
"NameOfThePropertyToAppearInTheDropDownList",
selectedValue);
}
And in the view:
#Html.DropDownListFor(
model => model.ForeignKeyProperty,
(SelectList)ViewBag.List)
(Of course, if you dislike the ViewBag, you can do it using the strongly typed view model.)
I have this in a partial view
#using (Html.BeginForm(MVC.Inventory.ActionNames.AddVehicles, MVC.Inventory.Name, new { model = Model.Items }))
{
<div><button>#AuctionControllerResource.AddToBiddingProcess</button></div>
}
The post method is this
[HttpPost]
public virtual ActionResult AddVehicles(List<VehicleViewModel> model)
{
return null;
}
When I put a breakpoint in the view I can see that Model.Items has 1 item in it as it should. However, when I hit the Post action method on button click, there are no items in the model.
I have added this in the form
#Html.HiddenFor(m => m.Items)
but it doesn't help.
What am I doing wrong?
thanks,
Sachin
EDIT
Additional code
public class ListViewModel<T> : IQuery
where T : class
{
public List<T> Items { get; set; }
...
}
The following doesn't do what you think it does:
new { model = Model.Items }
You cannot pass complex objects like that. You will have to generate hidden fields in the form if you want this to work.
I have added this in the form
#Html.HiddenFor(m => m.Items)
No, it's normal that it doesn't help. The hidden field works only with simple types. You wil have to loop through the items in the collection and generate corresponding fields for each property of each element:
#using (Html.BeginForm(MVC.Inventory.ActionNames.AddVehicles, MVC.Inventory.Name))
{
for (var i = 0; i < Model.Items.Count; i++)
{
#Html.HiddenFor(x => x.Items[i].Prop1)
#Html.HiddenFor(x => x.Items[i].Prop2)
#Html.HiddenFor(x => x.Items[i].ComplexProp3.Prop1)
#Html.HiddenFor(x => x.Items[i].ComplexProp3.Prop2)
...
}
<div>
<button>#AuctionControllerResource.AddToBiddingProcess</button>
</div>
}
But this seems quite a waste. Since the user cannot modify those values anyway in the form, I would recommend you simply passing an id which will allow you to retrieve the corresponding items from your data store in the POST action:
#using (Html.BeginForm(MVC.Inventory.ActionNames.AddVehicles, MVC.Inventory.Name, new { id = Model.ItemsId }))
{
<div>
<button>#AuctionControllerResource.AddToBiddingProcess</button>
</div>
}
and then:
[HttpPost]
public virtual ActionResult AddVehicles(int id)
{
List<VehicleViewModel> model = GetItemsFromDataStore(id);
...
}