简体   繁体   English

分组进行循环剃刀视图

[英]Grouping in for loop razor view

I Have the following in my razor view 我的剃须刀视图中有以下内容

                @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>
                }
            }

I want to be able to display all the objects in QuestionList but group them by AreaName, which is the group key. 我希望能够显示QuestionList中的所有对象,但按AreaName(即组键)将它们分组。

The current code displays the title of the first group then the questions in that group but after that all it does is display the next group name followed by the same questions then again for all the group. 当前代码显示第一个组的标题,然后显示该组中的问题,但在此之后,将显示下一个组名,然后显示相同的问题,然后再次显示所有组。

It's a no brainer I'm sure but I'm still not skilled enough to spot it. 我敢肯定,这毫无疑问,但是我仍然不够熟练。

You might be able to get by with something like this, but I'd take other's advice about creating a specific view model for this view also. 您也许可以通过这样的方法来解决问题,但是我也会接受其他人的建议,也可以为此视图创建特定的视图模型。

@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 点网小提琴示例

You should not be using complex queries in your view, and while the JamieD77's answer will solve the issue of correctly displaying the items, it will fail to bind to your model when you submit. 您不应在视图中使用复杂的查询,而JamieD77的答案将解决正确显示项目的问题,但在您提交时它将无法绑定到模型。

If you inspect the html you generating you will see that for each group you have inputs such as 如果检查生成的html,将看到每个组都有输入,例如

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

but the DefaultModelBinder requires collection indexers to start at zero and be consecutive so when binding, it will correctly bind the inputs in the first group, but ignore those in all other groups because the indexers starts back at zero. 但是DefaultModelBinder要求集合索引器从零开始并且是连续的,因此在绑定时,它将正确绑定第一组中的输入,但是忽略所有其他组中的输入,因为索引器从零开始。

As always start with view models to represent waht you want to display/edit ( What is ViewModel in MVC? ). 与往常一样,从表示您要显示/编辑的视图模型开始( MVC中的ViewModel是什么? )。 In this case I'm assuming the SelectList options associated with Score are common across all Questions 在这种情况下,我假设与Score相关联的SelectList选项在所有问题中都是相同的

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; }
}

While you could use nested loops in the view 虽然您可以在视图中使用嵌套循环

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)

a better solution is to use EditorTemplates which give you a reusable component (and you would not have to change the collection properties to IList<T> ). 更好的解决方案是使用EditorTemplates ,它为您提供可重用的组件(并且您不必将collection属性更改为IList<T> )。 Note I have omitted <div> elements to keep it simple. 注意,为了简化起见,我省略了<div>元素。

In /Views/Shared/EditorTemplates/QuestionVM.cshtml /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" })

In /Views/Shared/EditorTemplates/QuestionGroupVM.cshtml /Views/Shared/EditorTemplates/QuestionGroupVM.cshtml

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

and the main view would be 而主要的观点是

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

Then in the get method, project your data model to the view model, for example 然后在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);

and the signature of the POST method would be 并且POST方法的签名为

public ActionResult Edit(QuestionnaireVM model)

Side note: You do not currently have an input for the group name property which means if you needed to return the view because ModelState was invalid, you would need to run the query again, so consider adding @Html.HiddenFor(m => m.Name) to the QuestionGroupVM.cshtml template (and of course if you do retur the view, you also need to reassign the SelectList property. 旁注:您当前没有组名属性的输入,这意味着如果由于ModelState无效而需要返回视图,则需要再次运行查询,因此请考虑添加@Html.HiddenFor(m => m.Name)QuestionGroupVM.cshtml模板(当然,如果要重建视图,则还需要重新分配SelectList属性)。

When you displaying your questions you should work with your group object (grouped collection) but not with the initial collection. 显示问题时,应使用group对象(分组集合),而不要使用初始集合。

I mean you should change your 我的意思是你应该改变你的

Model.QuestionList[i]

To

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

Anyway your solution really messy It's better to do such grouping on server side. 无论如何,您的解决方案确实很混乱。最好在服务器端进行此类分组。

Please consider that a View should be completely agnostic to the business logic implemented by the service layer. 请考虑,视图应该与服务层实现的业务逻辑完全无关。 View is just a dummy presentation mechanism which grabs data served by a Controller through a ViewModel and displays the data. View只是一个虚拟的表示机制,它通过ViewModel捕获Controller提供的数据并显示数据。 It is the basic of the MVC architecture and my strong recommendation is that following the architecture is itself a very good reason to go the right way. 这是MVC架构的基础,我强烈建议遵循该架构本身是走正确道路的很好理由。

That being said the view you have is getting messy. 话虽这么说,您的观点却变得凌乱。 Consider reconstructing it as something like this: 考虑将其重构为如下形式:

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; }
}

Construct this server side and just render through simple razor foreach loops. 构造此服务器端,然后通过简单的razor foreach循环进行渲染。 This will not only benefit you with cleaner code but will also help avoid the model binding pain you are about to run into when you post the form back with your current implementation. 这不仅可以使您获得更清晰的代码,而且还可以避免在将表单发布回当前实现时会遇到的模型绑定麻烦。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM