0

I am trying to use a solution for a cascading dropdownlist (the selection in one dropdownlist determines the options in the second one) found here:

http://www.pieterg.com/post/2010/04/12/Cascading-DropDownList-with-ASPNET-MVC-and-JQuery.aspx

<script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            //Hook onto the MakeID list's onchange event
            $("#CustomerId").change(function () {
                //build the request url
                var url = "Timesheet/Tasks";
                //fire off the request, passing it the id which is the MakeID's selected item value
                $.getJSON(url, { id: $("#CustomerId").val() }, function (data) {
                    //Clear the Model list
                    $("#TaskId").empty();
                    //Foreach Model in the list, add a model option from the data returned
                    $.each(data, function (index, optionData) {
                        $("#TaskId").append("<option value='" + optionData.ID + "'>" + optionData.Description + "</option>");
                    });
                });
            }).change();
        });
    </script>
    <h2>
        Index</h2>
    <fieldset>
        <legend>Fields</legend>
        <div>
            <label for="Customers">
                Kund:</label>
            <%:Html.DropDownListFor(m => m.Customers, new SelectList(Model.Customers,"Id", "Name"), new {@id="CustomerId"}) %>
            <%--<%:Html.DropDownListFor(m => m.CustomerId, new SelectList(Model.Customers,"Id", "Name")) %>--%>
            &nbsp;&nbsp;
            <label for="Tasks">
                Aktiviteter:</label>
            <%:Html.DropDownListFor(m => m.Tasks, new SelectList(Model.Tasks,"Id", "Name"), new {@id="TaskId"}) %>
            <%--<%:Html.DropDownListFor(m => m.TaskId, new SelectList(Model.Tasks,"Id", "Name")) %>--%>
<%--            <select id="TaskId" name="TaskId">
            </select>--%>
        </div>
    </fieldset>

And here's the Action method that gets the tasks:

    public JsonResult Tasks(string id)
    {
        var result = new JsonResult();
        //var tasks = from t in _model.Tasks
        //            where t.CustomerId.ToString() == id
        //            select t;
        List<Task> tasklist = new List<Task>();
        foreach (var task in _model.Tasks)
        {
            if(task.CustomerId.ToString() == id)
                tasklist.Add(task);
        }

        //result.Data = tasks.ToList();
        result.Data = tasklist;

        result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
        return result;
    }

I have two problems:

  1. The second dropdownlist does not change at all depending on the first one. I have tried all kinds of variants (as you can see from the commented out dropdownlists). I do get an error message in Firebug, but I can't interpret it (see below).
  2. The linq expression in the Action method doesn't seem to work for some reason, which is really strange, because I've been using this sort of expression a lot without problems before. Maybe I'm tired and missing something, but I can't see it. The expression returns all tasks, not just the ones with the same id as the current customer. The foreach works fine, which makes it even more strange... This is completely unrelated to the first problem, because again, the foreach works fine and only selects three tasks (which is correct). But still the second dropdownlist shows all tasks...

Any help appreciated!

Firebug error log:

<html>
    <head>
        <title>A circular reference was detected while serializing an object of type 'Tidrapportering.Models.Task'.</title>
        <style>
         body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;} 
         p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
         b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
         H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
         H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
         pre {font-family:"Lucida Console";font-size: .9em}
         .marker {font-weight: bold; color: black;text-decoration: none;}
         .version {color: gray;}
         .error {margin-bottom: 10px;}
         .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
        </style>
    </head>

    <body bgcolor="white">

            <span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>

            <h2> <i>A circular reference was detected while serializing an object of type 'Tidrapportering.Models.Task'.</i> </h2></span>

            <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">

            <b> Description: </b>An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

            <br><br>

            <b> Exception Details: </b>System.InvalidOperationException: A circular reference was detected while serializing an object of type 'Tidrapportering.Models.Task'.<br><br>

            <b>Source Error:</b> <br><br>

            <table width=100% bgcolor="#ffffcc">
               <tr>
                  <td>
                      <code>

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.</code>

                  </td>
               </tr>
            </table>

            <br>

            <b>Stack Trace:</b> <br><br>

            <table width=100% bgcolor="#ffffcc">
               <tr>
                  <td>
                      <code><pre>

[InvalidOperationException: A circular reference was detected while serializing an object of type &#39;Tidrapportering.Models.Task&#39;.]
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +1478
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +194
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeEnumerable(IEnumerable enumerable, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +126
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +1311
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +194
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeCustomObject(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +502
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +1355
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +194
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeCustomObject(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +502
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +1355
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +194
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeEnumerable(IEnumerable enumerable, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +126
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +1311
   System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat) +194
   System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj, StringBuilder output, SerializationFormat serializationFormat) +26
   System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj, SerializationFormat serializationFormat) +74
   System.Web.Script.Serialization.JavaScriptSerializer.Serialize(Object obj) +6
   System.Web.Mvc.JsonResult.ExecuteResult(ControllerContext context) +458
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +39
   System.Web.Mvc.&lt;&gt;c__DisplayClass14.&lt;InvokeActionResultWithFilters&gt;b__11() +60
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +391
   System.Web.Mvc.&lt;&gt;c__DisplayClass16.&lt;InvokeActionResultWithFilters&gt;b__13() +61
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +285
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +830
   System.Web.Mvc.Controller.ExecuteCore() +136
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +111
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +39
   System.Web.Mvc.&lt;&gt;c__DisplayClass8.&lt;BeginProcessRequest&gt;b__4() +65
   System.Web.Mvc.Async.&lt;&gt;c__DisplayClass1.&lt;MakeVoidDelegate&gt;b__0() +44
   System.Web.Mvc.Async.&lt;&gt;c__DisplayClass8`1.&lt;BeginSynchronous&gt;b__7(IAsyncResult _) +42

   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +141
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +54
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +52
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8841105

   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&amp; completedSynchronously) +184
</pre></code>

                  </td>
               </tr>
            </table>

            <br>

            <hr width=100% size=1 color=silver>

            <b>Version Information:</b>&nbsp;Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1

            </font>

    </body>
</html>

UPDATE:

I now updated it to the following:

Action method for getting tasks associated with the selected customer:

    public JsonResult Tasks(string id)
    {
        var result = new JsonResult();
        var tasks =
            _model.Tasks.Where(task => task.CustomerId.ToString() == id).Select(
                task => new {ID = task.Id, Name = task.Name});
        result.Data = tasks;

        result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
        return result;
    }

And in the View:

    <div>
        <label for="Customers">
            Kund:</label>
        <%:Html.DropDownListFor(m => m.Customers, new SelectList(Model.Customers,"Id", "Name"), new {@id="CustomerId"}) %>
        &nbsp;&nbsp;
        <label for="Tasks">
            Aktiviteter:</label>
        <%:Html.DropDownListFor(m => m.Tasks, new SelectList(Model.Tasks,"Id", "Name"), new {@id="TaskId"}) %>
    </div>

Now I get results somehow filtered and selected in the second dropdown, the number of options are correct when selecting different customers. However, the value in the task dropdownlist is "undefined" for all of them...

UPDATE 2:

I found the problem:

In the jQuery I had forgotten to fix the property names of the task objects. Here's the working version:

    $(document).ready(function () {
        //Hook onto the MakeID list's onchange event
        $("#CustomerId").change(function () {
            //build the request url
            var url = "Timesheet/Tasks";
            //fire off the request, passing it the id which is the MakeID's selected item value
            $.getJSON(url, { id: $("#CustomerId").val() }, function (data) {
                //Clear the Model list
                $("#TaskId").empty();
                //Foreach Model in the list, add a model option from the data returned
                $.each(data, function (index, optionData) {
                    $("#TaskId").append("<option value='" + optionData.Id + "'>" + optionData.Name + "</option>");
                });
            });
        }).change();
    });

But I would like to know how to get a default value for the dropdownlists. Since otherwise the customer dropdownlist will have a value when you first get to the page, but the task list won't...

Anders
  • 12,556
  • 24
  • 104
  • 151
  • are you sure that action method is complete? where is `_model.Tasks` coming from? The code using it is commented out - therefore that line won't compile, or have you un-commented it out? Please post the action method in full. – RPM1984 Dec 06 '10 at 21:14
  • No, like I mentioned, I commented out the linq expression because it didn't work. The foreach does the same thing the linq was supposed to do. _model.Tasks is the model with the data from the database. I did'nt include the code that gets the data. But this action method works. It's just the linq expression that didn't work (hence commented out). But I would still like to know why the linq expression doesn't work (when uncommented of course). But mainly, I'm wondering why the jQuery doesn't seem to fill the second dropdown with the result tasks... – Anders Dec 06 '10 at 21:38

1 Answers1

2

JsonResult uses JavascriptSerializer to generate JSON data, and JavascriptSerializer does not support circular references. Maybe the following post will help you solve your problem: Json and Circular Reference Exception

Update

Instead of working around the circular reference, just return a List of another object, with just the two fields you need: ID and Description. It would look something like this:

var tasklist = _model.Tasks.Where(x => x.CustomerId.ToString() == id)
                           .Select(x => new { ID = task.ID, Description = task.Description });
Community
  • 1
  • 1
CGK
  • 2,662
  • 21
  • 24
  • Hmm, ok... But I'm not sure I know what to do with that post, it's a bit over my head. I am using the Entity Framework for my model, and I guess I shouldn't introduce any attributes ([ScriptIgnore]) there, because if I update the model it will disappear. I do have a ViewModel to simplify access to the model. But that just has a bunch of properties accessing the collections of Customers, Tasks, etc. I don't know where to use this attribute... – Anders Dec 06 '10 at 21:47
  • Thanks, yes I think it would be better to skip the circular workaround, and I tried your suggestion. It did change something that seems to be on the right track, but I now get "undefined" as values in the task dropdown. See update above. What am I doing wrong? – Anders Dec 06 '10 at 22:38
  • I got it working, using the code you suggested. Could you please just tell me how to get a default value in the first dropdown though? – Anders Dec 06 '10 at 23:16
  • Figured it out. Just had to add it after the selectlist parameter: <%:Html.DropDownListFor(m => m.Customers, new SelectList(Model.Customers, "Id", "Name"), "Please select...", new { @id = "CustomerId" })%> Thanks for all your help CGK! – Anders Dec 06 '10 at 23:43