0

I have the following Address ViewModel:

public class AddressViewModel { [StringLength(20, MinimumLength = 2, ErrorMessage = "Country name is too short")] public String Country { get; set; } public String City { get; set; } public String Street { get; set; } public String Number { get; set; } public String ApartmentBuilding { get; set; } public String Sector { get; set; } } 

And the view that renders it:

<div class="control-group offset2 span6"> @Html.LabelFor(m => m.Country) <div class="controls"> @{ var countryCtrlName = Html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName("Country"); Html.RenderAction("List", "Country", new { ControlName = countryCtrlName }); } @Html.ValidationMessageFor(m => m.Country) </div> </div> 

Html.RenderAction("List") calls a controller method that gets all countries from the database and renders and renders a partial with the dropdown, here's the view:

@model IEnumerable<SelectListItem> @{var controlName = (string)ViewBag.ControlName;} @Html.DropDownList(controlName, Model, new {@class = ViewBag.CssClass}) 

Even though my DropdownList control is rendered with the correct name and thus mapped to the correct ViewModel upon POST, the input control isn't decorated with the necessary data-val attributes to enable client-side validation (I believe this is because the model for the partial is IEnumerable instead of the the string property that holds the country name.

The address view model is used through my application as a nested property on many views. Any ideas on how to make it validate?

Edit: updated ViewModel based on @Robert's answer:

public class AddressViewModel { [StringLength(20, MinimumLength = 2, ErrorMessage = "Country name is too short")] public String Country { get; set; }

public String City { get; set; } public String Street { get; set; } public String Number { get; set; } public String ApartmentBuilding { get; set; } public String Sector { get; set; } public IEnumerable<CountryViewModel> CountryList {get; set;} //Constructor to pass the list of countries public AddressViewModel(IEnumerable<CountryViewModel> countries) { this.CountryList = countries; } 

}

2
  • I'm not familiar with KDropDownList. Google tells me it's part of Keyoti.Search. Is that correct? Commented Mar 6, 2013 at 20:57
  • KDropDownList is an extension I made that decorates the dropdown with some additional classes. It's the same as calling Html.DropDownListFor (I updated the code anyway for readability) Commented Mar 6, 2013 at 21:03

2 Answers 2

1

Have you tried making a CountryModel and have a separate controller that deals with your dropdown list. have the controller return a partial view that you can put on any page you want. Have one property on the CountryModel with an IEnumerable?

Address View:

@model AddressModel @Html.Partial("nameOfPartialView", CountryModel) 

Model:

public class CountryModel { public IEnumerable<Countries> Countries { get; set; } } 

Controller:

public ActionResult Countries() { var countries = //get the list from the database return PartialView(countries); } 

Partial View with the countries DropDownList:

@model CountryModel @{var controlName = (string)ViewBag.ControlName;} @Html.DropDownListFor(Model => Model.Countries) 

Controller that accepts the country:

public ActionResult GetCountry(int CountryId) { //do something with the selected country } 
Sign up to request clarification or add additional context in comments.

5 Comments

How would I declare the binding for the country property on the address view?
Robert, I updated my ViewModel based on your answer. I assume that each time I declare an address on any ViewModel I'd initialize it using the data from the database and just pass it to the view that will be rendered.
sorry for the late response, yes that is correct. Also as a side note if your list of countries is always the same you should look into caching it so that you are not making that call to the database every time you need to populate a list of the countries
On the address view you would add the partial view with the countries. The partial view has its own model that you don't have to add anything to. So your partial view would literally only contain the list of countries and you would just need an int CountryId in your addressViewModel. I will adjust my answer to show you what I mean
Yeah, now every time I have a controller than returns a view with an entity that has a nested AddressViewModel as a property I pass the list of countries just once (the way I was doing it before I was requesting the database list between 2 and 6 times per rendered view). Thanks again for the help. I will have to look for a caching strategy, but further down the road.
0

I think you're right about what the problem is: you're not passing your annotated model to the partial view, but an IEnumerable of SelectListItem. The framework doesn't know what you're displaying represents: it just knows what to call it.

I can see that doing it this way is handy, but it kind of violates the spirit of MVC. In this case, your "model" isn't really a model, it's just a way of passing a list of markup items (the list items).

I would use the whole AddressViewModel as your model. That way you'll retain the information from the data annotations that tell you what the requirements for that property are.

2 Comments

I didn't want to make a different partial view for each time I need to render a dropdown list that is another entity (and sometimes the dropdown is filled or updated with data from an ajax call). Is it a good idea to include the list of possible countries along with my whole model? I don't want to resend the whole country list back to my server on POST, and it will if it's part of the bound model, right?
Not necessarily. The only thing that gets posted back is the value of the tags. So the value of the DropDownList will be posted back, but not the SelectListItems.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.