ASP.Net Filter下拉列表来自多个表的项目

时间:2017-11-17 05:58:11

标签: asp.net-mvc entity-framework linq viewmodel

要点:

我正在尝试使用两个DropDownList控件来过滤当前正在视图中排序和显示的数据。

我们将要学习的内容 创建可以使用DropDownList过滤数据的一对多和多对多关系的ViewController

可能的原因 如果我的DropdownList代码没有错误,我用来显示数据的ViewModel没有对DropDownList项目的适当支持。

换句话说,RazorView和我的ViewModel与我想要达到的目标不兼容。如果我尝试更改我的ViewModel或RazorView,我会得到现有代码的无错循环错误。

或者Linq查询需要专家关注

这是FilterViewModel.cs

    public IEnumerable <App>      Apps { get; set; }
    public IEnumerable <Language> Languages { get; set; }
    public IEnumerable <Platform> Platforms { get; set; }
    public IEnumerable <AgeGroup> AgeGroups { get; set; }
    public IEnumerable <ProductCode> ProductCodes { get; set; }

这是AppsController.cs

 public ActionResult FilterApps(App app)
    {
        var apps = _context.Apps.ToList();
        var languages = _context.Languages.ToList();
        var productCodes = _context.ProductCodes.ToList();
        var platforms = _context.Platforms.ToList();
        var ageGroups = _context.AgeGroups.ToList();
        var viewModel = new FilterViewModel
        {
            AgeGroups = ageGroups,
            Languages = languages,
            Platforms = platforms,
            ProductCodes = productCodes,
            Apps = apps
            .Where(a => a.LanguageId == app.LanguageId && a.PlatformId == app.PlatformId)
     // I also tried all possible combinations :(a.Lanage.Id etc)
        };
        return View("FilterApps", viewModel);
    }

以下是FilterApps.cshtml

@model Marketing.ViewModels.FilterViewModel
<h2>FilterApps</h2>
@using (Html.BeginForm("FilterApps", "Apps", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">

    @Html.DropDownListFor( m => m.Languages,
      new SelectList(Model.Languages, "Id", "Name"),"Select Language",
      new { @class = "form-control", @id = "dropDown" })

    @Html.DropDownListFor(m => m.Platforms, 
      new SelectList(Model.Platforms, "Id", "Name"), "Select Platform",
      new { @onchange = "this.form.submit();",
      @class = "form-control", @id = "dropDown" })

</div>
}

//The existing code below is working fine so far.
@foreach (var group in Model.AgeGroups)
  {
   <h4>@group.Name</h4>

     @foreach (var app in Model.Apps.OrderBy(a => a.AppOrder))
            {
                if (app.AgeGroupId == group.Id)
                {
                 @app.ProductCode.Name
                 @app.Name
                 @app.Platform.Name
                }
             }
   }

可能没用,但我希望其他信息会有所帮助。

其他信息

App.cs引用所有其他表,例如

public Language Language { get; set; }
public int LanguageId { get; set; }

public Platform Platform { get; set; }
public int PlatformId { get; set; } 
and so on... 

我已尝试过的内容 几个断点和日志来跟踪数据,我也尝试使用以下内容,但它破坏了我现有的排序和分组。

public App App { get; set; } //Instead of the IEnumerable<App> 

1 个答案:

答案 0 :(得分:2)

您的代码存在多个问题。

首先,您无法将<select>元素绑定到复杂对象的集合。 <select>会回复其所选选项的值(假设int的{​​{1}}属性为Id,则为Language。)

接下来,模型中的视图为int(并且您的生成表单控件具有基于这些属性的FilterViewModel属性),但您发布回不同的模型(name)不包含那些属性,所以无论如何都不会绑定。

您添加了App标签选项(“选择语言”),如果选择了该选项,则会发布一个null值,这会导致您的查询失败。

我也在下面提到了一些不良做法。

您的视图模型应为

null

我不清楚public class AppsFilterVM { public int? Language { get; set; } public int? Platform { get; set; } public IEnumerable<SelectListItem> LanguageOptions { get; set; } public IEnumerable<SelectListItem> PlatformOptions { get; set; } ... public IEnumerable <App> Apps { get; set; } } AgeGroups是什么,所以我在上面的代码中省略了它们,从您的评论中,我假设用户可以按ProductCodes进行过滤或Language或两者

控制器代码为

Platform

然后在视图中(注意它是进行GET,而不是POST)

public ActionResult FilterApps(AppsFilterVM model)
{
    var apps = _context.Apps;
    if (model.Language.HasValue)
    {
        apps = apps.Where(x => x.LanguageId == model.Language.Value);
    }
    if (model.Platform.HasValue)
    {
        apps = apps.Where(x => x.PlatformId == model.Platform.Value);
    }
    model.Apps = apps;
    ConfigureViewModel(model);
    return View(model);
}

private void ConfigureViewModel(AppsFilterVM model)
{
    // populate the selectlists
    var languages = _context.Languages;
    var platforms = _context.Platforms
    model.LanguageOptions = new SelectList(languages, "Id", "Name");
    model.PlatformOptions = new SelectList(platforms , "Id", "Name");
}

还有一些你不应该做的事情。您为@model.AppsFilterVM .... @using (Html.BeginForm("FilterApps", "Apps", FormMethod.Get)) { @Html.LabelFor(m => m.Language) @Html.DropdownListFor(m => m.Language, Model.LanguageOptions, "No filter") @Html.ValidationMessageFor(m => m.Language) @Html.LabelFor(m => m.Platform) @Html.DropdownListFor(m => m.Platform, Model.PlatformOptions, "No filter") @Html.ValidationMessageFor(m => m.Platform) <input type="submit" value="Filter" /> } @foreach (var group in Model.AgeGroups) { .... 个元素提供了相同的<select>属性,该属性是无效的html(id方法已根据属性名称生成唯一的DropDownListFor()

您不应该根据id change事件提交表单。如果用户使用键盘浏览选项(例如使用箭头键,或者键入一个字符转到以该字母开头的第一个选项,然后表单将立即提交。此外,用户可能会先从第二个下拉列表中选择一个选项,该选项会在他们有机会选择该选项之前立即发布在第一个。允许用户进行选择,检查,然后在他们选择时提交表单。

您的视图不应包含linq查询,并且在将模型传递给视图之前,应在控制器中完成分组和排序。您的<select>属性实际上应该是一个视图模型,其中包含组名称的属性和应用程序的集合属性(类似于previous question中的视图模型),因此视图很简单

Apps

您还应该考虑使用ajax来提交表单,该表单将调用单独的服务器方法,该方法返回仅@foreach(var group in Model.AgeGroups) { @group.Name foreach (var app in group.Apps) { @app.ProductCode @app.Name @app.Platform } } 的部分视图,并在成功回调中更新DOM,这将提高性能。有关示例,请参阅Rendering partial view on button click in ASP.NET MVC