开发者

Populating child drop down based on what was selected from parent drop down list

开发者 https://www.devze.com 2023-03-27 00:01 出处:网络
I am using the latest version of jQuery and ASP.NET MVC 3 with the Razor view engine. I have tried Google looking for a decent example of loading a child drop down list when a parent drop down item i

I am using the latest version of jQuery and ASP.NET MVC 3 with the Razor view engine.

I have tried Google looking for a decent example of loading a child drop down list when a parent drop down item is selected. I am looking to do this via jQuery AJAX using JSON. My knowledge of this is zero.

I have a Category class with a list of 开发者_Python百科categories. It's a parent-child association.

If I select a category from the parent drop down list, then all the child categories need to be listed in the child drop down list for the selected parent category.

This is what I currently have, but need to complete it, not sure if I am in the right direction:

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

I loaded my drop down list from my view model as such:

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

The first item has text "-- Select --" (for both parent and child drop down lists). On initial page load nothing must be loaded in the child drop down list. When a value is selected then the child drop down list must be populated. And when "-- Select --" is selected again in the parent drop down list then all the items in the child drop down list must cleared except "-- Select --".

If possible, if the child categories is loading, how do I display that "round" loading icon?

UPDATE

I have updated my code to Darin's code, and I cannot get it to work properly:

Category class:

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 class:

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 class:

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 view:

<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>

It goes into my AjaxBindingChildCategories action and it brings back records, it just doesn't want to display my child category dropdownlist. I had a look in Fire Bug and the error that I get is:

GET AjaxBindingChildCategories?parentCategoryId=1

500 Internal Server Error


Here's an example of cascading drop down lists. As always start by defining a view model:

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; }
}

then a controller:

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);
    }
}

a view:

@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>
}

and finally the unobtrusive javascript in a separate file:

$(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
                }));
            });
        });
    });
});


jQuery script will look like this:

<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>

A repository to retrieve data could look like this (obviously hook it up to live data though):

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);
}

Controllers could look like this:

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")); 
    }
}


I am assuming you populate the parent dropdown from server side and also the first option in this parent dd is "--- Select ---"

You can try this

$(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>"); 
            }

          }
        });
     });
   }


});
0

精彩评论

暂无评论...
验证码 换一张
取 消