构建与OPTGROUP组选择列表 [英] Constructing a Select List with OptGroup groups

查看:220
本文介绍了构建与OPTGROUP组选择列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前项目:


  • ASP.NET 4.5.2

  • MVC 5

我想从模型建立与 OptGroups 一个选择菜单,但我的问题是,我似乎无法建立 OptGroups 自己。

I am trying to build a select menu with OptGroups from the Model, but my problem is that I cannot seem to build the OptGroups themselves.

我的模型:

[DisplayName("City")]
public string CityId { get; set; }
private IList<SelectListItem> _CityName;
public IList<SelectListItem> CityName {
  get {
    List<SelectListItem> list = new List<SelectListItem>();
    Dictionary<Guid, SelectListGroup> groups = new Dictionary<Guid, SelectListGroup>();
    List<Region> region = db.Region.Where(x => x.Active == true).OrderBy(x => x.RegionName).ToList();
    foreach(Region item in region) {
      groups.Add(item.RegionId, new SelectListGroup() { Name = item.RegionName });
    }
    List<City> city = db.City.Where(x => x.Active == true).ToList();
    foreach(City item in city) {
      list.Add(new SelectListItem() { Text = item.CityName, Value = item.CityId.ToString(), Group = groups[item.RegionId] });
    }
    return list;
  }
  set { _CityName = value; }
}

每个城市可以在一个区域。我希望有一个选择菜单到城市各地区组。所有我可以计算出,在code以上是应该做的工作,而是我得到的所有城市下拉菜单下的分组OPTGROUP名为 System.Web.Mvc.SelectListGroup

Each city can be in a region. I want a select menu to group the cities by region. By all that I can figure out, the code above is supposed to do the job, but instead I get a drop-down menu with all cities grouped under the OptGroup named System.Web.Mvc.SelectListGroup

在上面的code最关键的事情是,我先通过区域迭代,并把它们放入一个字典,用 RegionId 设置是关键那带回 RegionName (它本身被格式化为一个SelectListGroup)。

The key thing in the above code is that I first iterate through the Regions, and put them into a Dictionary, with the RegionId set to be the key that brings back the RegionName (which itself is formatted as a SelectListGroup).

然后,我通过城市迭代,并分配到每个城市相匹配的组全市 RegionId

Then I iterate through the Cities, and assign to each city the group that matches the city’s RegionId.

我还没有看到网上说其实从数据库中获取内容的任何实例 - 所有的例子100%使用硬codeD SelectListGroup SelectListItem 值。

I have not seen any examples on the Internet that actually pull content from a database -- 100% of all examples use hard-coded SelectListGroup and SelectListItem values.

我的看法是正确的也是,据我所知:

My View is also correct, AFAIK:

@Html.DropDownListFor(x => x.CityId, new SelectList(Model.CityName, "Value", "Text", "Group", 1), "« ‹ Select › »", htmlAttributes: new { @class = "form-control" })

正如你所看到的,集团应该被带入的SelectList 的DropDownList 的正与 OptGroups ,只是不正确的。

As you can see, the Group is supposed to be brought into the SelectList, and the DropDownList is being created with OptGroups, just not the correct ones.

我的下拉菜单看起来是这样的:

My resulting drop-down menu looks something like this:

« ‹ Select › »
System.Web.Mvc.SelectListGroup
  City1
  City2
  ...
  LastCity

当它应该是这样的:

« ‹ Select › »
Region1
  City2
  City4
  City5
Region2
  City3
  City1
  City6

建议?

修改方案:我按照 通过的斯蒂芬Muecke ,但稍微修改它。

Modified solution: I have followed the solution provided by Stephen Muecke, but have modified it slightly.

之一的一般的MVC的规则是,你有一个模型比控制器重,该模型定义你的业务逻辑。斯蒂芬断言所有数据库的访问应在控制器来完成。我来既同意。

One of the general rules of MVC is that you have a model that is heavier than the controller, and that the model defines your business logic. Stephen asserts that all database access should be done in the controller. I have come to agree with both.

我的一个最大的问题是一个下拉菜单或其他任何pre-填充表单元素的任何创作需要被称为每次页面被调用时。这意味着,对于任何一个创建或编辑页面,你需要调用它不仅对[HTTPGET]的方法,而且在[HttpPost]方法,其中模型送回视图,因为它没有正确验证。这意味着你必须code(传统上通过ViewBags)添加到的每种方法的,只是pre-填充像下拉列表元素。这就是所谓的code重复,是不是一件好事。必须有一个更好的方式,并感谢斯蒂芬的指导下,我已经发现了。

One of my biggest "issues" is that any creation of a drop-down menu or any other pre-populated form element needs to be called every time the page is called. That means, for either a creation or edit page, you need to call it not only on the [HttpGet] method, but also in the [HttpPost] method where the model is sent back to the view because it did not properly validate. This means you have to add code (traditionally via ViewBags) to each Method, just to pre-populate elements like drop-down lists. This is called code duplication, and is not a Good Thing. There has to be a better way, and thanks to Stephen’s guidance, I have found one.

在保持数据存取出来的模型的问题是,你需要用数据来填充你的模型。以避免code再利用,并避免可能的错误的问题是,你不应该做的数据绑定到控制器中的元素作业。这后一种作用是<青霉>业务逻辑的,并在模型中理应属于。在我的情况下,业务逻辑是,我需要用户输入限制城市的列表,按地区分组,管理员可以从下拉列表中选择。因此,虽然我们可能的组装的中控制器的数据,我们的绑定的它到模型的的模型。我的错误之前,在模型中,这是完全不适合做两个。

The problem with keeping data access out of the Model is that you need to populate your model with the data. The problem with avoiding code reuse and avoiding potential errors is that you should not do the job of binding data to elements in the controller. This latter action is business logic, and rightfully belongs in the model. The business logic in my case is that I need to limit user input to a list of cities, grouped by region, that the administrator can select from a drop-down. So while we might assemble the data in the controller, we bind it to the model in the model. My mistake before was doing both in the model, which was entirely inappropriate.

通过将数据绑定到模型中的模型,我们避免具有结合两次 - 一旦在每个控制器的[HTTPGET]和[HttpPost]的方法。我们只需要一次绑定,在由这两种方法所处理的模式。如果我们有一个可以创建和编辑功能之间共享一个更通用的模型,我们可以做到这一点只在一个地方,而不是四个结合(但我没有这种程度的通用性,所以我不会放弃,作为一个例子)

By binding the data to the model in the model, we avoid having to bind it twice - once in each of the [HttpGet] and [HttpPost] methods of the controller. We only have to bind it once, in the model that is handled by both methods. And if we have a more generic model that can be shared between Create and Edit functions, we can do this binding in only one spot instead of four (but I don’t have this degree of genericness, so I won’t give that as an example)

所以,开始的时候,我们实际上是剥离整个数据集,并坚持它自己的类中:

So to start out, we actually peel off the entire data-assembly, and stick it inside its own class:

public class SelectLists {
  public static IEnumerable<SelectListItem> CityNameList() {
    ApplicationDbContext db = new ApplicationDbContext();
    List<City> items = db.City.Where(x => x.Active == true).OrderBy(x => x.Region.RegionName).ThenBy(x => x.CityName).ToList();
    return new SelectList(items, "CityId", "CityName", "Region.RegionName", 1);
  }
}

这存在命名空间中,但我们正在处理的部分的控制之下。为了清楚起见,我插在文件的最后,只是命名空间的闭幕前。

This exists within the namespace, but beneath the controller of the section we are dealing with. For clarity’s sake, I stuck it at the very end of the file, just before the closing of the namespace.

然后,我们来看看其型号为这个页面:

Then we look at the model for this page:

public string CityId { get; set; }
private IEnumerable<SelectListItem> _CityName;
public IEnumerable<SelectListItem> CityName {
  get { return SelectLists.CityNameList(); }
  set { _CityName = value; }
}

请注意:即使 CityId 的Guid 和数据库字段是唯一标识符,我是通过视图,因为客户端验证吸驴球的带来的GUID这个值作为一个字符串。这是更容易做一个下拉菜单,在客户端验证,如果作为字符串,而不是一个GUID处理。你只要把它转换回一个GUID你花掉它放回主模型为表之前。另外,CITYNAME是不是在城市表中的实际场 - 这纯粹是存在作为下拉菜单本身,为什么它在 CreateClientViewModel 存在的这是一个占位符创建页面。这样,在视图中我们可以创建一个 DropDownListFor 明确绑定CityId的下拉菜单,居然允许在首位客户端验证(GUID是刚一个额外的头痛)。

Note: Even though the CityId is a Guid and the DB field is a uniqueidentifier, I am bringing this value in as a string through the view because client-side validation sucks donkey balls for Guids. It’s far easier to do client-side validation on a drop-down menu if the Value is handled as a string instead of a Guid. You just convert it back into a Guid before you plunk it back into the master model for that table. Plus, CityName is not an actual field in the City table - it exists purely as a placeholder for the drop-down menu itself, which is why it exists in the CreateClientViewModel for the Create page. That way, in the view we can create a DropDownListFor that explicitly binds the CityId to the drop-down menu, actually allowing client-side validation in the first place (Guids are just an added headache).

最关键的事情是获得{} 。正如你所看到的,没有更多的丰富code这确实DB访问,只是一个简单的 SelectLists 哪些目标类和方法的调用 CityNameList()。你甚至可以传递给方法的变量,所以你可以有同样的方法带回相同的下拉菜单中的不同变化。也就是说,如果你想在一个页面(创建)在一个下拉到必须通过OptGroups分组的选项,而另一个下拉(编辑),以没有选项的分组。

The key thing is the get {}. As you can see, no more copious code which does DB access, just a simple SelectLists which targets the class, and a calling of the method CityNameList(). You can even pass variables on to the method, so you can have the same method bring back different variations of the same drop-down menu. Say, if you wanted one drop-down on one page (Create) to have its options grouped by OptGroups, and another drop-down (Edit) to not have any groupings of options.

实际的模型最终被比以前更加简单:

The actual model ends up being even simpler than before:

@Html.DropDownListFor(x => x.CityId, Model.CityName, "« ‹ Select › »", htmlAttributes: new { @class = "form-control" })

无需修改,在下拉列表中的数据所带来的元素 - 你只是通过 Model.ElementName 称之为

我希望这有助于。

推荐答案

首先,您查看模型不应该包含数据库访问code来填充其属性。这是控制器的责任,你们已经做出了code不可能单元测试。通过改变模式启动

Firstly, you view model should not contain database access code to populate its properties. That is the responsibility of the controller and you have made your code impossible to unit test. Start by changing the model to

public class CreateClientViewModel
{
    [DisplayName("City")]
    public string CityId { get; set; }
    public IList<SelectListItem> CityList { get; set; }
    ....
}

然后在控制器中,可以使用的SelectList 的接受的一个重载一个组名来生成集合

Then in the controller, you can use one of the overloads of SelectList that accepts a groupName to generate the collection

var cities = var cities = db.City.Include(x => x.Region).Where(x => x.Active == true)
    .OrderBy(x => x.Region.RegionName).ThenBy(x => x.CityName);

var model = new CreateClientViewModel()
{
    CityList = new SelectList(cities, "CityId", "CityName", "Region.RegionName", null)
};
return View(model);

和视图

@Html.DropDownListFor(x => x.CityId, Model.CityList , "« ‹ Select › »", new { @class = "form-control" })

作为替代方案,你也可以做到这一点使用 SelectListItem

var model = new CreateClientViewModel()
{
    CityList = new List<SelectListItem> // or initialize this in the constructor
};
var cities = var cities = db.City.Include(x => x.Region).Where(x => x.Active == true).GroupBy(x => x.Region.RegionName);
foreach(var regionGroup in cities)
{
    var optionGroup = new SelectListGroup() { Name = regionGroup.Key };
    foreach(var city in regionGroup)
    {
        model.CityList.Add(new SelectListItem() { Value = city.CityId.ToString(), Text = city.CityName, Group = optionGroup });
    }
}
return View(model);

这篇关于构建与OPTGROUP组选择列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆