ASP.NET MVC Core 级联下拉列表

6
我在寻找一个教程/视频,展示如何使用EntityFramework从数据库中实现级联下拉列表。我正在使用ASP.NET MVC Core,EntityFramework Core和C#。
目前,我能够成功地从我的数据库中检索数据到三个下拉列表中。
我想要实现的是,用户首先选择一个州,然后显示所有与该州相关的城市。然后,当用户选择了一个城市后,它将显示与该城市相关的邮政编码。
任何帮助都将不胜感激。 模型
    public class Customer
{
    public int CustomerId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public int StateId { get; set; }
    public int CityId { get; set; }
    public int ZipId { get; set; }

    public State State { get; set; }
    public City City { get; set; }
    public Zip Zip { get; set; }
}

    public class State
{
    public int StateId { get; set; }
    public string Abbr { get; set; }

    public List<Customer> Customers { get; set; }
}

    public class City
{
    public int CityId { get; set; }
    public string Name { get; set; }

    public int StateId { get; set; }
    public State State { get; set; }

    public List<Customer> Customers { get; set; }
}

    public class Zip
{
    public int ZipId { get; set; }
    public string PostalCode { get; set; }

    public int CityId { get; set; }
    public City City { get; set; }

    public List<Customer> Customers { get; set; }
}

ViewModels

    public class CustomerFormVM
{
    public int CustomerId { get; set; }

    [Display(Name = "First Name")]
    [StringLength(50)]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [StringLength(50)]
    public string LastName { get; set; }

    [Required(ErrorMessage = "Select State")]
    [Display(Name = "State")]
    public int StateId { get; set; }

    //public IEnumerable<State> States { get; set; }
    public IEnumerable<SelectListItem> States { get; set; }

    [Required(ErrorMessage = "Select City")]
    [Display(Name = "City")]
    public int CityId { get; set; }

    //public IEnumerable<City> Citys { get; set; }
    public IEnumerable<SelectListItem> Citys { get; set; }

    [Required(ErrorMessage = "Select Zip")]
    [Display(Name = "Zip")]
    public int ZipId { get; set; }

    //public IEnumerable<Zip> Zips { get; set; }
    public IEnumerable<SelectListItem> Zips { get; set; }
}

客户控制器

public class CustomerController : Controller
{
    private MultiDbContext db;

    public CustomerController(MultiDbContext context)
    {
        db = context;
    }

    // GET: /<controller>/
    public IActionResult Index()
    {
        return View(db.Customers.ToList());
    }

    public IActionResult getCititesFromDatabaseByStateId(int id)
    {
        return View(db.Citys.Where(c => c.StateId == id).ToList());
    }

    public IActionResult getCities(int id)
    {
        var cities = new List<City>();
        cities = getCititesFromDatabaseByStateId(id); //call repository
        return Json(cities);
    }

    public ActionResult Create()
    {
        var states = db.States.ToList();
        var citys = db.Citys.ToList();
        var zips = db.Zips.ToList();

        var viewModel = new CustomerFormVM
        {
            States = states,
            Citys = citys,
            Zips = zips
        };

        return View(viewModel);
    }


    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(CustomerFormVM vm)
    {
        if (ModelState.IsValid)
        {
            var customer = new Customer();
            {
                customer.FirstName = vm.FirstName;
                customer.LastName = vm.LastName;
                customer.StateId = vm.StateId;
                customer.CityId = vm.CityId;
                customer.ZipId = vm.ZipId;
            }
            db.Customers.Add(customer);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        else
        {
            vm.States = db.States.ToList();
            vm.Citys = db.Citys.ToList();
            vm.Zips = db.Zips.ToList();
            return View(vm);
        }
    }


    public ActionResult Edit(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        var customervm = new CustomerFormVM();
        {
            Customer customer = db.Customers.SingleOrDefault(c => c.CustomerId == id);

            if (customer == null)
            {
                return NotFound();
            }

            customervm.CustomerId = customer.CustomerId;
            customervm.FirstName = customer.FirstName;
            customervm.LastName = customer.LastName;

            // Retrieve list of States
            var states = db.States.ToList();
            customervm.States = states;

            // Retrieve list of Citys
            var citys = db.Citys.ToList();
            customervm.Citys = citys;

            // Retrieve list of Citys
            var zips = db.Zips.ToList();
            customervm.Zips = zips;

            // Set the selected state
            customervm.StateId = customer.StateId;

            // Set the selected city
            customervm.CityId = customer.CityId;

            // Set the selected zip
            customervm.ZipId = customer.ZipId;
        }
        return View(customervm);
    }


    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(CustomerFormVM vmEdit)
    {
        if (ModelState.IsValid)
        {
            Customer customer = db.Customers.SingleOrDefault(c => c.CustomerId == vmEdit.CustomerId);

            if (customer == null)
            {
                return NotFound();
            }

            customer.FirstName = vmEdit.FirstName;
            customer.LastName = vmEdit.LastName;
            customer.StateId = vmEdit.StateId;
            customer.CityId = vmEdit.CityId;
            customer.ZipId = vmEdit.ZipId;

            db.Entry(customer).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(vmEdit);
    }
}

创建视图

        <div class="form-group">
        @Html.LabelFor(c => c.FirstName)
        @Html.TextBoxFor(c => c.FirstName, new { @class = "form-control" })
    </div>

    <div class="form-group">
        @Html.LabelFor(c => c.LastName)
        @Html.TextBoxFor(c => c.LastName, new { @class = "form-control" })
    </div>

    <div class="form-group">
        @*@Html.LabelFor(s => s.StateId)
            @Html.DropDownListFor(s => s.StateId, new SelectList(Model.States, "StateId", "Abbr"), "", new { @class = "form-control" })
            @Html.ValidationMessageFor(s => s.StateId)*@

        <label asp-for="StateId "></label>
        <select asp-for="StateId " asp-items="Model.States" class="form-control" id="state-target"></select>
        <span asp-validation-for="StateId " class="text-danger"></span>
    </div>

    <div class="form-group">
        @*@Html.LabelFor(ct => ct.CityId)
            @Html.DropDownListFor(ct => ct.CityId, new SelectList(Model.Citys, "CityId", "Name"), "", new { @class = "form-control" })
            @Html.ValidationMessageFor(ct => ct.CityId)*@

        <label asp-for="CityId"></label>
        <select asp-for="CityId" asp-items="Model.Citys" class="form-control" id="city-target"></select>
        <span asp-validation-for="CityId" class="text-danger"></span>
    </div>

    <div class="form-group">
        @Html.LabelFor(z => z.ZipId)
        @Html.DropDownListFor(z => z.ZipId, new SelectList(Model.Zips, "ZipId", "PostalCode"), "", new { @class = "form-control" })
        @Html.ValidationMessageFor(z => z.ZipId)
    </div>

    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
}

@section scripts {
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
    <script src="~/lib/js/example.js"></script>,
}

你需要添加一些JS来处理下拉列表的更改事件。 - H. Herzl
@H. Herzl,有没有不用JS来处理这个的方法? - Brian Brian
据我所知,您需要使用JS来满足这个需求。 - H. Herzl
你所需要的唯一JS代码是以内联属性的形式存在,即 onchange="this.form.submit()"。你需要在控制器中处理该提交并为下拉列表填充新的集合。 - Mackan
你可以尝试使用JQuery代替JS。 - Jason Ebersey
1个回答

10

我曾经遇到过类似的情况,但是在我的例子中,我有一个根文件夹,根据我使用的哪个根文件夹,下一个下拉列表将显示相应的子文件夹。

不确定是否有纯粹的ASP.NET解决方案,但我用了JQuery / Ajax来实现此功能。

你的代码应该像这样:

html列表:

<label asp-for="StateId "></label>
<select asp-for="StateId " asp-items="Model.States" class="form-control" id="state-target"></select>
<span asp-validation-for="StateId " class="text-danger"></span>

<label asp-for="CityId"></label>
<select asp-for="CityId" asp-items="Model.Citys" class="form-control" id="city-target"></select>
<span asp-validation-for="CityId" class="text-danger"></span>

Jquery代码,你需要将其编写在.js文件中,然后使用此语句将其添加到特定视图中:<script src="~/js/example.js"></script>,别忘了在任何其他JavaScript之前需要将jQuery库添加到你的项目中,而你的example.js文件将包含:

$(document).ready(function () {
  $("#state-target").on("change", function () {
    $list = $("#city-target");
    $.ajax({
        url: "/getCities",
        type: "GET",
        data: { id: $("#state-target").val() }, //id of the state which is used to extract cities
        traditional: true,
        success: function (result) {
            $list.empty();
            $.each(result, function (i, item) {
                $list.append('<option value="' + item["CityId"] + '"> ' + item["Name"] + ' </option>');
            });
        },
        error: function () {
            alert("Something went wrong call the police");
        }
    });
  });
});

这个Ajax请求将调用控制器中的操作,该操作将从数据库检索城市列表(使用类似于在getCititesFromDatabaseByStateId(id)方法中使用的return dbContext.CityTable.Where(c => c.StateId == id).ToList()),然后返回Json对象,success函数将创建选项列表并应用它:

public IActionResult getCities(int id)
{
    var cities = new List<City>();
    cities = getCititesFromDatabaseByStateId(id); //call repository
    return Json(citites);
}
在您的ViewModel中,请考虑将IEnumerable<State/City/Zip> (IEnumerable<T>)更改为IEnumerable<SelectListItem>。 我还可以说,您的模型很混乱(但如果您可以从数据库获取数据,请专注于先使列表正常工作),以后再考虑改进它们。
评论中提到的2个错误的修复:
public List<City> getCititesFromDatabaseByStateId(int id)
{
    return db.Citys.Where(c => c.StateId == id).ToList();
}

public ActionResult Create()
{
     var states = new SelectList(db.States.ToList(), "StateId", "Abbr");
     var citys = new SelectList(db.Citys.ToList(), "CityId", "Name");
     var zips = new SelectList(db.Zips.ToList(), "ZipId", "Code");

     var viewModel = new CustomerFormVM
     {
         States = states,
         Citys = citys,
         Zips = zips
     };

     return View(viewModel);
}

@anton-toshnik,非常抱歉,我目前并不熟悉JS / jQuery。您是否可以根据我发布的代码提供帮助?我尝试添加您提供的Contoller代码,但是当我在return Json(List <City>);上得到一条红线时,我遇到了问题。该行的消息状态为“List <City>”是一个类型,在给定的上下文中无效。 - Brian Brian
好的,我明白你的意思了。我会创建一个聊天室,在那里我可以向你解释一切。 - Anton Toshik
好的,似乎我无法创建一个聊天室,我会在答案中添加更多细节,并建议您查阅一些jQuery教程。 - Anton Toshik
好的,我会看看如何做你提到的事情。我还在学习中,可能需要一点时间 :) - Brian Brian
@BrianBrian,你需要自己调试代码。使用断点查看数据缺失或潜在错误的位置。然后搜索相关信息,如果愿意,可以给我你的Skype/电子邮件,我会与你联系并尝试帮助你解决代码问题。 - Anton Toshik
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接