简体   繁体   中英

C# MVC Get List of Models from View

I have a List of Models which i display in the view within a form. That works so on but if i click on save i got the model back but the list is empty. Has anyone a idea?

View:

@model AdminFeatureSetsModel

@{
    Layout = "~/Views/Shared/_AdministrationLayout.cshtml";
}

@section headerTitle
{
<div>Edit Feature Settings</div>
<br />
<br />
}

@using (Html.BeginForm("Save", "AdminFeatureSets", FormMethod.Post, new { id = "EditFeatureSet", name = "EditFeatureSet", @class = "form-horizontal" }))
{
    <div class="grey-border-generic" id="EditFeatureSet">
        <div class="col-sm-2">
            <b> Feature Name</b>
        </div>
        <div class="col-sm-2">
            <b> Enabled</b>
        </div>
        <div class="col-sm-2">
            <b> Verify</b>
        </div>
        <div class="col-sm-2">
            <b> Enabled By User</b>
        </div>
        <div class="col-sm-2">
            <b>Enabled By Organization </b>
        </div>
        <br />
        <br />
        @for (int i = 0; i < Model.FeatureSets.Count; i++)
        {

            if (Model.FeatureSets[i].IsFeatureInDB == true)
            {
                <div class="col-sm-2">
                    <b> @Model.FeatureSets[i].Name</b>
                </div>
                <div class="col-sm-2">
                    @Html.TextBoxFor(m => Model.FeatureSets[i].Enabled, new { @class = "form-control", @id = "EditEnable" })
                </div>
                <div class="col-sm-2">
                    @Html.TextBoxFor(m => Model.FeatureSets[i].Verify, new { @class = "form-control", @id = "EditVerify" })
                </div>
                <div class="col-sm-2">
                    @Html.TextBoxFor(m => Model.FeatureSets[i].EnabledByUser, new { @class = "form-control", @id = "EditEnableByUser" })
                </div>
                <div class="col-sm-2">
                    @Html.TextBoxFor(m => Model.FeatureSets[i].EnabledByOrganisation, new { @class = "form-control", @id = "EditEnableByOrganization" })
                </div>
                <br />
                <br />
            }
            else
            {
                <div class="col-sm-2">
                    <b> @Model.FeatureSets[i].Name</b>
                </div>
                <div class="col-sm-2" style="color:red">
                    <b> Not in database</b>
                </div>
                <br />
                <br />
            }


        }

                <button class="btn btn-xyzmo-secondary " id="saveEditsBtn">Save</button>
    </div>
}

Controller: In the function save by the foreach(model.FeatureSets) the FeatureSets (List) is always empty.

public ActionResult Index()
        {
            AdminFeatureSetsModel modelFeatureSets = new AdminFeatureSetsModel();
            modelFeatureSets.FeatureSets = new List<FeatureSetModel>();
            var featureInDB = DbContext.GetAllEntitiesQueryable<Feature>();

            var clazz = typeof(FeatureSet);
            foreach (var property in clazz.GetProperties())
            {
                FeatureSetModel modelFeatureSet = new FeatureSetModel();
                var feature = featureInDB.FirstOrDefault(f => f.Name == property.Name);
                if (feature != null)
                {
                    modelFeatureSet.Name = feature.Name;
                    modelFeatureSet.Enabled = feature.Enabled;
                    modelFeatureSet.Verify = feature.Verify;
                    modelFeatureSet.EnabledByUser = feature.EnableByUser;
                    modelFeatureSet.EnabledByOrganisation = feature.EnableByOrganization;
                    modelFeatureSet.IsFeatureInDB = true;
                }else
                {
                    modelFeatureSet.Name = property.Name;
                    modelFeatureSet.IsFeatureInDB = false;
                }
                modelFeatureSets.FeatureSets.Add(modelFeatureSet);             

            }
            return View(modelFeatureSets);   
        }

        public ActionResult Save(AdminFeatureSetsModel model)
        {

            var featureInDB = DbContext.GetAllEntitiesQueryable<Feature>();
            foreach (var featureSet in model.FeatureSets) // here the FeatureSet of the model is always empty
            {
                if (featureSet.IsFeatureInDB == true)
                {
                    var feature = featureInDB.FirstOrDefault(f => f.Name == featureSet.Name);
                    feature.Enabled = featureSet.Enabled;
                    feature.Verify = featureSet.Verify;
                    feature.EnableByUser = featureSet.EnabledByUser;
                    feature.EnableByOrganization = featureSet.EnabledByOrganisation;
                }
            }
            DbContext.SaveChanges();
            return RedirectToAction("Index", "AdminFeatureSet");
        }

Model with the List:

public class AdminFeatureSetsModel :AdminBaseModel
    {
        public List<FeatureSetModel> FeatureSets { get; set; }    
    }

Model FeatureSet:

public class FeatureSetModel : AdminBaseModel
    {
        public string Name { get; set; }

        public bool Enabled { get; set; }
        public bool Verify { get; set; }
        public bool EnabledByUser { get; set; }
        public bool EnabledByOrganisation { get; set; }

        public bool IsFeatureInDB { get; set; }

        public List<User> EnabledUserList { get;set;}
        public List<Organization> EnabledOrginaizationList { get; set; }

    }

FeatureSetModels的视图


Edit

     @Html.HiddenFor(m => m.FeatureSets[i].IsFeatureInDB)
                if (Model.FeatureSets[i].IsFeatureInDB == true)
                {
                    @*<input type="hidden" name="FeatureSets.Index" value="@i" />*@ 
                    <div class="col-sm-2">
                        <b> @Model.FeatureSets[i].Name</b>
                    </div>
                    <div class="col-sm-2">
                        @Html.CheckBoxFor(m => Model.FeatureSets[i].Enabled)
                    </div>
                    <div class="col-sm-2">
                        @Html.CheckBoxFor(m => Model.FeatureSets[i].Verify)
                    </div>
                    <div class="col-sm-2">
                        @Html.CheckBoxFor(m => Model.FeatureSets[i].EnabledByUser)
                    </div>
                    <div class="col-sm-2">
                        @Html.CheckBoxFor(m => Model.FeatureSets[i].EnabledByOrganisation)
                    </div>
                    <br />
                    <br />
                }
                else
                {
                    @Model.FeatureSets[i].Name
                    @Html.HiddenFor(m => m.FeatureSets[i].Enabled)
                    @Html.HiddenFor(m => m.FeatureSets[i].Verify)
                    @Html.HiddenFor(m => m.FeatureSets[i].EnabledByUser)
                    @Html.HiddenFor(m => m.FeatureSets[i].EnabledByOrganisation)
}

The reason its not binding is because of your

if (Model.FeatureSets[i].IsFeatureInDB == true)

code. If the value is false, you do not generate any form controls for that FeatureSetModel so you post back non-consecutive indexed objects.

By default the DefaultModelBinder requires collection indexers to start at zero and be consecutive. If the 1st item in the collection has IsFeatureInDB = false then the collection will be empty. (if the 1st 2 items were true and the 3rd was false , then you would get only 2 items in the collection)

One way to solve this would be to add hidden inputs for each property of FeatureSetModel inside the else block.

@for (int i = 0; i < Model.FeatureSets.Count; i++)
{
    @Html.HiddenFor(m => m[i].IsFeatureInDB ) // add this so its value posts back
    if (Model.FeatureSets[i].IsFeatureInDB == true)
    {
        @Model.FeatureSets[i].Name
        @Html.TextBoxFor(m => m[i].Enabled, new { @class = "form-control" }) // remove id attribute
        ....
    }
    else
    {
        @Model.FeatureSets[i].Name</b>
        @Html.HiddenFor(m => m[i].Enabled) // add hidden inputs
        ....
    }

Another alternative, if you do not want the objects with IsFeatureInDB = false to be included in the collection when you submit is to include a hidden input for the collection indexer, which does allow non-zero/non-consecutive indexers to be bound

if (Model.FeatureSets[i].IsFeatureInDB == true)
{
    <input type="hidden" name="FeatureSets.Index" value="@i" /> // add this
    <div class="col-sm-2">
        <b> @Model.FeatureSets[i].Name</b>
    </div>
    <div class="col-sm-2">
        @Html.TextBoxFor(m => Model.FeatureSets[i].Enabled, new { @class = "form-control" }) // remove the invalid id attributes
    </div>

Note also that you need to include a hidden input for the IsFeatureInDB property. Without it, the value of that property will always be false in the POST method and the code in your loop will not be executed

Side note: Remove the new { @id = "xxx" code from your helpers. Duplicate id attributes are invalid html.

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