I have a site where all pages need a dropdown listing countries and another dropdown containing child data based on the selected country.
I have taken a similar approach to this article:
MVC 3 Layout Page, Razor Template, and DropdownList
So I need the functionality as follows:
- 1.) Context controls always present on the layout page (the country dropdown and it's dependent)
- 2.) When the country dropdown changes, populate the second dropdown with data relevant to that country.
Currently I have a UserContextController which handles the get and post requests and returning partial views:
Index action from UserContextController:
[HttpPost]
public ActionResult Index(UserContextViewModel userContext)
{
//CurrentUserContext is a Session wrapper to store the selections from the dropdowns
//in application scope for ease of reference. This is accessed in a base controller all controllers inherit from
this.CurrentUserContext.SetCountryContext(this.countryRepository.GetList().SingleOrDefault(c => c.Id == userContext.CountryId));
if (userContext.TerritoryId > 0)
{
//return partial view containing model data and select list data
return PartialView("ContextControls", new UserContextViewModel(this.countryRepository.GetList(),
AvailableTerritories,
CurrentUserContext.CurrentCountry,
CurrentUserContext.CurrentTerritory));
}
My Layout page referencing the UserContextController and Index action:
<header>
<div class="content-wrapper">
<a href=".">HOME</a>
@Html.Action("Index", "UserContext")
</div>
</header>
The dropdowns in the partial view added to the layout page have some jquery that invokes an http post to the UserContextController when they change (shaped to the UserContextViewModel schema):
//_targetUrl is: /UserContext
$.post(_targetUrl, { CountryId: $('#CountryId').val(),TerritoryId:$('#TerritoryId').val() }, function (result) { });
View Model code that sets the selected item:
public IEnumerable<SelectListItem> CountrySelectItems
{
get
{
int selectedCountryId = this.selectedCountry==null ? 0 : this.selectedCountry.Id;
return new SelectList(this.countries, "Id", "Name",selectedCountryId);
}
}
Constructor of the ViewModel that sets the selected country:
public UserContextViewModel(IEnumerable<Country> countries, IEnumerable<Territory> territories,Country selectedCountry,Territory selectedTerritory)
{
this.countries = countries;
this.territories = territories;
this.selectedCountry = selectedCountry;
this.selectedTerritory = selectedTerritory;
}
The partial view:
<p>
<label>Country:</label>
@Html.DropDownList("CountryId", @Model.CountrySelectItems, new { @class = "context-control" })
</p>
The User context session wrapper to hold the values set :
protected IUserContext CurrentUserContext
{
get
{
const string contextKey = "USER_CONTEXT";
if (HttpContext.Session[contextKey] == null)
{
HttpContext.Session.Add(contextKey, new UserContext());
}
return HttpContext.Session[contextKey] as IUserContext;
}
}
So this works and updates the values in my UserContext but the country dropdown never sets the selected country and always defaults to the first item in the list. i.e. contains UK and Germany. Select Germany -> post to controller -> sets the selected value -> dropdown still on UK!!
The UI should return to a start page when these dropdowns change as it effects the data rendered in the UI and the user needs to be aware of the "context change" (so they don't add data for a different country context for example).
What's interesting is that in Fiddler when the dropdown changes I can see the response returned contains the whole layout page and dropdown HTML has the correctly selected options!
I think my approach is wrong...does anyone have similar functionality they have achieved? I have come at this from the perspective of a production WebForms project completed recently that does the same via user controls and wanted to produce a PoC in MVC but failing miserably !
I think I need to drive the dropdowns via Angular to WebAPI requests and the setting of the context.