Reputation: 2269
I'm trying to customize Identity (2.1) using MVC5 and EF6.1. I've added a CountryId to the Identity users table and a whole separate Country table (CountryId, CountryName) with a FK constraint on CountryId. I'm trying to do a drop down list that selects the users country if the user has already selected it in the past (from the CountryId in the users table). However, if it hasn't been selected, the value will be null.
The logic I'm trying to accomplish is this:
if (Model.Country.CountryName != null) {
@Html.DropDownListFor(m => m.Country, new SelectList(Model.Countries, "Value", "Text"), Model.Country.CountryName)<br />
} else {
@Html.DropDownListFor(m => m.Country, new SelectList(Model.Countries, "Value", "Text"), "Select a Country")<br />
}
If you remove the if else statement above (which doesn't work either), each DropDownListFor
by itself works, except that the former throws a NullRefernceException when CountryName is null. The latter simply doesn't remember what the user chose in the past. Here is my controller:
var userId = User.Identity.GetUserId();
ApplicationUser user = await UserManager.FindByIdAsync(userId);
var model = new IndexViewModel
{
HasPassword = HasPassword(),
//PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
//TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
//Or we can write like this because of the user statement above.
//PhoneNumber = user.PhoneNumber,
//TwoFactor = user.TwoFactorEnabled,
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId),
Country = user.Country,
State = user.State,
City = user.City,
Countries = new SelectList(db.Countries, "CountryId", "CountryName")
};
return View(model);
If I use something like this in my controller model:
Countries = new SelectList(db.Countries, "CountryId", "CountryName", user.Country.CountryName)
Then it throws a NullReferenceException at the controller. I understand why this is happening, but for the life of me can't find an answer after half a day's search.
I'm just wondering if there is a clean way to handle the null value and therefore, pass a different default value if CountryId is null?
Any help is always much appreciated.
Edit: I forgot to mention the viewmodel. I basically added all this to the IndexViewModel (although I'm not sure if I need it all at this point):
public int CountryId { get; set; }
public int StateId { get; set; }
public int CityId { get; set; }
public virtual Country Country { get; set; }
public virtual State State { get; set; }
public virtual City City { get; set; }
public IEnumerable<SelectListItem> Countries { get; set; }
public IEnumerable<SelectListItem> States { get; set; }
public IEnumerable<SelectListItem> Cities { get; set; }
Upvotes: 1
Views: 3067
Reputation:
Firstly you cannot bind a dropdown to a complex type. Html has no concept of what your c# Country
class is. You need to bind to your CountryId
property
@Html.DropDownListFor(m => m.CountryId , Model.Countries)
Note also that Countries
is already a SelectList
so its a bit pointless and unnecessary extra overhead to then create another SelectList
from it, so it's just Model.Countries
, not new SelectList(Model.Countries, "Value", "Text")
Next, your binding to a property (CountryId
) so its the value of CountryId
that determines which option is selected - the 3rd parameter (Model.Country.CountryName
) is just ignored. But even if it wasn't, it simply would not have selected anything because the value
attributes of your options are the values of the CountryId
property, not the CountryName
property. You need to set the value of CountryId
in the controller if Country
exists.
Finally, your if (Model.Country.CountryName != null) {
throws an exception because the value of Country
is null
(you cannot access the CountryName
property of a null
object).
The if/else
block is completely unnecessary and you need just one line of code in the view
@Html.DropDownListFor(m => m.CountryId , Model.Countries, "-Please select-")
If the value of CountryId
is 0
(the default for int
) then the "-Please select-" option will be selected. If the value of CountryId
matches one of the values in the collection, then that option will be selected when the view is rendered. Because CountryId
is typeof int
, then a validation error will be generated if the user does not select a valid country.
And finally, despite the IndexViewModel
naming, it is not a view model! A view model includes only the properties you need to display/edit in a view so the 3 virtual properties you have should be removed (and probably the 4 properties relating to States
and Cities
but not sure if you have just omitted some code from you view and controller)
Upvotes: 2