我對如何實現Razor視圖中地址的級聯下拉列表感興趣。我的網站實體具有SuburbId屬性。郊區有CityId,城市有ProvinceId。我想在網站視圖中顯示所有郊區,城市和省的下拉菜單,例如,郊區下拉菜單將首先顯示「首選城市」,城市下拉菜單「首選省份」。在選擇一個省,省內的城市人口衆多等。MVC 3 Razor視圖中的級聯下拉
我該如何做到這一點?我從哪說起呢?
我對如何實現Razor視圖中地址的級聯下拉列表感興趣。我的網站實體具有SuburbId屬性。郊區有CityId,城市有ProvinceId。我想在網站視圖中顯示所有郊區,城市和省的下拉菜單,例如,郊區下拉菜單將首先顯示「首選城市」,城市下拉菜單「首選省份」。在選擇一個省,省內的城市人口衆多等。MVC 3 Razor視圖中的級聯下拉
我該如何做到這一點?我從哪說起呢?
我們來舉個例子來說明。與往常一樣開始一個模型:
public class MyViewModel
{
public string SelectedProvinceId { get; set; }
public string SelectedCityId { get; set; }
public string SelectedSuburbId { get; set; }
public IEnumerable<Province> Provinces { get; set; }
}
public class Province
{
public string Id { get; set; }
public string Name { get; set; }
}
下一個控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
// TODO: Fetch those from your repository
Provinces = Enumerable.Range(1, 10).Select(x => new Province
{
Id = (x + 1).ToString(),
Name = "Province " + x
})
};
return View(model);
}
public ActionResult Suburbs(int cityId)
{
// TODO: Fetch the suburbs from your repository based on the cityId
var suburbs = Enumerable.Range(1, 5).Select(x => new
{
Id = x,
Name = "suburb " + x
});
return Json(suburbs, JsonRequestBehavior.AllowGet);
}
public ActionResult Cities(int provinceId)
{
// TODO: Fetch the cities from your repository based on the provinceId
var cities = Enumerable.Range(1, 5).Select(x => new
{
Id = x,
Name = "city " + x
});
return Json(cities, JsonRequestBehavior.AllowGet);
}
}
最後一個觀點:
@model SomeNs.Models.MyViewModel
@{
ViewBag.Title = "Home Page";
}
<script type="text/javascript" src="/scripts/jquery-1.4.4.js"></script>
<script type="text/javascript">
$(function() {
$('#SelectedProvinceId').change(function() {
var selectedProvinceId = $(this).val();
$.getJSON('@Url.Action("Cities")', { provinceId: selectedProvinceId }, function (cities) {
var citiesSelect = $('#SelectedCityId');
citiesSelect.empty();
$.each(cities, function (index, city) {
citiesSelect.append(
$('<option/>')
.attr('value', city.Id)
.text(city.Name)
);
});
});
});
$('#SelectedCityId').change(function() {
var selectedCityId = $(this).val();
$.getJSON('@Url.Action("Suburbs")', { cityId: selectedCityId }, function (suburbs) {
var suburbsSelect = $('#SelectedSuburbId');
suburbsSelect.empty();
$.each(suburbs, function (index, suburb) {
suburbsSelect.append(
$('<option/>')
.attr('value', suburb.Id)
.text(suburb.Name)
);
});
});
});
});
</script>
<div>
Province:
@Html.DropDownListFor(x => x.SelectedProvinceId, new SelectList(Model.Provinces, "Id", "Name"))
</div>
<div>
City:
@Html.DropDownListFor(x => x.SelectedCityId, Enumerable.Empty<SelectListItem>())
</div>
<div>
Suburb:
@Html.DropDownListFor(x => x.SelectedSuburbId, Enumerable.Empty<SelectListItem>())
</div>
作爲改進的JavaScript代碼可以通過寫一個jQuery縮短插件以避免重複某些部分。
UPDATE:
和談論的一個插件,你可以有在電線之間的東西:
(function ($) {
$.fn.cascade = function (options) {
var defaults = { };
var opts = $.extend(defaults, options);
return this.each(function() {
$(this).change(function() {
var selectedValue = $(this).val();
var params = { };
params[opts.paramName] = selectedValue;
$.getJSON(opts.url, params, function (items) {
opts.childSelect.empty();
$.each(items, function (index, item) {
opts.childSelect.append(
$('<option/>')
.attr('value', item.Id)
.text(item.Name)
);
});
});
});
});
};
})(jQuery);
,然後簡單地連線起來:
$(function() {
$('#SelectedProvinceId').cascade({
url: '@Url.Action("Cities")',
paramName: 'provinceId',
childSelect: $('#SelectedCityId')
});
$('#SelectedCityId').cascade({
url: '@Url.Action("Suburbs")',
paramName: 'cityId',
childSelect: $('#SelectedSuburbId')
});
});
我正要寫一篇評論,內容是「爲什麼要進入這項工作?使用jquery插件」 - 然後我讀了你的最後一句話。 :) +1 – RPM1984 2010-12-16 09:21:33
+只是爲了您的答案的全面性,謝謝。我還沒有在我的代碼中使用它。但它看起來像一個勝利者。 – ProfK 2010-12-16 09:22:32
我只寫了我的javascript函數,然後向下滾動查看函數:/ +1。 – Doomsknight 2011-11-24 16:17:44
知道,該解決方案不適用於EF 4.0。它導致「在序列化...時檢測到循環引用」錯誤。這裏有可能的解決方案http://blogs.telerik.com/atanaskorchev/posts/10-01-25/resolving_circular_references_when_binding_the_mvc_grid.aspx,我用過第二個。
感謝Darin爲您帶來解決方案。它極大地幫助我達到了這一點。但正如'xxviktor'提到的那樣,我確實得到了通告。錯誤。爲了擺脫它,我已經這樣做了。
public string GetCounties(int countryID)
{
List<County> objCounties = new List<County>();
var objResp = _mastRepo.GetCounties(countryID, ref objCounties);
var objRetC = from c in objCounties
select new SelectListItem
{
Text = c.Name,
Value = c.ID.ToString()
};
return new JavaScriptSerializer().Serialize(objRetC);
}
爲了實現自動級聯,我用這種方法稍微擴展了jQuery擴展。
$('#ddlCountry').cascade({
url: '@Url.Action("GetCounties")',
paramName: 'countryID',
childSelect: $('#ddlState'),
childCascade: true
});
而實際的JS使用這個參數如下(在JSON請求內)。
// trigger child change
if (opts.childCascade) {
opts.childSelect.change();
}
希望這可以幫助有類似問題的人。
要實現支持MVC的內置驗證和綁定的級聯下拉列表,您需要做一些與其他答案中所做的不同的事情。
如果你的模型有驗證,這將支持它。從模型的摘錄與驗證:
[Required]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public Guid cityId { get; set; }
在你的控制器,你需要添加一個get方法,讓你的看法以後可以得到相關的數據:現在
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetData(Guid id)
{
var cityList = (from s in db.City where s.stateId == id select new { cityId = s.cityId, name = s.name });
//simply grabbing all of the cities that are in the selected state
return Json(cityList.ToList(), JsonRequestBehavior.AllowGet);
}
,以我提到的查看早期:
在你看來,你有類似的這種兩個下拉菜單:
<div class="editor-label">
@Html.LabelFor(model => model.stateId, "State")
</div>
<div class="editor-field">
@Html.DropDownList("stateId", String.Empty)
@Html.ValidationMessageFor(model => model.stateId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.cityId, "City")
</div>
<div class="editor-field">
@*<select id="cityId"></select>*@
@Html.DropDownList("cityId", String.Empty)
@Html.ValidationMessageFor(model => model.cityId)
</div>
下拉菜單中的內容由控制器綁定,並自動填充。注意:根據我的經驗,刪除此綁定並依靠java腳本來填充下拉菜單會使您失去驗證。此外,我們在這裏綁定的方式在驗證方面很好,所以沒有理由去改變它。
現在到我們的jQuery插件:
(function ($) {
$.fn.cascade = function (secondaryDropDown, actionUrl, stringValueToCompare) {
primaryDropDown = this; //This doesn't necessarily need to be global
globalOptions = new Array(); //This doesn't necessarily need to be global
for (var i = 0; i < secondaryDropDown.options.length; i++) {
globalOptions.push(secondaryDropDown.options[i]);
}
$(primaryDropDown).change(function() {
if ($(primaryDropDown).val() != "") {
$(secondaryDropDown).prop('disabled', false); //Enable the second dropdown if we have an acceptable value
$.ajax({
url: actionUrl,
type: 'GET',
cache: false,
data: { id: $(primaryDropDown).val() },
success: function (result) {
$(secondaryDropDown).empty() //Empty the dropdown so we can re-populate it
var dynamicData = new Array();
for (count = 0; count < result.length; count++) {
dynamicData.push(result[count][stringValueToCompare]);
}
//allow the empty option so the second dropdown will not look odd when empty
dynamicData.push(globalOptions[0].value);
for (var i = 0; i < dynamicData.length; i++) {
for (var j = 0; j < globalOptions.length; j++) {
if (dynamicData[i] == globalOptions[j].value) {
$(secondaryDropDown).append(globalOptions[j]);
break;
}
}
}
},
dataType: 'json',
error: function() { console.log("Error retrieving cascading dropdown data from " + actionUrl); }
});
}
else {
$(secondaryDropDown).prop('disabled', true);
}
secondaryDropDown.selectedindex = 0; //this prevents a previous selection from sticking
});
$(primaryDropDown).change();
};
} (jQuery));
您可以複製上面的jQuery,我創建,爲<script>...</script>
標籤在你看來,或者在一個單獨的腳本文件,如果你希望(注意:我更新,這使它跨瀏覽器,但是我使用的場景不再需要,但它應該可以工作)。
在這些相同的腳本標籤,(不是在一個單獨的文件),您可以通過以下JavaScript調用插件:
$(document).ready(function() {
var primaryDropDown = document.getElementById('stateId');
var secondaryDropdown = document.getElementById('cityId');
var actionUrl = '@Url.Action("GetData")'
$(primaryDropDown).cascade(secondaryDropdown, actionUrl);
});
記得添加$(document).ready
一部分,該頁面必須完全在你面前裝嘗試使下降級聯。
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
//Dropdownlist Selectedchange event
$("#country").change(function() {
$("#State").empty();
$.ajax({
type: 'POST',
url: '@Url.Action("State")', // we are calling json method
dataType: 'json',
data: { id: $("#country").val() },
// here we are get value of selected country and passing same value
success: function (states) {
// states contains the JSON formatted list
// of states passed from the controller
$.each(states, function (i, state) {
$("#State").append('<option value="' + state.Value + '">' +
state.Text + '</option>');
// here we are adding option for States
});
},
error: function (ex) {
alert('Failed to retrieve states.' + ex);
}
});
return false;
})
});
</script>
<div>
@Html.DropDownList("country", ViewBag.country as List<SelectListItem>, "CountryName", new { style = "width: 200px;" })
</div>
<div>
</div>
<div>
@Html.DropDownList("State", ViewBag.country as List<SelectListItem>)
</div>
從控制器我收到值
public async Task<ActionResult> Country()
{
Country co = new Country();
List<SelectListItem> li = new List<SelectListItem>();
li.Add(new SelectListItem { Text = "Select", Value = "0" });
li.Add(new SelectListItem { Text = "India", Value = "1" });
li.Add(new SelectListItem { Text = "Nepal", Value = "2" });
li.Add(new SelectListItem { Text = "USA", Value = "3" });
li.Add(new SelectListItem { Text = "Kenya", Value = "4" }); ;
ViewBag.country= li;
return View();
}
public JsonResult state(string id)
{
List<SelectListItem> states = new List<SelectListItem>();
states.Add(new SelectListItem { Text = "--Select State--", Value = "0" });
switch (id)
{
case "1":
states.Add(new SelectListItem { Text = "MP", Value = "1" });
states.Add(new SelectListItem { Text = "UP", Value = "2" });
break;
case "3":
states.Add(new SelectListItem { Text = "USA1", Value = "3" });
states.Add(new SelectListItem { Text = "USA2", Value = "4" });
break;
}
return Json(new SelectList(states, "Value", "Text", JsonRequestBehavior.AllowGet));
}
我的博客層疊的DropDownList在ASP.Net MVC(http://blogs.msdn.com/b/ rickandy/archive/2012/01/09/cascasding-dropdownlist-in-asp-net-mvc.aspx)完全正確。另請參閱我的教程使用DropDownList Box和jQuery(http://www.asp.net/mvc/tutorials/javascript/working-with-the-dropdownlist-box-and-jquery/using-the-dropdownlist-helper- with-aspnet-mvc) – RickAndMSFT 2012-03-23 17:06:52