16

I have a view in place that contains 2 @DropDownListFor's Helpers:

    @using (Html.BeginForm("CreateOneWayTrip", "Trips"))
    {
        @Html.ValidationSummary(false);
        <fieldset>
            <legend>Enter Your Trip Details</legend>

            <label>Start Point</label>
            @Html.DropDownListFor(m => m.StartPointProvince, (SelectList)ViewBag.Provinces, new { @Id = "province_dll", @class = "form-control" })
            @Html.DropDownListFor(m => m.StartPointCity, (SelectList)ViewBag.Cities, new { @Id = "city_dll", @class = "form-control" })

            <p style="float: none; text-align: center;">
                <button type="submit" value="Create" class="btn btn-info btn-circle btn-lg">
                    <i class="fa fa-check"></i>
                </button>

                <button type="submit" value="Create" class="btn btn-warning btn-circle btn-lg">
                    <i class="fa fa-times"></i>
                </button>
            </p>
        </fieldset>
    }

Here is the temporary model I use to Capture data:

 public class CaptureCreateTrip
 {
    [Required]
    [Display(Name = "Trip ID")]
    public string TripID { get; set; }

    [Required]
    public string StartPointProvince { get; set; }

    [Required]
    public string StartPointCity { get; set; }
}

One of the DropsDownList's are populated when the page is created and the second will be populated based on the option that the user chooses in the first DropDownList. To achieve this, i am using ajax. The javascript to I use looks like this:

$("#province_dll").change(function () {
        $.ajax({
            url: 'getCities/Trips',
            type: 'post',
            data: {
                provinceId: $("#province_dll").val()
            }
        }).done(function (response) {
            $("cities_dll").html(response);
        });
    });

Here is the Controller the AJAX calls:

  [HttpPost]
  public ActionResult getCicites(int provinceId)
  {
      var lstCities = new SelectList(new[] { "City1", "City2", "City3" });

      return Content(String.Join("", lstCities));
  }

Up until this point everything works - When I choose a value in my Province DropDown the javascript event fires and the Controller action does return the select list values to the Cities DropDown, the problem however is that the data(of the formatof the data) that the Action returns is incorrect. I Tested this by creating a Paragraph element and updating it's text with the return value of the ajax call, which is : "System.Web.Mvc.SelectListItemSystem.Web.Mvc.SelectListItemSystem.Web.Mvc.Select‌​‌​ListItem"

*Note: I am new to ajax and in the process of learning Jquery and AJAX.

Marnus Steyn
  • 1,053
  • 2
  • 16
  • 44
  • in your getCities action, can you put "var options = String.Join("", lstCities); return Content(options);" instead of "return Content(String.Join("", lstCities"));" and then check options variable before return to see what it is stores? – Márcio Gonzalez May 06 '15 at 18:24

3 Answers3

39

The reason you are getting a colletion of strings "System.Web.Mvc.SelectListItemSystem" is that var lstCities = new SelectList(new[] { "City1", "City2", "City3" }); returns IEnumerable<SelectListItem> and String.Join("", lstCities) calls the .ToString() method of each SelectListItem item in the collection which returns "System.Web.Mvc.SelectListItemSystem" (not the value of the Text property of SelectListItem)

The best way to populate the second dropdown list is to return json containing the collection of cities and update the DOM in the ajax success callback. There is no reason to create a SelectList - its just unnecessary extra overhead and you returning at least 3 times as much data back to the client as is necessary (the client has no concept of a C# SelectListItem class.

public JsonResult FetchCities(int provinceId) // its a GET, not a POST
{
    // In reality you will do a database query based on the value of provinceId, but based on the code you have shown
    var cities = new List<string>() { "City1", "City2", "City3" });
    return Json(cities, JsonRequestBehavior.AllowGet);
}

Then in the script (not sure why you have modified the default id from id="StartPointProvince" to id="province_dll", but)

var url = '@Url.Action("FetchCities", "Trips")'; // Don't hard code your url's!
var cities = $('#city_dll'); // cache it
$("#province_dll").change(function () {
    var id = $(this).val(); // Use $(this) so you don't traverse the DOM again
    $.getJSON(url, { provinceId: id }, function(response) {
        cities.empty(); // remove any existing options
        $.each(response, function(index, item) {
            cities.append($('<option></option>').text(item));
        });
    });
});

Edit

Further to OP's comments, if the database contained a table name Cities with fields ID and Name, then the controller method would be something like

public JsonResult FetchCities(int provinceId) // its a GET, not a POST
{
    var cities = db.Cities.Select(c => new
    {
      ID = c.ID,
      Text = c.Text
    }
    return Json(cities, JsonRequestBehavior.AllowGet);
}

and the script to create the options would be

$.each(response, function(index, item) { // item is now an object containing properties ID and Text
    cities.append($('<option></option>').text(item.Text).val(item.ID));
});
  • I tried using this javascript, still no dice. I Replaced the #city_dll and #province_dll with StartPointCity and StartPointProvince.Did I miss something? – Marnus Steyn May 07 '15 at 20:53
  • Note `#city_dll` would need to be replaced with `#StartPointCity` if you have removed the `new { id = "city_dll" }`. If its not working, open your browser console and check for errors. Put a breakpoint on the script and step through it to debug and/or add `console.log()` statements to check the value of the variables –  May 07 '15 at 22:32
  • I Changed `#city_dll` to `#StartPointCity` alreay, the problem is, I put a break point in the Controller that should return the `JsonResult` and it's not hitting the breakpoint when I choose a value in the `#StartPointProvince` DropDownList. – Marnus Steyn May 10 '15 at 11:55
  • I have tested the code and it works. Put a break point in the script using your browser tools and step through it. –  May 10 '15 at 12:00
  • I debugged the java script and the server error it produces says that now value is given to the provinceId. So i temporary removed the line `var id = $(this).val();` and hard coded a value and now it works. What do you think might be causing this? – Marnus Steyn May 10 '15 at 12:17
  • You have not shown the html generated by your first dropdownlist so cant be sure, but just check what the output of `var id = $(this).val();` actually is (is it a value that can be parsed to an `int`?) Best to add the generated html for `@Html.DropDownListFor(m => m.StartPointProvince, ..)` in you question to be sure. –  May 10 '15 at 12:22
  • Fixed it! Sorry for the inconvenience, I am new to JQuery and Ajax. Can I also maybe ask that you update your answer to show a simple implementation of the same thing but with items that have both a value and text? Thank you – Marnus Steyn May 10 '15 at 12:36
  • Sure, will update in a few minutes. I'll assume your database has a table named `Cities'` with fields `ID` and `Name` –  May 10 '15 at 12:41
  • 12
    This worked for me, except I had to use: `cities.append($('').text(item.Text).val(item.ID));` – astrosteve Jan 13 '16 at 20:32
  • I had to use obj.append(new Option(item.Text, item.Value)); – tdbeckett Jun 08 '18 at 06:32
5

Can you try the following?

This is a post that i answared some days ago. Font:Dynamic DropDownLists In MVC 4 Form

You should create a ajax call in the change event of the province ddl. This call will request to an action and return the cities of selected province.

$("province_dll").change(function(){
    $.ajax({
         url: 'getCitiesController/getCitiesAction',
         type: 'post',
         data: {
               provinceId: provinceIdVar
         }
    }).done(function(response){
         $("cities_dll").html(response);
    }); 
});

In the action:

[HttpPost]
public ActionResult getCicitesAction(int provinceId)
{
     var cities = db.cities.Where(a => a.provinceId == provinceId).Select(a => "<option value='" + a.cityId + "'>" + a.cityName + "'</option>'";

     return Content(String.Join("", cities));
}
Community
  • 1
  • 1
Márcio Gonzalez
  • 1,020
  • 1
  • 8
  • 20
2

Let's make it more simple

  • Step 1: Server side function / data

    public JsonResult FetchP(string O)
    {
        List<SelectListItem> PList = new List<SelectListItem>();
         PList = _PService.GetAllPActive()
                  .Select(i => new SelectListItem()
                             {
                                 Text = i.PName,
                                 Value = i.PID                                   
                             }).ToList();
    
        return Json(PList, JsonRequestBehavior.AllowGet);
    }
    
  • Step 2 : Client side function / data interpreter

    function FetchP()
    {
        var url = '@Url.Action("FetchP", "Your_Controller")'; 
        var PDD= $("#PDD");
        var ODD= $("#ODD").val(); 
    
        $.getJSON(url, { O: ODD}, function (response) {
            PDD.empty(); 
            debugger;
            $.each(response, function (index, item) {
                debugger;
                var p = new Option(item.Text, item.Value);
                PDD.append(p);
            });
        });
    }
    
  • Step 3 : Call - client side function / start

  • List 1 :

    @Html.DropDownList("ODD", ViewBag.OList as IEnumerable<SelectListItem>, new { @id = "ODD", @class = "form-control", @onchange= "FetchP()" })
    
  • List 2 :

    @Html.DropDownList("PDD", ViewBag.PList as IEnumerable<SelectListItem>,new { @id = "PDD", @class = "form-control"})
    
Alan Ball
  • 593
  • 8
  • 17
Kedar9444
  • 1,745
  • 1
  • 11
  • 15