14

I've got a cascading drop-drown using mvc. Something like, if you select a country in the first-dropdown, the states of that country in the second one should be populated accordingly.

At the moment, all seems fine and I'm getting Json response (saw it using F12 tools), and it looks something like [{ "stateId":"01", "StateName": "arizona" } , { "stateId" : "02", "StateName":"California" }, etc..] ..

I'd like to know how to populate my second-dropdown with this data. My second drop-down's id is "StateID". Any help would be greatly appreciated.

Below is the code used to produce the JSON Response from the server:

[HttpPost]
public JsonResult GetStates(string CountryID)
{
    using (mdb)
    {
        var statesResults = from q in mdb.GetStates(CountryID)
                        select new Models.StatesDTO
                        {
                            StateID = q.StateID,
                            StateName = q.StateName
                        };

        locations.statesList = stateResults.ToList();
    }

    JsonResult result = new JsonResult();

    result.Data = locations.statesList;

    return result;
}

Below is the client-side HTML, my razor-code and my script. I want to write some code inside "success:" so that it populates the States dropdown with the JSON data.

<script type="text/javascript">
    $(function () {
        $("select#CountryID").change(function (evt) {

            if ($("select#CountryID").val() != "-1") {

                $.ajax({
                    url: "/Home/GetStates",
                    type: 'POST',
                    data: { CountryID: $("select#CountryID").val() },
                    success: function () { alert("Data retrieval successful"); },
                    error: function (xhr) { alert("Something seems Wrong"); }
                });
            }
        });
    });
</script> 
np_6
  • 514
  • 1
  • 6
  • 19
Ren
  • 1,493
  • 6
  • 31
  • 56

8 Answers8

18

To begin with, inside a jQuery event handler function this refers to the element that triggered the event, so you can replace the additional calls to $("select#CountryID") with $(this). Though where possible you should access element properties directly, rather than using the jQuery functions, so you could simply do this.value rather than $(this).val() or $("select#CountryID").val().

Then, inside your AJAX calls success function, you need to create a series of <option> elements. That can be done using the base jQuery() function (or $() for short). That would look something like this:

$.ajax({
    success: function(states) {
        // states is your JSON array
        var $select = $('#StateID');
        $.each(states, function(i, state) {
            $('<option>', {
                value: state.stateId
            }).html(state.StateName).appendTo($select);
        });
    }
});

Here's a jsFiddle demo.

Relevant jQuery docs:

Anthony Grist
  • 38,173
  • 8
  • 62
  • 76
  • Each iterates over states (the first parameter). States is passed in to the success function. States is the data returned from the server. – Tim Hobbs Jan 15 '13 at 15:03
  • @user1980311 `$("this")` is incorrect - that would pass the string "this" to the function, you actually want to pass what `this` refers to, so do away with the double-quotes; though, as I said, using `this.value` is preferable (it's easier to type, easier to read, eliminates calls to jQuery functions). – Anthony Grist Jan 15 '13 at 15:47
  • In regard to the `$.each()` call: The anonymous function that's being assigned as the `success` function has a single parameter, named `states`. When jQuery successfully receives a response from the server, it passes whatever it received to that function as its argument. `states` is just a name that's been given so that response (in this case an array) can be referenced inside the function, it could in theory be called almost anything at all. – Anthony Grist Jan 15 '13 at 15:50
  • Uncaught TypeError: Cannot read property 'offsetTop' of undefined in jquery.selectric.js. Seems to load the dom faster than the ajax query – Jurgen Cuschieri Apr 05 '18 at 00:01
13

In my project i am doing like this it's below

iN MY Controller

public JsonResult State(int countryId)
{               
    var stateList = CityRepository.GetList(countryId);
    return Json(stateList, JsonRequestBehavior.AllowGet);
}

In Model

public IQueryable<Models.State> GetList(int CountryID)
{

    var statelist = db.States.Where(x => x.CountryID == CountryID).ToList().Select(item => new State
    {
        ID = item.ID,
        StateName = item.StateName
    }).AsQueryable();
    return statelist;
}

In view

<script type="text/javascript">
   function cascadingdropdown() {
       $("#stateID").empty();
       $("#stateID").append("<option value='0'>--Select State--</option>");
       var countryID = $('#countryID').val();
       var Url="@Url.Content("~/City/State")";
       $.ajax({
           url:Url,
           dataType: 'json',
           data: { countryId: countryID },
           success: function (data) {                
               $("#stateID").empty();
               $("#stateID").append("<option value='0'>--Select State--</option>");
               $.each(data, function (index, optiondata) {                  
                   $("#stateID").append("<option value='" + optiondata.ID + "'>" + optiondata.StateName + "</option>");
               });
           }
       });
   }     
</script>

i think this will help you......

np_6
  • 514
  • 1
  • 6
  • 19
Rajpurohit
  • 1,951
  • 2
  • 16
  • 19
4

Step 1:

  • At very first, we need a model class that defines properties for storing data.

    public class ApplicationForm
    {
        public string Name { get; set; }
        public string State { get; set; }
        public string District { get; set; }
    }
    

    Step 2:

  • Now, we need an initial controller that will return an Index view by packing list of states in ViewBag.StateName.

    public ActionResult Index()
    {
        List<SelectListItem> state = new List<SelectListItem>();
        state.Add(new SelectListItem { Text = "Bihar", Value = "Bihar" });
        state.Add(new SelectListItem { Text = "Jharkhand", Value = "Jharkhand" });
        ViewBag.StateName = new SelectList(state, "Value", "Text");
    
        return View();
    }
    

    In above controller we have a List containing states attached to ViewBag.StateName. We could get list of states form database using Linq query or something and pack it to ViewBag.StateName, well let’s go with in-memory data.

    Step 3:

  • Once we have controller we can add its view and start creating a Razor form.

      @Html.ValidationSummary("Please correct the errors and try again.")
    
      @using (Html.BeginForm())
         {
         <fieldset>
        <legend>DropDownList</legend>
        @Html.Label("Name")
        @Html.TextBox("Name")
        @Html.ValidationMessage("Name", "*")
    
        @Html.Label("State")
        @Html.DropDownList("State", ViewBag.StateName as SelectList, "Select a State", new { id = "State" })
        @Html.ValidationMessage("State", "*")
    
        @Html.Label("District")
        <select id="District" name="District"></select>
        @Html.ValidationMessage("District", "*")
    
        <p>
            <input type="submit" value="Create" id="SubmitId" />
        </p>
    </fieldset>
    }
    

    You can see I have added proper labels and validation fields with each input controls (two DropDownList and one TextBox) and also a validation summery at the top. Notice, I have used which is HTML instead of Razor helper this is because when we make JSON call using jQuery will return HTML markup of pre-populated option tag. Now, let’s add jQuery code in the above view page.

    Step 4:

    Here is the jQuery code making JSON call to DDL named controller’s DistrictList method with a parameter (which is selected state name). DistrictList method will returns JSON data. With the returned JSON data we are building tag HTML markup and attaching this HTML markup to ‘District’ which is DOM control.

      @Scripts.Render("~/bundles/jquery")
        <script type="text/jscript">
           $(function () {
            $('#State').change(function () {
                $.getJSON('/DDL/DistrictList/' + $('#State').val(),         function (data) {
                    var items = '<option>Select a District</option>';
                    $.each(data, function (i, district) {
                        items += "<option value='" + district.Value + "'>" + district.Text + "</option>";
                    });
                    $('#District').html(items);
                });
               });
            });
        </script>
    

    Please make sure you are using jQuery library references before the tag.

    Step 5:

  • In above jQuery code we are making a JSON call to DDL named controller’s DistrictList method with a parameter. Here is the DistrictList method code which will return JSON data.

    public JsonResult DistrictList(string Id)
    {
        var district = from s in District.GetDistrict()
                        where s.StateName == Id
                        select s;
    
        return Json(new SelectList(district.ToArray(), "StateName", "DistrictName"), JsonRequestBehavior.AllowGet);
    }
    

    Please note, DistrictList method will accept an ‘Id’ (it should be 'Id' always) parameter of string type sent by the jQuery JSON call. Inside the method, I am using ‘Id’ parameter in linq query to get list of matching districts and conceptually, in the list of district data there should be a state field. Also note, in the linq query I am making a method call District.GetDistrict().

    Step 6:

    In above District.GetDistrict() method call, District is a model which has a GetDistrict() method. And I am using GetDistrict() method in linq query, so this method should be of type IQueryable. Here is the model code.

    public class District
    {
        public string StateName { get; set; }
        public string DistrictName { get; set; }
    
        public static IQueryable<District> GetDistrict()
        {
            return new List<District>
            {
                new District { StateName = "Bihar", DistrictName = "Motihari" },
                new District { StateName = "Bihar", DistrictName = "Muzaffarpur" },
                new District { StateName = "Bihar", DistrictName = "Patna" },
                new District { StateName = "Jharkhand", DistrictName = "Bokaro" },
                new District { StateName = "Jharkhand", DistrictName = "Ranchi" },
            }.AsQueryable();
        }
    }
    

    Step 7:

    You can run the application here because cascading dropdownlist is ready now. I am going to do some validation works when user clicks the submit button. So, I will add another action result of POST version.

    [HttpPost]
    public ActionResult Index(ApplicationForm formdata)
    {
        if (formdata.Name == null)
        {
            ModelState.AddModelError("Name", "Name is required field.");
        }
        if (formdata.State == null)
        {
            ModelState.AddModelError("State", "State is required field.");
        }
        if (formdata.District == null)
        {
            ModelState.AddModelError("District", "District is required field.");
        }
    
        if (!ModelState.IsValid)
        {
            //Populate the list again
            List<SelectListItem> state = new List<SelectListItem>();
            state.Add(new SelectListItem { Text = "Bihar", Value = "Bihar" });
            state.Add(new SelectListItem { Text = "Jharkhand", Value = "Jharkhand" });
            ViewBag.StateName = new SelectList(state, "Value", "Text");
    
            return View("Index");
        }
    
        //TODO: Database Insertion
    
        return RedirectToAction("Index", "Home");
    }
    
Zied R.
  • 4,964
  • 2
  • 36
  • 67
kavitha Reddy
  • 3,303
  • 24
  • 14
1

Try this inside the ajax call:

$.ajax({
  url: "/Home/GetStates",
  type: 'POST',
  data: {
    CountryID: $("select#CountryID").val()
  },
  success: function (data) {
    alert("Data retrieval successful");
    var items = "";

    $.each(data, function (i, val) {
      items += "<option value='" + val.stateId + "'>" + val.StateName + "</option>";
    });

    $("select#StateID").empty().html(items);
  },
  error: function (xhr) {
    alert("Something seems Wrong");
  }
});

EDIT 1

success: function (data) {

  $.each(data, function (i, val) {
    $('select#StateID').append(
    $("<option></option>")
      .attr("value", val.stateId)
      .text(val.StateName));
  });
},
palaѕн
  • 72,112
  • 17
  • 116
  • 136
  • Thanks for the response. It's working now, but would you recommend using html tags within jquery ? are there any other better ways of doing it using the similar method you've followed ? - Thanks – Ren Jan 15 '13 at 14:38
  • @user1980311: Yes, sure there is better way. Try the updated code. – palaѕн Jan 15 '13 at 14:54
  • cool. I will try this one now. Just wondering, Can I use the similar approach to update a
    or a

    , or may be an accordion in Jquery...?..-Thanks

    – Ren Jan 15 '13 at 15:19
  • Yes, inside the each loop you can just append the json content to the `
    ` like http://jsfiddle.net/tnFzL/
    – palaѕн Jan 15 '13 at 15:26
0
<script type="text/javascript">
   $(document).ready(function () {
       $("#ddlStateId").change(function () {
           var url = '@Url.Content("~/")' + "Home/Cities_SelectedState";
           var ddlsource = "#ddlStateId";
           var ddltarget = "#ddlCityId";
           $.getJSON(url, { Sel_StateName: $(ddlsource).val() }, function (data) {
               $(ddltarget).empty();
               $.each(data, function (index, optionData) {
                   $(ddltarget).append("<option value='" + optionData.Text + "'>" + optionData.Value + "</option>");
               });
   
           });
       });
   });
</script>
np_6
  • 514
  • 1
  • 6
  • 19
kavitha Reddy
  • 3,303
  • 24
  • 14
0

I know this post is a year old but I found it and so might you. I use the following solution and it works very well. Strong typed without the need to write a single line of Javascript.

mvc4ajaxdropdownlist.codeplex.com

You can download it via Visual Studio as a NuGet package.

0

You should consider using some client-side view engine that binds a model (in your case JSON returned from API) to template (HTML code for SELECT). Angular and React might be to complex for this use case, but JQuery view engine enables you to easily load JSON model into template using MVC-like approach:

<script type="text/javascript">
    $(function () {
        $("select#CountryID").change(function (evt) {

            if ($("select#CountryID").val() != "-1") {

                $.ajax({
                    url: "/Home/GetStates",
                    type: 'POST',
                    data: { CountryID: $("select#CountryID").val() },
                    success: function (response) {
                             $("#stateID").empty();
                             $("#stateID").view(response);
                    },
                    error: function (xhr) { alert("Something seems Wrong"); }
                });
            }
        });
    });
</script> 

It is much cleaner that generating raw HTML in JavaScript. See details here: https://jocapc.github.io/jquery-view-engine/docs/ajax-dropdown

Jovan MSFT
  • 13,232
  • 4
  • 40
  • 55
0

Try this:

public JsonResult getdist(int stateid)
{
    var res = objdal.getddl(7, stateid).Select(m => new SelectListItem { Text = m.Name, Value = m.Id.ToString() });
    return Json(res,JsonRequestBehavior.AllowGet);
}
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
Meera
  • 1
  • 3