Group a collection in MVC View and update model in

2019-04-12 11:23发布

问题:

I have a View that displays a collection from a ViewModel, where the user can update a property for each collection item ("Level" of proficiency).

Here's the View:

@model Consultants.ViewModels.ProgramsViewModel
@{
    ViewBag.Title = "Programkunskaper";
}
<h2>@ViewBag.Title</h2>

<div id="formDiv">
    @using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
    {
        @Html.ValidationSummary(true)
        <fieldset>
            <legend>Ny arbetserfarenhet</legend>
            <table>
                <tr>
                    <th>
                        Program
                    </th>
                    <th>
                        Nivå
                    </th>
                </tr>
                @Html.EditorFor(m => m.ProgramSkills, "ProgramSkills")
                 @*Using editorformodel instead of for loop according to tip here: http://stackoverflow.com/questions/5420471/use-dropdownlistfor-with-foreach-in-asp-net-mvc-view*@
            </table>
            <p>
                <input type="submit" value="Spara" />
            </p>
        </fieldset>
    }
</div>

And the EditorFor template:

@model Consultants.Models.ProgramSkill
<tr>
    <td>@Model.Program.Name
    </td>
    <td>
        @Html.DropDownListFor(
            model => model.Level,
                     new SelectList(new[] { 0, 1, 2, 3, 4, 5 },
                Model.Level
            )
        )
    </td>
</tr>

Here are my action methods (I know, using the Index view seems a little weird and I'll probably change that, but never mind that for now):

    public ActionResult Index()
    {
        Consultant consultant = _repository.GetConsultantByUserName(User.Identity.Name);
        ViewBag.Consultant = consultant;
        if (consultant.ProgramSkills.Count == 0)
        {
            List<Program> programs = _repository.GetAllPrograms();
            foreach (var program in programs)
            {
                consultant.ProgramSkills.Add(new ProgramSkill { Program = program });
            }
            _repository.Save();
        }
        ProgramsViewModel vm = new ProgramsViewModel();
        vm.ProgramSkills = consultant.ProgramSkills.ToList();
        return View(vm);
    }

    [HttpPost]
    public ActionResult Index(ProgramsViewModel vm, FormCollection collection)
    {
        Consultant consultant = _repository.GetConsultantByUserName(User.Identity.Name);
        List<ProgramSkill> programSkills = consultant.ProgramSkills.ToList();
        for (int i = 0; i < programSkills.Count; i++)
        {
            var programSkill = programSkills[i];
            programSkill.Level = vm.ProgramSkills[i].Level;
        }
        _repository.Save();
        vm.ProgramSkills = programSkills;
        return View(vm);
    }

Now, even though this works fine, I realized that I need to be able to group the ProgramSkill items by it's Program.Category property. But then I'll get a couple of problems:

How do I group them and pass them in a ViewModel to the View?

The objects are Program, ProgramSkill and Consultant, where there's a Many to Many relationship between Program and Consultant, and ProgramSkill is the junction table (needed because it has the additional property "Level").

And if I were to have a Linq expression to group them according to this, and could make it into a ViewModel that the View can consume, how would I update the model again after posting back to the Controller?

It seems the code will get a lot more complicated, if it can be done at all, just for categorizing the ProgramSkill items by Program.Category. But at the same time, the View will have very poor usability if the items are not categorized...

Anyone know a good solution for this?

回答1:

I think what you want to do is add another view model

ProgramSkillsModel
{
    string Category {get;set;}
    IList<ProgramSkill> Skills {get;set;}
}

Then in your controller Index()

ProgramsViewModel vm = new ProgramsViewModel();        
vm.ProgramSkills = consultant.ProgramSkills.GroupBy(c => c.Category)
                     .Select(c => new ProgramSkillsModel{
Category = c.Key,
Skills = c.ToList(),
});

Hopefully that works. It's just pseudo code