显示对象列表并为每个输入MVC设置值字段

时间:2016-07-05 15:08:50

标签: c# asp.net-mvc model-binding

我将以下类定义为我的ViewModel

 public class CreateApplicationViewModel
    {
        public Step1ViewModel Step1 { get; set; }
        public Step2StandAloneViewModel Step2StandAlone { get; set; }
        public Step2ChildViewModel Step2Child { get; set; }
        public Step3ViewModel Step3 { get; set; }
        public Step4ViewModel Step4 { get; set; }
    }

我试图在Step4ViewModel中显示由以下内容组成的项目:

 public class Step4ViewModel
    {
        public List<DataDetails> DataDetails = new List<DataDetails>();

    }

public class DataDetails
    {
        public string GroupCode { get; set; }
        public string GroupDesc { get; set; }
        public decimal DetailSequence { get; set; }
        public string DetailCode { get; set; }
        public string DetailDesc { get; set; }
        public string YesNoFlag { get; set; }
        public string NumberFlag { get; set; }
        public string ValueFlag { get; set; }
        public string DateFlag { get; set; }
        public string ListValuesFlag { get; set; }
        public string CommentFlag { get; set; }
        public string CalcRateFlag { get; set; }
        public string ColumnSequence { get; set; }
        public string TextFlag { get; set; }
        public string CheckboxFlag { get; set; }
        public string YesNoValue { get; set; }
        public int NumberValue { get; set; }
        public DateTime DateValue { get; set; }
        public string ListValue { get; set; }
        public string CommentValue { get; set; }
        public string TextValue { get; set; }
        public bool CheckboxValue { get; set; }
    }

在我的控制器中,我填充了Step4ViewModel.DataDetails,如下所示:

private Step4ViewModel GetCaseDataDetails(string caseType)
        {
            Step4ViewModel model = new Step4ViewModel();

            List<DataDetails> data = new List<DataDetails>();
            List<DataDetailsValues> values = new List<DataDetailsValues>();

            var dataDetails = (from tb1 in db.DEFAULT_CASE_DATA_VW
                               join tb2 in db.CASE_DATA_DETAIL on tb1.CASE_DATA_GROUP_ID equals tb2.CASE_DATA_GROUP_ID
                               where tb1.BUS_CASE_CODE == caseType
                               orderby tb2.DETAIL_SEQUENCE
                               select new { tb1, tb2 });


            foreach (var detail in dataDetails.ToList())
            {
                DataDetails i = new DataDetails();
                DataDetailsValues j = new DataDetailsValues();
                i.CalcRateFlag = detail.tb2.CALC_RATE_FLAG;
                i.CheckboxFlag = detail.tb2.CHECKBOX_FLAG;
                i.ColumnSequence = detail.tb2.COLUMN_SEQUENCE;
                i.CommentFlag = detail.tb2.COMMENT_FLAG;
                i.DateFlag = detail.tb2.DATE_FLAG;
                i.DetailCode = detail.tb2.DETAIL_CODE;
                i.DetailDesc = detail.tb2.DETAIL_DESC;
                i.DetailSequence = detail.tb2.DETAIL_SEQUENCE;
                i.GroupCode = detail.tb1.GROUP_CODE;
                i.GroupDesc = detail.tb1.GROUP_DESC;
                i.ListValuesFlag = detail.tb2.LIST_VALUES_FLAG;
                i.NumberFlag = detail.tb2.NUMBER_FLAG;
                i.TextFlag = detail.tb2.TEXT_FLAG;
                i.ValueFlag = detail.tb2.VALUE_FLAG;
                i.YesNoFlag = detail.tb2.YES_NO_FLAG;
                data.Add(i);
            }
            model.DataDetails = data;
            return model;
        }

我对Step4ViewModel的思考过程是,对于每个DataDetail,我会将DetailDesc显示为标签,然后在其旁边我将输入NumberValue,YesOrNoValue,NumberValue,DateValue,ListValue,CommentValue,TextValue或CheckboxValue取决于控件类型,然后将该数据发布到服务器。我能够成功显示每个DataDetail.DetailDesc,但对于每个也呈现的输入,我输入到输入中的值永远不会回发到服务器。以下是我的观点:

@model Portal.Models.ViewModel.CreateApplicationViewModel
@{
    ViewBag.Title = "Step 4/5";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@using System.Linq

<h4>Case Data Details</h4>

@using (Html.BeginForm("Step4", "CreateApplication", FormMethod.Post, new { @class = "col-sm-12" }))
{

    foreach (var group in Model.Step4.DataDetails.GroupBy(item => item.GroupDesc))
    {
        <div class="panel panel-primary">
            <div class="panel-heading">@Html.Encode(group.Key)</div>
            <div class="panel-body">

                @for (var i = 0; i < group.Count(); i++)
                { 
                    <div class="form-group">
                        <div class="row">
                            <div class="col-xs-6">
                                <label class="form-label">@Model.Step4.DataDetails[i].DetailDesc</label>
                            </div>
                            <div class="col-xs-6">
                                @if (Model.Step4.DataDetails[i].TextFlag == "Y")
                                {
                                    @Html.TextBoxFor(val => Model.Step4.DataDetails[i].TextValue, new { @class = "form-control" })
                                }
                                else if (Model.Step4.DataDetails[i].CheckboxFlag == "Y")
                                {
                                    @Html.CheckBoxFor(val => Model.Step4.DataDetails[i].CheckboxValue, new { @class = "checkbox" })
                                }
                            </div>
                        </div>
                    </div>
                }
            </div>
        </div>
    }
    <div class="col-sm-12">
        <div class="row">
            @Html.ActionLink("Cancel", "Welcome", "Home", null, new { @class = "btn btn-default" })
            <button class="btn btn-default" onclick="history.go(-1);">Previous</button>
            <button type="submit" class="btn btn-default">Next</button>
        </div>
    </div>

发布数据的控制器

[HttpPost] 
public ActionResult Step4(Step4ViewModel step4) 
{ 
    if (ModelState.IsValid) 
    { 
        CreateApplicationViewModel model = (CreateApplicationViewModel)Session["case"]; 
        // model.Step4 = step4; 
        Session["case"] = model; 
        return View(); 
    } 
    return View(); 
}

我认为这可能是由于分组,我将每个组分成单独的HTML面板元素,但我的输入是使用名称中的索引号进行渲染。任何帮助或建议,以更好的方式来实现这一点将不胜感激。干杯!

更新

这是我更新的帖子控制器和视图:

@model Portal.Models.ViewModel.CreateApplicationViewModel
@{
    ViewBag.Title = "Step 4/5";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@using System.Linq

<h4>Case Data Details</h4>

@using (Html.BeginForm("Step4", "CreateApplication", FormMethod.Post, new { @class = "col-sm-12" }))
{

    int index = 0;
    foreach (var group in Model.Step4.DataDetails.GroupBy(item => item.GroupDesc))
    {
        <div class="panel panel-primary">
            <div class="panel-heading">@Html.Encode(group.Key)</div>
            <div class="panel-body">

                <input type="hidden" name="Step4.DataDetails.Index" value="@index" />

                @for (var i = 0; i < group.Count(); i++)
                {
                    <div class="form-group">
                        <div class="row">
                            <div class="col-xs-6">
                                <label class="form-label">@Model.Step4.DataDetails[i].DetailDesc</label>
                            </div>
                            <div class="col-xs-6">
                                @if (Model.Step4.DataDetails[i].TextFlag == "Y")
                                {
                                    @Html.TextBoxFor(val => val.Step4.DataDetails[i].TextValue, new { @class = "form-control" })
                                }
                                else if (Model.Step4.DataDetails[i].CheckboxFlag == "Y")
                                {
                                    @Html.CheckBoxFor(val => val.Step4.DataDetails[i].CheckboxValue, new { @class = "checkbox" })
                                }
                            </div>
                        </div>
                    </div>
                }
            </div>
        </div>
        index++;
    }
    <div class="col-sm-12">
        <div class="row">
            @Html.ActionLink("Cancel", "Welcome", "Home", null, new { @class = "btn btn-default" })
            <button class="btn btn-default" onclick="history.go(-1);">Previous</button>
            <button type="submit" class="btn btn-default">Next</button>
        </div>
    </div>
}


[HttpPost]
        public ActionResult Step4(CreateApplicationViewModel step4)
        {
            if (ModelState.IsValid)
            {
                CreateApplicationViewModel model = (CreateApplicationViewModel)Session["case"];
               // model.Step4 = step4;
                Session["case"] = model;
                return View();
            }
            return View();
        }

更新2

如果我将FormCollection传递给HttpPost控制器,我可以获取表单输入。关于为什么我可以将这些值作为FormCollection但不作为模型获取的任何想法?

2 个答案:

答案 0 :(得分:0)

您正在发布复杂对象列表。但MVC DefaultModelBinder无法绑定到您的DataDetails对象,因为在发布包含复杂对象列表的表单时,索引必须按顺序排列。在你的情况下,由于嵌套for循环,这个序列被破坏了。所以你可以做的是采用一个单独的变量并使用默认的0值进行初始化 - 我试图修改你的代码。

@using (Html.BeginForm("Step4", "CreateApplication", FormMethod.Post, new { @class = "col-sm-12" }))
{

    int index = 0;
    foreach (var group in Model.Step4.DataDetails.GroupBy(item => item.GroupDesc))
    {
        <div class="panel panel-primary">
            <div class="panel-heading">@Html.Encode(group.Key)</div>
            <div class="panel-body">

                <input type="hidden" name="Step4.DataDetails.Index" value="@index" />

                @for (var i = 0; i < group.Count(); i++)
                { 
                    <div class="form-group">
                        <div class="row">
                            <div class="col-xs-6">
                                <label class="form-label">@Model.Step4.DataDetails[i].DetailDesc</label>
                            </div>
                            <div class="col-xs-6">
                                @if (Model.Step4.DataDetails[i].TextFlag == "Y")
                                {
                                    @Html.TextBoxFor(val => val.Step4.DataDetails[i].TextValue, new { @class = "form-control" })
                                }
                                else if (Model.Step4.DataDetails[i].CheckboxFlag == "Y")
                                {
                                    @Html.CheckBoxFor(val => val.Step4.DataDetails[i].CheckboxValue, new { @class = "checkbox" })
                                }
                            </div>
                        </div>
                    </div>
                }
            </div>
        </div>
        index++;
    }
    <div class="col-sm-12">
        <div class="row">
            @Html.ActionLink("Cancel", "Welcome", "Home", null, new { @class = "btn btn-default" })
            <button class="btn btn-default" onclick="history.go(-1);">Previous</button>
            <button type="submit" class="btn btn-default">Next</button>
        </div>
    </div>
}

查看我在视图中添加的隐藏字段。即使使用破碎的序列,也可以发布数据。希望这能帮助你。

答案 1 :(得分:0)

我能够通过使用索引整数并从上面的答案中递增它并在我的视图中以不同的方式实现该想法来将模型返回到控制器:

    ...
    bootstrapper.ApplyComplete += OnApplyComplete;
    ...

    private void OnApplyComplete(object sender, ApplyCompleteEventArgs e)
    {
        // Deal with error here:
        if (e.Status != 0)
        {
            string error = new Win32Exception(e.Status).Message;
            ErrorMessage = $"Error installing: {error}. Code: 0x{e.Status:x8}";
        }
    }

视图中的上述代码为我提供了每个元素的正确索引,并允许我发布