Aaron Davis
Aaron Davis

Reputation: 281

MVC3 AJAX Cascading DropDownLists

I am having a hard time figuring out how to get cascading drop down lists to work for my asp.net mvc3 application. I have a popup box and I would like to display 2 dropdownlists, the 2nd being populated based on what is selected in the first. Each time I run the application the controller method returns the correct list of values, but instead of hitting the success part of the ajax call I hit the error part. I have done lots of research and followed several examples I have found but something is still not quite right, any help would be greatly appreciated.

Edit: Further inspection using firebug shows an error 500 internal server error which states: Exception Details: System.InvalidOperationException: A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.GameEdition

I have the following jQuery / AJAX:

<script type="text/javascript">
$(function () {
    $("#PlatformDropDownList").change(function () {
        var gameId = '@Model.GameID';
        var platformId = $(this).val();
        // and send it as AJAX request to the newly created action 
        $.ajax({
            url: '@Url.Action("Editions")',
            type: 'GET',
            data: { gameId: gameId, platformId: platformId },
            cache: 'false',
            success: function (result) {
                $('#EditionDropDownList').empty();
                // when the AJAX succeeds refresh the ddl container with 
                // the partial HTML returned by the PopulatePurchaseGameLists controller action 
                $.each(result, function (result) {
                    $('#EditionDropDownList').append(
                        $('<option/>')
                            .attr('value', this.EditionID)
                            .text(this.EditionName)
                    );

                });
            },
            error: function (result) {
                alert('An Error has occurred');
            }
        });
    });
});

Here is my controller method:

  public JsonResult Editions(Guid platformId, Guid gameId)
  {
     //IEnumerable<GameEdition> editions = GameQuery.GetGameEditionsByGameAndGamePlatform(gameId, platformId);
     var editions = ugdb.Games.Find(gameId).GameEditions.Where(e => e.PlatformID == platformId).ToArray<GameEdition>();

     return Json(editions, JsonRequestBehavior.AllowGet);
  }

Here is my web form html:

<div id="PurchaseGame">
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true, "Please correct the errors and try again.")
    <div>
        <fieldset>
            <legend></legend>
            <p>Select the platform you would like to purchase the game for and the version of the game you would like to purchase.</p>

            <div class="editor-label">
                @Html.LabelFor(model => model.PlatformID, "Game Platform")
            </div>
            <div class="editor-field">
                @Html.DropDownListFor(model => model.PlatformID, new SelectList(Model.Platforms, "GamePlatformID", "GamePlatformName"), new { id = "PlatformDropDownList", name="PlatformDropDownList" })
            </div>

            <div class="editor-label">
                @Html.LabelFor(model => model.EditionID, "Game Edition")
            </div>
            <div id="EditionDropDownListContainer">
                @Html.DropDownListFor(model => model.EditionID, new SelectList(Model.Editions, "EditionID", "EditionName"), new { id = "EditionDropDownList", name = "EditionDropDownList" })
            </div>

            @Html.HiddenFor(model => model.GameID)
            @Html.HiddenFor(model => model.Platforms)

            <p>
                <input type="submit" name="submitButton" value="Purchase Game" />
                <input type="submit" name="submitButton" value="Cancel" />
            </p>

        </fieldset>
    </div>
}

Upvotes: 3

Views: 4078

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038710

You cannot send JSON encoded requests using the GET verb. So replace type: 'GET' with type: 'POST' and it will work. Also since you have specified a JSON request you must, well, send a JSON request which is achieved with the JSON.stringify function: data: JSON.stringify({ gameId: gameId, platformId: platformId }),. But since you only have 2 values I think that using GET would be easier. So my recommendation is to remove the contentType: 'application/json' parameter and have your AJAX request look like this:

$.ajax({
    url: '@Url.Action("Editions")',
    type: 'GET',
    data: { gameId: gameId, platformId: platformId },
    cache: 'false',
    success: function (result) {
        $('#EditionDropDownList').empty();
        // when the AJAX succeeds refresh the ddl container with 
        // the partial HTML returned by the PopulatePurchaseGameLists controller action 
        if(result.length > 0)
        {
            $.each(result, function (result) {
                $('#EditionDropDownList').append(
                    $('<option/>')
                         .attr('value', this.EditionID)
                         .text(this.EditionName)
                );
            });
        }
        else
        {
            $('#EditionDropDownList').append(
                $('<option/>')
                    .attr('value', "")
                    .text("No edition found for this game")
            );
        }

    },
    error: function () {
        alert('An Error has occured');
    }
});

Also in the DropDownListFor helper in your Razor markup I notice the following:

onchange = "Model.PlatformID = this.value;"

All I can say is that this doesn't do what you might think it does.


UPDATE:

It seems that you are getting a circular object reference error because you are passing your editions domain model to the Json method. Circular reference object hierarchies cannot be JSON serialized. Besides you don't need to waste the bandwidth by sending all the crap contained in this editions to the client. All your client needs is a collection of ids and names. So simply use view models:

public ActionResult Editions(Guid platformId, Guid gameId)
{
    var editions = ugdb
        .Games
        .Find(gameId)
        .GameEditions
        .Where(e => e.PlatformID == platformId)
        .ToArray<GameEdition>()
        .Select(x => new 
        {
            EditionID = x.EditionID,
            EditionName = x.EditionName
        })
        .ToArray();

    return Json(editions, JsonRequestBehavior.AllowGet);
}

Upvotes: 4

Related Questions