1

I am writing an ASP.NET MVC4 / Razor / C# based application that needs to render a grid of records. Each row has several columns, and there may be 100 or so rows.

Each row has a checkbox field, text field and then three cascading dropdown lists. The first dropdown is prepopulated on page load. The second needs to be populated using Ajax on change of the first dropdown list. The third from a change on the second. Each row is separate and does not influence each other.

What is the recommended way to implement something like this? The various solutions for cascading dropdown lists are only for single cascading lists - they do not work (for me) when placed inside a foreach loop.

The skeleton of what I have is shown below:

@model IList<MyModel>

@using (Html.BeginForm("MyAction", "Home")) {
  <table><tr><th>Use?</th><th>Name</th><th>List A</th><th>List B</th><th>List C</th></tr>
  @Html.EditorForModel()
</table>
}

The model looks something like this:

public class MyModel
{
  public bool Use { get; set; }
  public string Name { get; set; }
  public int? ListAId { get; set; }
  public int? ListBId { get; set; }
  public int? ListCId { get; set; }
  public IList<ListAList> ListA { get; set; }
}

The shared EditorTemplates file MyModel.cshtml follows this structure:

@model MyNamespace.MyModel
<tr>
  <td>@Html.CheckBoxFor(model => model.Use)</td>
  <td>@Html.DisplayFor(model => model.Name)</td>
  <td>@Html.DropDownListFor(model => model.ListAId, new SelectList(Model.ListA, "Id", "Name", Model.ListAId), "")</td>
  <td>??</td>
  <td>??</td>
</tr>

The ?? indicates I am unsure what to put in here.

How do I proceed to render the ListB select box using Ajax on change of the ListA select box, then on change of ListB render the ListC select box?

tereško
  • 58,060
  • 25
  • 98
  • 150
pwnell
  • 171
  • 1
  • 11

1 Answers1

2

check this:

Update1: Suppose that there is Name ROWID, (and list all the same data source).

Update2: the example available on github

Based on these responses:

Model:

using System.Collections.Generic;

namespace MyNamespace
{
    public class MyModel
    {
        public MyModel() { ListA = new List<ListAList>(); }
        public bool Use { get; set; }
        public string Name { get; set; }
        public int? ListAId { get; set; }
        public int? ListBId { get; set; }
        public int? ListCId { get; set; }
        public IList<ListAList> ListA { get; set; }
    }

    public class ListAList
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

}

Main action in the Home Controller:

public ViewResult MyAction()
{
    var model = new List<MyModel>();
    for (int i = 0; i < 10; i++)
    {
        var item = new MyModel() 
            {
            Name = string.Format("Name{0}", i),
            Use = (i % 2 == 0),
            ListAId = null,
            ListBId = null,
            ListCId = null
            };

        for (int j = 0; j < 10; j++)
        {
            item.ListA.Add( new ListAList() 
                {
                    Id=j,
                    Name = string.Format("Name {0}-{1}",i,j)
                });
        }
        model.Add(item);
    }

    return View(model);
}

Data source provider in the Home controller:

public JsonResult PopulateOption(int? listid, string name)
{

    //todo: preparing the data source filter

    var sites = new[]
    {
        new { id = "1", name = "Name 1" },
        new { id = "2", name = "Name 2" },
        new { id = "3", name = "Name 3" },
    };
    return Json(sites, JsonRequestBehavior.AllowGet);
}

EditorTemplate:

@model MyNamespace.MyModel
<tr>
    <td>@Html.CheckBoxFor(model => model.Use)</td>
    <td>@Html.DisplayFor(model => model.Name)</td>
    <td>@Html.DropDownListFor(model => model.ListAId, new SelectList(Model.ListA, "Id", "Name", Model.ListAId), "", new { @id = string.Format("ListA{0}", Model.Name), @class="ajaxlistA" })</td>
    <td><select class="ajaxlistB" id="ListB@(Model.Name)"></select></td>
    <td><select class="ajaxlistC" id="ListC@(Model.Name)"></select></td>
</tr>

And the main view with Ajax functions:

@using MyNamespace
@model IList<MyModel>

@using (Html.BeginForm("MyAction", "Home")) {
  <table><tr><th>Use?</th><th>Name</th><th>List A</th><th>List B</th><th>List C</th></tr>
@Html.EditorForModel()
</table>
}

<script src="@Url.Content("~/Scripts/jquery-1.7.1.js")" type="text/javascript"></script>


<script>
    $(document).ready( $(function () {
        $('.ajaxlistA').change(function () {
            // when the value of the first select changes trigger an ajax request
            list = $(this);
            var listvalue = list.val();
            var listname = list.attr('id');
            $.getJSON('@Url.Action("PopulateOption", "Home")', { listid: listvalue, name: listname }, function (result) {
                // assuming the server returned json update the contents of the 
                // second selectbox
                var listB = $('#' + listname).parent().parent().find('.ajaxlistB');
                listB.empty();
                $.each(result, function (index, item) {
                    listB.append(
                        $('<option/>', {
                            value: item.id,
                            text: item.name
                        })
                    );
                });
            });
        });
        $('.ajaxlistB').change(function () {
            // when the value of the first select changes trigger an ajax request
            list = $(this);
            var listvalue = list.val();
            var listname = list.attr('id');
            $.getJSON('@Url.Action("PopulateOption", "Home")', { listid: listvalue, name: listname }, function (result) {
                // assuming the server returned json update the contents of the 
                // second selectbox
                var listB = $('#' + listname).parent().parent().find('.ajaxlistC');
                listB.empty();
                $.each(result, function (index, item) {
                    listB.append(
                        $('<option/>', {
                            value: item.id,
                            text: item.name
                        })
                    );
                });
            });
        });
    }));
</script>

And the result:

the result

Community
  • 1
  • 1
Gábor Plesz
  • 1,203
  • 1
  • 17
  • 28
  • Thanks, but I have reviewed some of the links you posted already. My big issue is the fact that I have these cascading dropdown lists in a foreach loop... Causing issues with ids. None of these articles hints at a way to resolve that - at least not what I can infer. Also, I'd like to have these dropdown lists auto bind back to my model. Therefore the Ids need to be correct. – pwnell May 27 '13 at 23:36
  • ok, I understand. Questions: (1) Each data sources of list (n x 3 pieces list) is the same: Model.ListA? (2) If yes, then how to filter the data in the second or third list? (3) Do you have any RowId? (Model.Id or something like that, which is based on the lines can be distinguished?) – Gábor Plesz May 28 '13 at 08:01
  • Thanks SO much for this info. I am just in the middle of another project but will look at this tomorrow and provide feedback. – pwnell May 29 '13 at 15:56
  • It appears to work well, the only problem I have is that the ajaxlistB and ajaxlistC entries do not bind back to the data model. The ID generated by MVC for the listAId field is [i].ListAId, with i an incrementing integer. The generated ID for the ajax list items are ListBNamei and ListCNamei, respectively. Do I just need to write out the ID as [i].ListBId and [i].ListCId, respectively, instead? – pwnell Jun 04 '13 at 21:21
  • I just modified it to write out the [].Name syntax and now it works. Thanks! – pwnell Jun 04 '13 at 21:51
  • Yeah, I was not paying attention, thanks for the correction. Do you want to send the GitHub to get that? – Gábor Plesz Jun 05 '13 at 07:43