2

This is the first time im posting a question here. I am new to mvc. I want to develop two cascading dropdown lists. I am using mvc4. Here what i have done.

Factory class

public class Factory
{
    [Key]
    public int FactoryId { get; set; }

    [Required(ErrorMessage = "Required")]
    public string FactoryCode { get; set; }

    [Required(ErrorMessage = "Required")]
    public string FactoryName { get; set; }

    public int City { get; set; }

    public int Country { get; set; }

I have Country and City classes separately with a foreign key relationship

City class

public  class City
{

   [Key]
   public int CityId { get; set; }

   [Required(ErrorMessage = "Required")]
   public string CityCode { get; set; }

   [Required(ErrorMessage = "Required")]
   public string CityName { get; set; }

   [ForeignKey("Country")]
   public int CountryId { get; set; }
   public virtual Country Country { get; set; }
}

Country class

public class Country
{
    [Key]
    public int CountryId { get; set; }

    [Required(ErrorMessage = "Required")]
    public string CountryCode { get; set; }

    [Required(ErrorMessage = "Required")]
    public string CountryName { get; set; }

    public virtual List<City> cities { get; set; }
}

Factory controller

public ActionResult FactoryIndex(string A, int? Id, string sortOrder, string currentFilter, string searchString, int? page)
    {
        var objContext = new KnittingdbContext();

        if (A == "New")
        {
            var model = new Factory();
            ViewData["mdl"] = model;
            ViewBag.CountryList = objContext.Countries;
            ViewBag.Module = A;


        }
.....................

Rendering the partialview within the IndexView

@if (ViewBag.Module != null)
    {
        if (ViewBag.Module == "New")
        {
            Html.RenderPartial("~/Areas/Masters/Views/Factory/_FactoryCreate.cshtml", ViewData["mdl"]);
        }

_FactoryCreate partial view

@Html.DropDownListFor(a=>a.Country,new SelectList(ViewBag.CountryList, "CountryId", "CountryName"), "Select country", new {id="CountryId", @class = "form-control"})

@Html.DropDownListFor(a => a.City; a.City, new SelectList(Enumerable.Empty<SelectListItem>(), "CityId", "CityName"), "Select city", new {@class = "form-control" })

jquery script within the partialview

$(function() {

            $('#CountryId').change(function () {
                $.ajax({
                    url: '/Factory/FillCity/',
                    type: "GET",
                    dataType: "JSON",
                    data: { Country: countryId },
                    success: function (cities) {
                        $("#City").html("");
                        $.each(cities, function (i, city) {
                            $("#City").append(
                                $('<option></option>').val(city.CityId).html(city.CityName));
                        });
                    }
                });
            })
        })

ActionResult in FactoryController that fetches the relevant City data

public ActionResult FillCity(int country)
{
    var db = new KnittingdbContext();
    var cities = db.Cities.Where(c =>c.CountryId == country);
    return Json(cities, JsonRequestBehavior.AllowGet);
}

Country dropdownlist is working. But the City DDL is not working. The data doesn't get binded. When the CountryId gets changed it comes to jquery script. i could recognize it using a Alert. But after the URL property in script it doesn't work.

Success method is not getting executed. I think the issue is with the way i present the url. I tried it with different ways. But still couldn't get it solved. These views and controllers are in a Area.

Please help me. Thanks in advance!

11
  • You never declare or assign a value to countryIdso country in the controller would be null. Commented Aug 15, 2015 at 5:07
  • Firstly thank you very much for the reply. As you said i tried to assign a value to countryId. I did it like this. data{Country:country}. But it didn't work. Then i changed it to data{Country:countryId=CountryId}. But Still doesn't work. I thought that the countryId get assigned by a value after the first ddl value got changed while selecting an item from ddl and then it is sent to the controller method as a parameter. Pls help.. Commented Aug 15, 2015 at 6:08
  • You have a few other issues in your code as well. Can you post the model for City (as returned by db.Cities) - may as well fix them all for you :) Commented Aug 15, 2015 at 6:17
  • Ok as you said i added the City and the Country classes above and have improved the code :) Commented Aug 15, 2015 at 6:42
  • Your parameter Country in data and the parameter you passed in FillCity does not match.One has capitalized character.Is it intentional? Commented Aug 15, 2015 at 6:47

1 Answer 1

1

Your not passing the selected value of the first dropdown to your controller method so it would throw an exception (country is typeof int and cannot be null). Your script should be (note its unclear why you would change the id attribute from id="Country" to id="CountryId" by using new { id="CountryId" } so this example uses the default

var cityDropdown = $("#City"); // cache it to avoid repeatedly searching the DOM
$('#Country').change(function () {
  $.ajax({
    url: '@Url.Action("FillCity", "Factory")', // dont hard code your url's
    type: "GET",
    dataType: "JSON",
    data: { Country: $(this).val() }, // pass the selected value
    success: function (cities) {
        cityDropdown.empty();
        $.each(cities, function (i, city) {
            cityDropdown.append($('<option></option>').val(city.CityId).html(city.CityName));
        });
    }
});

of more simply

$.getJSON('@Url.Action("FillCity", "Factory")', { Country: $(this).val() }, function(cities){
    cityDropdown.empty();
    ... 
});

Note also your method is serializing all properties of typeof City including CityCode, CountryId and Country (and all the properries of Country) which you never use so this is a waste of bandwidth and just degrades performance (and has the potential to cause a circular reference exception). Change your method to

public JsonResult FillCity(int country)
{
    var db = new KnittingdbContext();
    var cities = db.Cities.Where(c => c.CountryId == country).Select(c => new
    {
      CityId = c.CityId,
      CityName = c.CityName
    };
    return Json(cities, JsonRequestBehavior.AllowGet);
}

Note also there are other issues with your code, for example if you have validation errors and return the view in the POST method, the city dropdown list will not be populated, forcing the user to re-select it all over again. Suggest you start using view models and review the code in this DotNetFiddle

Sign up to request clarification or add additional context in comments.

8 Comments

thanks :) I am going to try it in your way. Once im done i will let you know the result.
ok i have put my working solution below. The issue was with the script i suppose. Finally i got it worked. You gave me some good tips. Thanks again:)
@ Stephen Muecke, I had to change the script, as an ex type: 'POST' instead of 'GET' and i had to change parameter type of FillCity to string. Otherwise it gave me internal 500 error. I found these things considering your instructions. I thought to help someone new to this like me with the little knowledge i gained up to now. That's why i put that answer.
You should not be changing it to POST (because your not changing data so it should be a GET). And you don't need to change it to string because property CountryId is type int so clearly you still have numerous thing to learn or are still making mistakes. And your code still shows you return a SelectList which is pointless extra overhead. And your script has logic errors, e.g. how could if (countrySelected == "") ever be entered - because in the preceding line your set it to 0 if countrySelected == ""!
ok i got those points now. Thank you. After some research i changed the solution because i couldn't get it worked. All your instructions are valuable for me. i ll develop it again considering your points and let you know the result.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.