简体   繁体   中英

MVC C#: Best way to bind a “list of dropdowns” with model

I have a model I'm not being able to bind correctly with my view so I need some help in order to go in the right direction.

I'm working with "Accidents", and an accident has some properties like year, type of collision, etc, but in my model it also has a list of injuries, and each of them is selected from a dropdown list (where a user selects from a list of almost 100 possible injuries).

In a way, we could say I have a "List of Dropdowns" in my model (what is obviously not a list of dropdowns but I call it that way so you have an idea where my problem begins, that is modelling that "list of lists").

This is my _InjuriesSelect template in Shared views folder:

@model xxx.Models.AccidentViewModel

<div class="col-12 form-group text-dark mx-auto my-auto pb-2">
    <div class="container" style="padding-left:0;">
        <div class="row">
            <div class="col">
                @*@Html.DropDownListFor(model => model.Accident.Injuries, Model.Injuries, Model.SelectInjuryText, new { @class = "form-control", @style = "width: 300px;", onchange = @"" })*@
                @Html.DropDownList("selectInjuries_1", Model.InjuriesSelect, Model.SelectInjuryText, new { @class = "form-control", @style = "width: 300px;", onchange = @"" })
                @Html.ValidationMessageFor(model => model.Accident.Injuries, "", new { @class = "text-danger font-weight-bold" })
            </div>
            <div class="col">
                <a id="removeInjury_1" href="javascript: void(0);" style="text-decoration: none; visibility: hidden;" onclick="javascript: removeInjury(this);">
                    <i class="fa fa-minus text-danger pr-3" aria-hidden="true"></i>
                </a>
                <a id="addInjury_1" href="javascript: void(0);" style="text-decoration: none; visibility: hidden;" onclick="javascript: addInjury(this);">
                    <i class="fa fa-plus text-success" aria-hidden="true"></i>
                </a>
            </div>
        </div>
    </div>
</div>

I allow the user to add (or remove) dropdowns to add / remove injuries with javascript via:

function addInjury() {

    $.post("/Home/LoadInjurySelect", $("form").serialize(), function (data) {
        lastInjuryIndex++;

        //add holder for new injury
        var newInjurydivId = "divInjuries_" + lastInjuryIndex;
        var newInjuryDivHtml = "<div id='" + newInjurydivId + "'>";
        newInjuryDivHtml += data.replace(/_1/g, "_" + lastInjuryIndex);
        newInjuryDivHtml += "</div>";

        $("#divInjuries").append(newInjuryDivHtml);

        //hide "add injury" from previous injury
        $("#addInjury_" + (lastInjuryIndex - 1).toString()).css("visibility", "hidden");

        $("#selectInjuries_" + lastInjuryIndex).change(function () {
            var selectedInjury = $('#selectInjuries_' + lastInjuryIndex + ' option:selected').val();
            $("#addInjury_" + lastInjuryIndex).css("visibility", selectedInjury != "" ? "visible" : "hidden")
            //
            setInjuryInModel(selectedInjury);
        });


        setRemoveInjury1Visibility();
        setRemoveInjuryNVisibility(lastInjuryIndex);
    });
}

This javascript calls controller and returns a partial view in this way:

[HttpPost]
public ActionResult LoadInjurySelect(AccidentViewModel model)
{
    model = fillViewModelLists(model);
    return PartialView("~/Views/Shared/_InjuriesSelect.cshtml", model);
}

This method returns the partial view but first fills all the view model IEnumerable Lists used to fill dropdowns (if not they get lost as jQuery form.serialize doesn't include the lists when serializing).

My Accident model looks like this:

using xxx.App_GlobalResources;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace xxx.Models
{
    public class Accident
    {
        //Person information

        [Display(Name = "home_accident_personname", ResourceType = typeof(languages))]
        //[Required(ErrorMessageResourceType = typeof(App_GlobalResources.languages), ErrorMessageResourceName = "home_accident_mustenterpersonname")]
        public string PersonName { get; set; }

        [Display(Name = "home_accident_personage", ResourceType = typeof(languages))]
        [Required(ErrorMessageResourceType = typeof(languages), ErrorMessageResourceName = "home_accident_agenotvalid")]
        public short PersonAge { get; set; }

        [Display(Name = "home_accident_personcondition", ResourceType = typeof(languages))]
        [Required(ErrorMessageResourceType = typeof(languages), ErrorMessageResourceName = "home_accident_conditionnotvalid")]
        public byte PersonCondition { get; set; }

        [Display(Name = "home_accident_personemail", ResourceType = typeof(languages))]
        //[Required(ErrorMessageResourceType = typeof(App_GlobalResources.languages), ErrorMessageResourceName = "home_accident_mustenterpersonemail")]
        [DataType(DataType.EmailAddress)]
        [EmailAddress]
        public string PersonEmail { get; set; }

        //Accident information

        [Display(Name = "home_accident_year", ResourceType = typeof(languages))]
        [Required(ErrorMessageResourceType = typeof(languages), ErrorMessageResourceName = "home_accident_yearnotvalid")]
        [Range(2016, 2019)]
        public short Year { get; set; }        

        [Display(Name = "home_accident_collisiontype", ResourceType = typeof(languages))]
        [Required(ErrorMessageResourceType = typeof(languages), ErrorMessageResourceName = "home_accident_collisiontypenotvalid")]
        public byte CollisionType { get; set; }

        [Display(Name = "home_accident_hospitalleave", ResourceType = typeof(languages))]
        [Range(0, int.MaxValue, ErrorMessageResourceType = typeof(languages), ErrorMessageResourceName = "home_accident_medicalleavenotvalid")]
        public short HospitalLeaveDays { get; set; }

        [Display(Name = "home_accident_workleave", ResourceType = typeof(languages))]
        [Range(0, int.MaxValue, ErrorMessageResourceType = typeof(languages), ErrorMessageResourceName = "home_accident_medicalleavenotvalid")]
        public short WorkLeaveDays { get; set; }

        [Display(Name = "home_accident_rehabilitationdays", ResourceType = typeof(languages))]
        [Range(0, int.MaxValue, ErrorMessageResourceType = typeof(languages), ErrorMessageResourceName = "home_accident_medicalleavenotvalid")]
        public short RehabilitationDays { get; set; }

        [Display(Name = "home_accident_injuries", ResourceType = typeof(languages))]
        public List<Injury> Injuries { get; set; }

        //Compensation

        public Compensation Compensation { get; set; }
    }
}

And my AccidentViewModel looks like this (It's rather long due to the big number of possible injuries):

using xxx.App_GlobalResources;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Linq;

namespace xxx.Models
{
    public class AccidentViewModel
    {
        public Accident Accident { get; set; }

        public string SelectInjuryText { get { return languages.home_accident_selectinjury; } }

        /* VIEWMODEL LISTS / METHODS*/

        public IEnumerable<SelectListItem> Years { get; set; }

        public static IEnumerable<SelectListItem> fillYearsList()
        {
            List<SelectListItem> listItems = new List<SelectListItem>();
            int y = DateTime.Now.Year;
            for (int i = y; i >= y - 3; i--)
            {
                listItems.Add(new SelectListItem
                {
                    Text = i.ToString(),
                    Value = i.ToString()
                });
            }

            return listItems;
        }

        public IEnumerable<SelectListItem> Ages { get; set; }

        public static IEnumerable<SelectListItem> fillAgesList()
        {
            List<SelectListItem> listItems = new List<SelectListItem>();
            listItems.Add(new SelectListItem { Text = "", Value = "" });
            listItems.Add(new SelectListItem { Text = "0-16", Value = "900" });
            listItems.Add(new SelectListItem { Text = "16-25", Value = "850" });
            listItems.Add(new SelectListItem { Text = "25-35", Value = "800" });
            listItems.Add(new SelectListItem { Text = "35-45", Value = "750" });
            listItems.Add(new SelectListItem { Text = "45-65", Value = "700" });
            listItems.Add(new SelectListItem { Text = "65+", Value = "650" });

            return listItems;
        }

        public IEnumerable<SelectListItem> Conditions { get; set; }

        public static IEnumerable<SelectListItem> fillConditionsList()
        {
            List<SelectListItem> listItems = new List<SelectListItem>();
            listItems.Add(new SelectListItem { Text = "", Value = "" });
            listItems.Add(new SelectListItem { Text = languages.home_accident_condition_driver, Value = "1" });
            listItems.Add(new SelectListItem { Text = languages.home_accident_condition_companion, Value = Helpers.Utils.getFromWebConfig("successPercentageCompanion") });
            listItems.Add(new SelectListItem { Text = languages.home_accident_condition_pedestrian, Value = Helpers.Utils.getFromWebConfig("successPercentagePedestrian") });

            return listItems;
        }

        public IEnumerable<SelectListItem> Collisions { get; set; }

        public static IEnumerable<SelectListItem> fillCollisionsList()
        {
            List<SelectListItem> listItems = new List<SelectListItem>();
            listItems.Add(new SelectListItem { Text = "", Value = "" });
            listItems.Add(new SelectListItem { Text = languages.home_accident_collisionfront, Value = Helpers.Utils.getFromWebConfig("successPercentageCollisionFront") });
            listItems.Add(new SelectListItem { Text = languages.home_accident_collisionside, Value = Helpers.Utils.getFromWebConfig("successPercentageCollisionSide") });
            listItems.Add(new SelectListItem { Text = languages.home_accident_collisionrear, Value = Helpers.Utils.getFromWebConfig("successPercentageCollisionBack") });

            return listItems;
        }

        //

        public List<InjuryGroup> InjuriesGroups { get; set; }

        public static List<InjuryGroup> fillInjuriesGroupsList()
        {

            List<InjuryGroup> injuriesGroupsList = new List<InjuryGroup>();

            injuriesGroupsList.Add(new InjuryGroup { Id = 1, Name = InjuryGroups.Lesiones_AfectacionesNerviosas });
            injuriesGroupsList.Add(new InjuryGroup { Id = 2, Name = InjuryGroups.Lesiones_TranstornosPsicologicos });
            injuriesGroupsList.Add(new InjuryGroup { Id = 3, Name = InjuryGroups.Lesiones_LesionesOculares });
            injuriesGroupsList.Add(new InjuryGroup { Id = 4, Name = InjuryGroups.Lesiones_LesionesAuditivas });
            injuriesGroupsList.Add(new InjuryGroup { Id = 5, Name = InjuryGroups.Lesiones_LesionesNasales });
            injuriesGroupsList.Add(new InjuryGroup { Id = 6, Name = InjuryGroups.Lesiones_LesionesMandibulares });
            injuriesGroupsList.Add(new InjuryGroup { Id = 7, Name = InjuryGroups.Lesiones_LesionesCervicales });
            injuriesGroupsList.Add(new InjuryGroup { Id = 8, Name = InjuryGroups.Lesiones_LesionesExtremidadesSuperiores });
            injuriesGroupsList.Add(new InjuryGroup { Id = 9, Name = InjuryGroups.Lesiones_LesionesExtremidadesInferiores });
            injuriesGroupsList.Add(new InjuryGroup { Id = 10, Name = InjuryGroups.Lesiones_LesionesRodilla });
            injuriesGroupsList.Add(new InjuryGroup { Id = 11, Name = InjuryGroups.Lesiones_LesionesTobillo });
            injuriesGroupsList.Add(new InjuryGroup { Id = 12, Name = InjuryGroups.Lesiones_LesionesPie });
            injuriesGroupsList.Add(new InjuryGroup { Id = 13, Name = InjuryGroups.Lesiones_LesionesCadera });
            injuriesGroupsList.Add(new InjuryGroup { Id = 14, Name = InjuryGroups.Lesiones_LesionesCardiacas });
            injuriesGroupsList.Add(new InjuryGroup { Id = 15, Name = InjuryGroups.Lesiones_LesionesOrganoDigestivo });
            injuriesGroupsList.Add(new InjuryGroup { Id = 16, Name = InjuryGroups.Lesiones_LesionesSistemaUrinario });
            injuriesGroupsList.Add(new InjuryGroup { Id = 17, Name = InjuryGroups.Lesiones_Quemaduras });
            injuriesGroupsList.Add(new InjuryGroup { Id = 18, Name = InjuryGroups.Lesiones_LesionesNeurologicasMedulares });
            injuriesGroupsList.Add(new InjuryGroup { Id = 19, Name = InjuryGroups.Lesiones_LesionesRespitarorias });

            return injuriesGroupsList;
        }

        public List<SelectListGroup> InjuriesSelectGroups { get; set; }

        public static List<SelectListGroup> fillInjuriesSelectListGroup(AccidentViewModel model) {

            List<SelectListGroup> injuriesSelectListGroup = new List<SelectListGroup>();
            foreach (InjuryGroup injuryGroup in model.InjuriesGroups) {
                injuriesSelectListGroup.Add(new SelectListGroup { Name = injuryGroup.Name });
            }

            return injuriesSelectListGroup;
        }

        //

        public List<Injury> Injuries { get; set; }        

        public static List<Injury> fillInjuriesList(AccidentViewModel model)
        {
            InjuryGroup Lesiones_AfectacionesNerviosas = model.InjuriesGroups[0];
            InjuryGroup Lesiones_TranstornosPsicologicos = model.InjuriesGroups[1];
            InjuryGroup Lesiones_LesionesOculares = model.InjuriesGroups[2];
            InjuryGroup Lesiones_LesionesAuditivas = model.InjuriesGroups[3];
            InjuryGroup Lesiones_LesionesNasales = model.InjuriesGroups[4];
            InjuryGroup Lesiones_LesionesMandibulares = model.InjuriesGroups[5];
            InjuryGroup Lesiones_LesionesCervicales = model.InjuriesGroups[6];
            InjuryGroup Lesiones_LesionesExtremidadesSuperiores = model.InjuriesGroups[7];
            InjuryGroup Lesiones_LesionesExtremidadesInferiores = model.InjuriesGroups[8];
            InjuryGroup Lesiones_LesionesRodilla = model.InjuriesGroups[9];
            InjuryGroup Lesiones_LesionesTobillo = model.InjuriesGroups[10];
            InjuryGroup Lesiones_LesionesPie = model.InjuriesGroups[11];
            InjuryGroup Lesiones_LesionesCadera = model.InjuriesGroups[12];
            InjuryGroup Lesiones_LesionesCardiacas = model.InjuriesGroups[13];
            InjuryGroup Lesiones_LesionesRespitarorias = model.InjuriesGroups[14];
            InjuryGroup Lesiones_LesionesOrganoDigestivo = model.InjuriesGroups[15];
            InjuryGroup Lesiones_LesionesSistemaUrinario = model.InjuriesGroups[16];
            InjuryGroup Lesiones_Quemaduras = model.InjuriesGroups[17];
            InjuryGroup Lesiones_LesionesNeurologicasMedulares = model.InjuriesGroups[18];

            List<Injury> injuries = new List<Injury>();

            injuries.Add(new Injury { Id = 1, Text = languages.Lesiones_AfectacionesNerviosas_AfectacionNerviosaAuditiva, Value = int.Parse(Helpers.Utils.getFromWebConfig("Lesiones_AfectacionesNerviosas_AfectacionNerviosaAuditiva")), Group = Lesiones_AfectacionesNerviosas });
            injuries.Add(new Injury { Id = 2, Text = languages.Lesiones_AfectacionesNerviosas_AfectacionNerviosaOcular, Value = int.Parse(Helpers.Utils.getFromWebConfig("Lesiones_AfectacionesNerviosas_AfectacionNerviosaOcular")), Group = Lesiones_AfectacionesNerviosas });
            injuries.Add(new Injury { Id = 3, Text = languages.Lesiones_AfectacionesNerviosas_AfectacionNervioCiatico, Value = int.Parse(Helpers.Utils.getFromWebConfig("Lesiones_AfectacionesNerviosas_AfectacionNervioCiatico")), Group = Lesiones_AfectacionesNerviosas });
            injuries.Add(new Injury { Id = 4, Text = languages.Lesiones_AfectacionesNerviosas_AfectacionNerviosaMandibular, Value = int.Parse(Helpers.Utils.getFromWebConfig("Lesiones_AfectacionesNerviosas_AfectacionNerviosaMandibular")), Group = Lesiones_AfectacionesNerviosas });
            injuries.Add(new Injury { Id = 5, Text = languages.Lesiones_AfectacionesNerviosas_AfectacionNerviosaRadioCubito, Value = int.Parse(Helpers.Utils.getFromWebConfig("Lesiones_AfectacionesNerviosas_AfectacionNerviosaRadioCubito")), Group = Lesiones_AfectacionesNerviosas });
            injuries.Add(new Injury { Id = 6, Text = languages.Lesiones_AfectacionesNerviosas_AfectacionNerviosaTibia, Value = int.Parse(Helpers.Utils.getFromWebConfig("Lesiones_AfectacionesNerviosas_AfectacionNerviosaTibia")), Group = Lesiones_AfectacionesNerviosas });

            ...

            return injuries;
        }

        public IEnumerable<SelectListItem> InjuriesSelect { get; set; }

        public static IEnumerable<SelectListItem> fillInjuriesSelectList(AccidentViewModel model)
        {
            List<SelectListItem> injuriesSelectList = new List<SelectListItem>();

            foreach (InjuryGroup injuryGroup in model.InjuriesGroups) {
                foreach (Injury injury in model.Injuries.Where(x => x.Group == injuryGroup).ToList()) {
                    injuriesSelectList.Add(new SelectListItem { Text = injury.Text, Value = injury.Id.ToString(), Group = model.InjuriesSelectGroups.Where(x => x.Name == injuryGroup.Name).First() });
                }
            }

            return injuriesSelectList;
        }
    }

Injury model:

using xxx.App_GlobalResources;

namespace xxx.Models
{
    public class Injury
    {
        public int Id { get; set; }
        public string Text { get; set; }
        public int Value { get; set; }
        public InjuryGroup Group { get; set; }
    }

    public static class InjuryGroups
    {
        public static string Lesiones_AfectacionesNerviosas { get { return languages.Lesiones_AfectacionesNerviosas; } }
        public static string Lesiones_TranstornosPsicologicos { get { return languages.Lesiones_TranstornosPsicologicos; } }
        public static string Lesiones_LesionesOculares { get { return languages.Lesiones_LesionesOculares; } }
        public static string Lesiones_LesionesAuditivas { get { return languages.Lesiones_LesionesAuditivas; } }
        public static string Lesiones_LesionesNasales { get { return languages.Lesiones_LesionesNasales; } }
        public static string Lesiones_LesionesMandibulares { get { return languages.Lesiones_LesionesMandibulares; } }
        public static string Lesiones_LesionesCervicales { get { return languages.Lesiones_LesionesCervicales; } }
        public static string Lesiones_LesionesExtremidadesSuperiores { get { return languages.Lesiones_LesionesExtremidadesSuperiores; } }
        public static string Lesiones_LesionesExtremidadesInferiores { get { return languages.Lesiones_LesionesExtremidadesInferiores; } }
        public static string Lesiones_LesionesRodilla { get { return languages.Lesiones_LesionesRodilla; } }
        public static string Lesiones_LesionesTobillo { get { return languages.Lesiones_LesionesTobillo; } }
        public static string Lesiones_LesionesPie { get { return languages.Lesiones_LesionesPie; } }
        public static string Lesiones_LesionesCadera { get { return languages.Lesiones_LesionesCadera; } }
        public static string Lesiones_LesionesCardiacas { get { return languages.Lesiones_LesionesCardiacas; } }
        public static string Lesiones_LesionesRespitarorias { get { return languages.Lesiones_LesionesRespitarorias; } }
        public static string Lesiones_LesionesOrganoDigestivo { get { return languages.Lesiones_LesionesOrganoDigestivo; } }
        public static string Lesiones_LesionesSistemaUrinario { get { return languages.Lesiones_LesionesSistemaUrinario; } }
        public static string Lesiones_Quemaduras { get { return languages.Lesiones_Quemaduras; } }
        public static string Lesiones_LesionesNeurologicasMedulares { get { return languages.Lesiones_LesionesNeurologicasMedulares; } }
    }
}

InjuryGroup model:

using System.Collections.Generic;

namespace compensationCalc.Models { public class InjuryGroup { public int Id { get; set; } public string Name { get; set; } public List Injuries { get; set; } } }

In the end, what I'm not being able to do is to bind the dropdowns of injuries with the model so when the form is submitted the list on injuries contains all the selected injuries, in other words, as the user selects (adds) a new injury the model updates its injuries list with the corresponding value.

What I also don't understand (this is conceptual) is that if I add injuries dropdowns with a template via a PartialView and I do this via a javascript post call, what happens with the new added dropdowns IDs into the html? Do I have to rename them with javascript so the new dropdown id is not repeated in DOM every time I add a new injury?

The most important is to have some help on the best way to bind my "list of dropdowns" with my accident model (and to keep each new added dropdown binded with the model too, of course).

I know I should use DropDownListFor instead of DropDownList, but I'm not sure how to do it this way.

Thanks.

Well, I'll end answering my own question in case it helps anyone else facing something similar.

If you have a List in your model one way to handle it is the next:

In model I add a new property to hold SelectedInjury (in my case) from the dropdown.

public int SelectedInjury { get; set; }

In view I create the DropDownList this way:

@Html.DropDownListFor(model => model.Accident.Injuries[0].SelectedInjury, Model.Injuries, Model.SelectInjuryText, new { @class = "form-control", @style = "width: 300px;", onchange = @"" })

Then, in javascript I have a Injury counter and whenever adding new DropDowns (that come from the controller from a template via an ajax call) I just replace the "0" in Accident_Injuries[0] (this is more or less the generated html id) with the corresponding new index before adding the html to DOM. This way when you select a new item in the dropdown a new Injury object is autommatically added to the list so the model is updated without the need to do an ajax call to controller or anything else (this was my mail issue).

Cheers, and happy coding!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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