【问题标题】:Populating child drop down based on what was selected from parent drop down list根据从父下拉列表中选择的内容填充子下拉列表
【发布时间】:2011-08-08 14:45:30
【问题描述】:

我正在使用最新版本的 jQuery 和 ASP.NET MVC 3 以及 Razor 视图引擎。

我曾尝试 Google 寻找一个在选择父下拉项时加载子下拉列表的体面示例。我希望通过jQuery AJAX 使用JSON 来做到这一点。我对此的了解为零。

我有一个包含类别列表的 Category 类。这是一个父子关联。

如果我从父下拉列表中选择一个类别,则所有子类别都需要在所选父类别的子下拉列表中列出。

这是我目前拥有的,但需要完成它,不确定我是否走对了方向:

$(document).ready(function () {
   $('#ddlParentCategories').change(function () {
      alert('changed');
   });
});

我从我的视图模型中加载了我的下拉列表:

@Html.DropDownListFor(x => x.ParentCategoryId, new SelectList(Model.ParentCategories, "Id", "Name", Model.ParentCategoryId), "-- Select --", new { id = "ddlParentCategories" })

第一项具有文本“-- Select --”(对于父级和子级下拉列表)。在初始页面加载时,必须在子下拉列表中加载任何内容。选择一个值后,必须填充子下拉列表。而当在父下拉列表中再次选择“-- Select--”时,子下拉列表中的所有项目都必须清除,除了“-- Select--”。

如果可能,如果子类别正在加载,我如何显示那个“圆形”加载图标?

更新

我已将我的代码更新为 Darin 的代码,但我无法使其正常工作:

Category类:

public class Category
{
   public int Id { get; set; }
   public string Name { get; set; }
   public string Description { get; set; }
   public string MetaKeywords { get; set; }
   public string MetaDescription { get; set; }
   public bool IsActive { get; set; }
   public int? ParentCategoryId { get; set; }
   public virtual Category ParentCategory { get; set; }
   public virtual ICollection<Category> ChildCategories { get; set; }
}

EditProductViewModel类:

public class EditProductViewModel
{
   public int Id { get; set; }
   public string Name { get; set; }
   public string ShortDescription { get; set; }
   public string LongDescription { get; set; }
   public bool IsActive { get; set; }
   public string PageTitle { get; set; }
   public bool OverridePageTitle { get; set; }
   public string MetaKeywords { get; set; }
   public string MetaDescription { get; set; }
   public int ParentCategoryId { get; set; }
   public IEnumerable<Category> ParentCategories { get; set; }
   public int ChildCategoryId { get; set; }
   public IEnumerable<Category> ChildCategories { get; set; }
}

ProductController类:

public ActionResult Create()
{
   EditProductViewModel viewModel = new EditProductViewModel
   {
      ParentCategories = categoryService.GetParentCategories()
         .Where(x => x.IsActive)
         .OrderBy(x => x.Name),
      ChildCategories = Enumerable.Empty<Category>(),
      IsActive = true
   };

   return View(viewModel);
}

public ActionResult AjaxBindingChildCategories(int parentCategoryId)
{
   IEnumerable<Category> childCategoryList = categoryService.GetChildCategoriesByParentCategoryId(parentCategoryId);

   return Json(childCategoryList, JsonRequestBehavior.AllowGet);
}

Create查看:

<tr>
   <td><label>Parent Category:</label> <span class="red">*</span></td>
   <td>@Html.DropDownListFor(x => x.ParentCategoryId,
         new SelectList(Model.ParentCategories, "Id", "Name", Model.ParentCategoryId),
         "-- Select --",
         new { data_url = Url.Action("AjaxBindingChildCategories"), id = "ParentCategories" }
      )
      @Html.ValidationMessageFor(x => x.ParentCategoryId)
   </td>
</tr>
<tr>
   <td><label>Child Category:</label> <span class="red">*</span></td>
   <td>@Html.DropDownListFor(x => x.ChildCategoryId,
         new SelectList(Model.ChildCategories, "Id", "Name", Model.ChildCategoryId),
         "-- Select --",
         new { id = "ChildCategories" }
      )
      @Html.ValidationMessageFor(x => x.ChildCategoryId)
   </td>
</tr>

<script type="text/javascript">

   $(document).ready(function () {
      $('#ParentCategories').change(function () {
         var url = $(this).data('url');
         var data = { parentCategoryId: $(this).val() };

         $.getJSON(url, data, function (childCategories) {
            var childCategoriesDdl = $('#ChildCategories');
            childCategoriesDdl.empty();

            $.each(childCategories, function (index, childCategory) {
               childCategoriesDdl.append($('<option/>', {
                  value: childCategory, text: childCategory
               }));
            });
         });
      });
   });


</script>

它进入我的 AjaxBindingChildCategories 操作并带回记录,它只是不想显示我的子类别下拉列表。我查看了 Fire Bug,得到的错误是:

GET AjaxBindingChildCategories?parentCategoryId=1

500 Internal Server Error

【问题讨论】:

  • 您所询问的内容称为“级联下拉菜单”。这将在谷歌搜索时有所帮助。
  • 好的,谢谢,让我去进一步搜索:)

标签: ajax asp.net-mvc-3 jquery razor


【解决方案1】:

以下是级联下拉列表的示例。一如既往地从定义视图模型开始:

public class MyViewModel
{
    [DisplayName("Country")]
    [Required]
    public string CountryCode { get; set; }
    public IEnumerable<SelectListItem> Countries { get; set; }

    public string City { get; set; }
    public IEnumerable<SelectListItem> Cities { get; set; }
}

然后是控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            // TODO: Fetch countries from somewhere
            Countries = new[]
            {
                new SelectListItem { Value = "FR", Text = "France" },
                new SelectListItem { Value = "US", Text = "USA" },
            },

            // initially we set the cities ddl to empty
            Cities = Enumerable.Empty<SelectListItem>()
        };
        return View(model);
    }

    public ActionResult Cities(string countryCode)
    {
        // TODO: based on the selected country return the cities:
        var cities = new[]
        {
            "Paris", "Marseille", "Lyon"
        };
        return Json(cities, JsonRequestBehavior.AllowGet);
    }
}

一个视图:

@model MyViewModel

@using (Html.BeginForm())
{
    <div>
        @Html.LabelFor(x => x.CountryCode)
        @Html.DropDownListFor(
            x => x.CountryCode, 
            Model.Countries, 
            "-- Select country --", 
            new { data_url = Url.Action("cities") }
        )
        @Html.ValidationMessageFor(x => x.CountryCode)
    </div>

    <div>
        @Html.LabelFor(x => x.City)
        @Html.DropDownListFor(
            x => x.City, 
            Model.Cities, 
            "-- Select city --"
        )
        @Html.ValidationMessageFor(x => x.City)
    </div>

    <p><input type="submit" value="OK" /></p>
}

最后是不显眼的 javascript 在一个单独的文件中:

$(function () {
    $('#CountryCode').change(function () {
        var url = $(this).data('url'); 
        var data = { countryCode: $(this).val() };
        $.getJSON(url, data, function (cities) {
            var citiesDdl = $('#City');
            citiesDdl.empty();
            $.each(cities, function (index, city) {
                citiesDdl.append($('<option/>', {
                    value: city,
                    text: city
                }));
            });
        });
    });
});

【讨论】:

  • 谢谢达林。我想为我的编辑页面使用相同的代码(这目前用于我的创建页面)。我将如何使用它以便在加载时选择国家和城市?
  • 我实际上有城市 ID 和名称。上面的代码将如何改变以适应这种情况?
  • @Brendan Vogt,您可以将Cities 属性添加到您的视图模型中,该属性的类型为IEnumerable&lt;SelectListItem&gt;,并让您的编辑操作用所选国家/地区的相应城市填充它。然后在编辑视图中使用以下代码生成城市 ddl:@Html.DropDownListFor(x =&gt; x.City, Model.Cities, "-- Select city --")。我已更新我的答案以将此属性包含到视图模型中。
  • 我正在逐行处理您的代码。我刚刚对 url 和 data 变量进行了警告,但它们返回时没有任何内容,这是正确的吗?
  • $.getJSON(url, data, function (cities) { 中的城市是什么?这是如何填充的?
【解决方案2】:

jQuery 脚本将如下所示:

<script type="text/javascript">
function getCities(abbr) {
    $.ajax({
        url: "@Url.Action("Cities", "Locations")",
        data: {abbreviation: abbr},
        dataType: "json",
        type: "POST",
        error: function() {
            alert("An error occurred.");
        },
        success: function(data) {
            var items = "";
            $.each(data, function(i, item) {
                items += "<option value=\"" + item.Value + "\">" + item.Text + "</option>";
            });

            $("#City").html(items);
        }
    });
}

$(document).ready(function(){
    $("#State").change(function() {
        var abbr = $("#State").val();

        getCities(abbr);
    });
});
</script>

检索数据的存储库可能如下所示(但显然将其连接到实时数据):

public class LocationRepository : ILocationRepository
{
    public IQueryable<State> GetStates()
    {
        return new List<State>
        {
            new State { Abbreviation = "NE", Name = "Nebraska" },
            new State { Abbreviation = "NC", Name = "North Carolina" }
        }.AsQueryable();
    }

    public IQueryable<City> GetCities(string abbreviation)
    {
        var cities = new List<City>();

        if (abbreviation == "NE")
        {
            cities.AddRange(new List<City> {
                new City { Id = 1, Name = "Omaha" },
                new City { Id = 2, Name = "Lincoln" }
            });
        }
        else if (abbreviation == "NC")
        {
            cities.AddRange(new List<City> {
                new City { Id = 3, Name = "Charlotte" },
                new City { Id = 4, Name = "Raleigh" }
            });
        }

        return cities.AsQueryable();
    }
}

public interface ILocationRepository
{
    IQueryable<State> GetStates();
    IQueryable<City> GetCities(string abbreviation);
}

控制器可能如下所示:

public class LocationsController : Controller
{
    private ILocationRepository locationRepository = new LocationRepository();

    [HttpPost]
    public ActionResult States()
    {
        var states = locationRepository.GetStates();

        return Json(new SelectList(state, "Id", "Name"));
    } 

     [HttpPost]
    public ActionResult Cities(string abbreviation)
    {
        var cities = locationRepository.GetCities(abbreviation);

        return Json(new SelectList(cities, "Abbreviation", "Name")); 
    }
}

【讨论】:

  • 谢谢,但我可以看看你的 2 个下拉菜单的 HTML 吗?您可以从我的帖子中看到我是如何填充父类别的。您将如何构造子类别,也从视图模型?
  • 请看我更新的帖子。不确定我在这里做错了什么?
【解决方案3】:

我假设您从服务器端填充父下拉列表,并且此父 dd 中的第一个选项是 "--- Select ---"

你可以试试这个

$(document).ready(function () {
   var $parent = $('#ddlParentCategories');

   var $child = $('#ddlChildCategories');
   $child.find("option:gt(0)").remove();

   if(!$parent.children().eq(0).is(":selected")){

     $parent.change(function () {
        $.ajax({
          url: "urlToFetchTheChild",
          data: { categoryId: this.value },
          success: function(data){  
            //The data you send should be a well formed array of json object containing code/value pair of child categories

            for(var i = 0;i<data.length;i++){
               $child.append("<option value='"+ data[i].code +"'>"+ data[i].value +"</option>"); 
            }

          }
        });
     });
   }


});

【讨论】:

  • 请看我更新的帖子。不确定我在这里做错了什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多