分组for loop razor视图

时间:2016-01-14 14:30:56

标签: c# asp.net-mvc razor

我的剃须刀视图中有以下内容

                @foreach (var group in Model.QuestionList.GroupBy(x => x.AreaName))
            {
                <h4>@group.Key</h4>

                for (int i = 0; i < Model.QuestionList.Count(x => x.AreaName == group.Key); i++)
                {

                <div class="form-group">
                    <div class="row">
                        <div class="col-md-4">
                            @Html.DisplayFor(x => Model.QuestionList[i].Question)
                        </div>
                        <div class="col-md-2">
                            @Html.HiddenFor(x => Model.QuestionList[i].StnAssureQuestionId)
                            @Html.DropDownListFor(model => model.QuestionList[i].Score, new SelectList(Model.QuestionList[i].Scores, "ScoreId", "ScoreNum", 0), "Please Select", new { @class = "form-control" })
                            @Html.ValidationMessageFor(model => model.QuestionList[i].Score, "", new { @class = "text-danger" })
                        </div>
                        <div class="col-md-4">
                            @Html.EditorFor(x => Model.QuestionList[i].Comments, new { htmlAttributes = new { @class = "form-control" } })
                            @Html.ValidationMessageFor(model => model.QuestionList[i].Comments, "", new { @class = "text-danger" })
                        </div>
                    </div>
                </div>
                }
            }

我希望能够显示QuestionList中的所有对象,但是按AreaName(组密钥)对它们进行分组。

当前代码显示第一组的标题,然后显示该组中的问题,但在此之后,它只显示下一个组名,然后再显示相同的问题,然后再显示所有组。

我很确定,但我仍然没有足够的技巧来发现它。

4 个答案:

答案 0 :(得分:3)

您可能会遇到类似这样的事情,但我也会就此视图创建特定视图模型提出其他建议。

@foreach (var group in Model.QuestionList.GroupBy(x => x.AreaName))
{
    var questionList = group.ToList();
    <h4>@group.Key</h4>
    for (int i = 0; i < questionList.Count(); i++)
    {
        <div class="form-group">
            <div class="row">
                <div class="col-md-4">
                    @Html.DisplayFor(x => questionList[i].Question)
                </div>
                <div class="col-md-2">
                    @Html.HiddenFor(x => questionList[i].StnAssureQuestionId)
                    @Html.DropDownListFor(model => questionList[i].Score, new SelectList(questionList[i].Scores, "ScoreId", "ScoreNum", questionList[i].Score), "Please Select", new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => questionList[i].Score, "", new { @class = "text-danger" })
                </div>
                <div class="col-md-4">
                    @Html.EditorFor(x => questionList[i].Comments, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => questionList[i].Comments, "", new { @class = "text-danger" })
                </div>
            </div>
        </div>
    }
}            

Dot Net Fiddle Example

答案 1 :(得分:3)

你不应该在你的视图中使用复杂的查询,虽然JamieD77的答案将解决正确显示项目的问题,但是当你提交时它将无法绑定到你的模型。

如果您检查生成的html,您会看到每个组都有输入,例如

<input type="hidden" name="questionList[0].StnAssureQuestionId" ... />
<input type="hidden" name="questionList[1].StnAssureQuestionId" ... />

DefaultModelBinder要求集合索引器从零开始并且是连续的,因此在绑定时,它将正确绑定第一组中的输入,但忽略所有其他组中的输入,因为索引器从零开始。

一如既往地从视图模型开始,以表示您想要显示/编辑(What is ViewModel in MVC?)。在这种情况下,我假设与SelectList相关联的Score选项在所有问题中都很常见

public class QuestionnaireVM
{
  public IEnumerable<QuestionGroupVM> Groups { get; set; }
  public SelectList Scores { get; set; }
}
public class QuestionGroupVM
{
  public string Name { get; set; }
  public IEnumerable<QuestionVM> Questions { get; set; }
}
public class QuestionVM
{
  public int ID { get; set; }
  public string Question { get; set; }
  public int Score { get; set; }
  public string Comments { get; set; }
}

虽然您可以在视图中使用嵌套循环

for (int i = 0; i < Model.Groups.Count; i++)
{
  <h4>Model.Groups[i].Name</h4>
  for (int j = 0; j < Model.Groups[i].Count; j++)
  {
    @Html.DisplayFor(m => m.Groups[i].Questions[j].Question)

更好的解决方案是使用EditorTemplates为您提供可重用的组件(并且您不必将集合属性更改为IList<T>)。注意我省略了<div>元素以保持简单。

/Views/Shared/EditorTemplates/QuestionVM.cshtml

@model QuestionVM
@Html.DisplayFor(m => m.Question)
@Html.HiddenFor(m => m.ID)
@Html.DropDownListFor(m => m.Score, (SelectList)ViewData["scores"], "Please Select", new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Score, "", new { @class = "text-danger" })
@Html.EditorFor(m => m.Comments, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(m => m.Comments, "", new { @class = "text-danger" })

/Views/Shared/EditorTemplates/QuestionGroupVM.cshtml

@model QuestionGroupVM
<h4>@Html.DisplayFor(m => m.Name)</h4>
@Html.EditorFor(m => m.Questions, new { scores = ViewData["scores"] })

,主视图将是

@model QuestionnaireVM
@using (Html.BeginForm())
{
  @Html.EditorFor(m => m.Groups, new { scores = Model.Scores })
  <input type="submit" value="Save" />
}

然后在get方法中,将数据模型投影到视图模型,例如

QuestionnaireVM model = new QuestionnaireVM
{
  Groups = db.Questions.GroupBy(x => x.AreaName).Select(x => new QuestionGroupVM
  {
    Name = x.Key,
    Questions = x.Select(y => new QuestionVM
    {
      ID = y.StnAssureQuestionId,
      Question = y.Question,
      Score = y.Score,
      Comments = y.Comments
    }
  },
  Scores = new SelectList(.....)
};
return View(model);

并且POST方法的签名将是

public ActionResult Edit(QuestionnaireVM model)

附注:您目前没有组名属性的输入,这意味着如果由于ModelState无效而需要返回视图,则需要再次运行查询,因此请考虑添加{{ 1}}到@Html.HiddenFor(m => m.Name)模板(当然,如果您返回视图,还需要重新分配QuestionGroupVM.cshtml属性。

答案 2 :(得分:2)

当您显示问题时,您应该使用group对象(分组集合),但不能使用初始集合。

我的意思是你应该改变你的

Model.QuestionList[i]

group.Select(x => x.QuestionList)[i]

无论如何,你的解决方案非常麻烦最好在服务器端进行这样的分组。

答案 3 :(得分:1)

请考虑View应与服务层实现的业务逻辑完全无关。 View只是一种虚拟表示机制,它通过ViewModel抓取Controller提供的数据并显示数据。它是MVC架构的基础,我的强烈建议是遵循架构本身是一个非常好的理由走正确的道路。

据说你的观点变得混乱了。考虑将其重建为:

public class QuestionDataViewModel
{
    public List<QuestionData> Data { get; set; }
}

public class QuestionData
{
    public string AreaName { get; set; }
    public List<Question> QuestionList { get; set; }
}

public class Question
{
    public int StnAssureQuestionId { get; set; }
    public int Score { get; set; }
    public IEnumerable<SelectListItem> Scores { get; set; }
    public List<Comment> Comments { get; set; }
}

构建此服务器端,然后通过简单的razor foreach循环进行渲染。这不仅有助于您使用更干净的代码,还有助于避免在您使用当前实现发布表单时遇到的模型绑定问题。