我的剃须刀视图中有以下内容
@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(组密钥)对它们进行分组。
当前代码显示第一组的标题,然后显示该组中的问题,但在此之后,它只显示下一个组名,然后再显示相同的问题,然后再显示所有组。
我很确定,但我仍然没有足够的技巧来发现它。
答案 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>
}
}
答案 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循环进行渲染。这不仅有助于您使用更干净的代码,还有助于避免在您使用当前实现发布表单时遇到的模型绑定问题。