2014-10-02 222 views
11

我有一个包含一份问题清单一FeedbackViewModel的列表:MVC后复杂的对象

public class FeedbackViewModel 
{ 
    public List<QuestionViewModel> Questions { get; set; } 
} 

这QuestionViewModel是可以通过5种不同类型的问题被继承的对象

public class QuestionViewModel 
{ 
    public string QuestionText { get; set; } 
    public string QuestionType { get; set; } 
} 

一个继承问题类型的例子:

public class SingleQuestionViewModel : QuestionViewModel 
{ 
    public string AnswerText { get; set; } 
} 

在在控制器Index行动我从数据库中的问题,并在FeedbackViewModel问题列表中添加正确的问题类型,然后,我使这个模型视图:

@using (Html.BeginForm()) 
{ 
    //foreach (var item in Model.Questions) 
    for (int i = 0; i < Model.Questions.Count; i++) 
    { 
     <div class="form-group"> 
      @Html.DisplayFor(modelItem => Model.Questions[i].QuestionText, new { @class = "control-label col-md-4" }) 
      <div class="col-md-6"> 
       @if (Model.Questions[i].QuestionType == "Single") 
       { 
        @Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText) 
       } 
       else if (Model.Questions[i].QuestionType == "Multiple") 
       { 
        @Html.TextAreaFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText) 
       } 
       else if (Model.Questions[i].QuestionType == "SingleSelection") 
       { 
        @Html.RadioButtonForSelectList(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectedAnswer, 
                   (Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectionAnswers) 
       } 
       else if (Model.Questions[i].QuestionType == "MultipleSelection") 
       { 
        @Html.CustomCheckBoxList((Model.Questions[i] as OpenDataPortal.ViewModels.MultipleSelectionQuestionViewModel).AvailableAnswers) 
       } 
       else if (Model.Questions[i].QuestionType == "UrlReferrer") 
       { 
        @Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText) 
       } 
      </div> 
     </div> 
     <br /> 
    } 

    <br /> 
    <button type="submit">Submit</button> 
} 

enter image description here


现在,我简直无法让它在模型中发布问题列表。是否有可能发布不同对象类型的列表?


编辑:下面是我使用发现小提琴手在帖子内的数据列表:

enter image description here

+0

我看不出有任何理由不应该工作。只要输入的名称设置正确,您应该收到数据。但是,您的问题可能来自默认模型联编程序。你的复杂类型可能太复杂了,因为它是_skills_。您应该首先检查名称是否设置正确,然后检查发布到服务器的数据并编写您自己的模型绑定器。 – 2014-10-02 06:50:04

+0

@AndreiV - 我检查了使用fiddler从页面发布的名称和值,并将其添加到上面的问题中。价值似乎在那里。你觉得我需要写一个模型绑定器吗? (从来没有这样做过) – Carel 2014-10-02 07:21:55

+1

没有看到确切的POST数组,我只是猜测,但是:如果你想模型绑定任何类型的集合,索引不能跳过数字,或者模型联编程序跳过所有东西后。因此,如果您的POST值与Questions [1] .SelectedAnswer等类似,您将遇到默认模型联编程序的问题。 – 2014-10-02 07:28:13

回答

23

经过大量的研究,我发现两个解决方案:

一是编写硬编码ID和名称的HTML 二是将您的ICollection/IEnumerable转换为数组或列表(即IList与'索引'的东西),并有一个A在您的Controller POST Action的BindingModel中使用rray对象。

感谢Phil Haack的(@ haacked)2008博客文章http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/ 这与默认ModelBinder如何在MVC中工作的方式仍然有关。 (注:在菲尔的文章的链接来样,项目和扩展方法被破坏)

HTML片段,启发了我:

<form method="post" action="/Home/Create"> 
    <input type="hidden" name="products.Index" value="cold" /> 
    <input type="text" name="products[cold].Name" value="Beer" /> 
    <input type="text" name="products[cold].Price" value="7.32" /> 

    <input type="hidden" name="products.Index" value="123" /> 
    <input type="text" name="products[123].Name" value="Chips" /> 
    <input type="text" name="products[123].Price" value="2.23" /> 

    <input type="submit" /> 
</form> 

后阵列看起来有点像:

products.Index=cold&products[cold].Name=Beer&products[cold].Price=7.32&products.Index=123&products[123].Name=Chips&products[123].Price=2.23 

型号:

public class CreditorViewModel 
{ 
    public CreditorViewModel() 
    { 
     this.Claims = new HashSet<CreditorClaimViewModel>(); 
    } 
    [Key] 
    public int CreditorId { get; set; } 
    public string Comments { get; set; } 
    public ICollection<CreditorClaimViewModel> Claims { get; set; } 
    public CreditorClaimViewModel[] ClaimsArray { 
     get { return Claims.ToArray(); } 
    } 
} 

public class CreditorClaimViewModel 
{ 
    [Key] 
    public int CreditorClaimId { get; set; } 
    public string CreditorClaimType { get; set; } 
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:N2}")] 
    public Decimal ClaimedTotalAmount { get; set; } 
} 

控制器GET:

public async Task<ActionResult> Edit(int id) 
    { 
     var testmodel = new CreditorViewModel 
     { 
      CreditorId = 1, 
      Comments = "test", 
      Claims = new HashSet<CreditorClaimViewModel>{ 
       new CreditorClaimViewModel{ CreditorClaimId=1, CreditorClaimType="1", ClaimedTotalAmount=0.00M}, 
       new CreditorClaimViewModel{ CreditorClaimId=2, CreditorClaimType="2", ClaimedTotalAmount=0.00M}, 
      } 
     }; 
     return View(model); 
    } 

编辑。CSHTML:

@Html.DisplayNameFor(m => m.Comments) 
@Html.EditorFor(m => m.Comments) 

<table class="table"> 
    <tr> 
     <th> 
      @Html.DisplayNameFor(m => Model.Claims.FirstOrDefault().CreditorClaimType) 
     </th> 
     <th> 
      @Html.DisplayNameFor(m => Model.Claims.FirstOrDefault().ClaimedTotalAmount) 
     </th> 
    </tr>   
<!--Option One--> 
@foreach (var item in Model.Claims) 
{ 
    var fieldPrefix = string.Format("{0}[{1}].", "Claims", item.CreditorClaimId); 
    <tr> 
     <td> 
      @Html.DisplayFor(m => item.CreditorClaimType) 
     </td> 
     <td> 
     @Html.TextBox(fieldPrefix + "ClaimedTotalAmount", item.ClaimedTotalAmount.ToString("F"), 
     new 
     { 
      @class = "text-box single-line", 
      data_val = "true", 
      data_val_number = "The field ClaimedTotalAmount must be a number.", 
      data_val_required = "The ClaimedTotalAmount field is required." 
     }) 
     @Html.Hidden(name: "Claims.index", value: item.CreditorClaimId, htmlAttributes: null) 
     @Html.Hidden(name: fieldPrefix + "CreditorClaimId", value: item.CreditorClaimId, htmlAttributes: null) 
     </td> 
    </tr> 
    } 
</table>  
<!--Option Two--> 
@for (var itemCnt = 0; itemCnt < Model.ClaimsArray.Count(); itemCnt++) 
{ 
    <tr> 
     <td></td> 
     <td> 
      @Html.TextBoxFor(m => Model.ClaimsArray[itemCnt].ClaimedTotalAmount) 
      @Html.HiddenFor(m => Model.ClaimsArray[itemCnt].CreditorClaimId) 
    </td></tr> 
} 

形式是在控制器处理:

邮政型号:

public class CreditorPostViewModel 
{ 
    public int CreditorId { get; set; } 
    public string Comments { get; set; } 
    public ICollection<CreditorClaimPostViewModel> Claims { get; set; } 
    public CreditorClaimPostViewModel[] ClaimsArray { get; set; } 
} 

public class CreditorClaimPostViewModel 
{ 
    public int CreditorClaimId { get; set; } 
    public Decimal ClaimedTotalAmount { get; set; } 
} 

控制器:

[HttpPost] 
    public ActionResult Edit(int id, CreditorPostViewModel creditorVm) 
    { 
     //... 
4

感谢这个职位指着我在正确的方向。我一直在努力获得绑定非连续的IDictionary<string, bool>对象的语法。不知道这是100%正确的,但这种剃刀代码为我工作:

<input type="hidden" name="MyDictionary.Index" value="ABC" /> 
<input type="hidden" name="MyDictionary[ABC].Key" value="ABC" /> 
@Html.CheckBox(name: "MyDictionary[ABC].Value", isChecked: Model.MyDictionary["ABC"], htmlAttributes: null) 

如果你需要一个复选框,一定要使用Html.CheckBox而不是标准的HTML复选框。如果未提供值,则模型将炸毁,并且Html.CheckBox会生成隐藏字段,以确保未选中复选框时存在值。

0

使用剃须刀,你可以实现使用词典无需改变你的对象如下循环:

@foreach (var x in Model.Questions.Select((value,i)=>new { i, value })) 
{ 
    if (Model.Questions[x.i].QuestionType == "Single") 
    { 
      @Html.EditorFor(modelItem => (modelItem.Questions[x.i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText) 
    } 
    ... 
} 

收集需要是A这个工作列表或数组。

2

确保您按顺序呈现您的视图,以便Model.Questions[i]按顺序呈现。例如,Model.Questions[0], Model.Questions[1], Model.Questions[2]。 我注意到,如果顺序不正确,mvc模型绑定器将只绑定第一个元素。

+0

这是作为答案发布的,但它不会试图回答这个问题。它应该可能是编辑,评论,另一个问题,或者完全删除。 – 2016-12-27 02:47:20

0

我用这个代码,也许它可以帮助

<input type="hidden" name="OffersCampaignDale[@(item.ID)].ID" value="@(item.ID)" /> 

@Html.Raw(Html.EditorFor(modelItem => item.NameDale, new { htmlAttributes = new { @class = "form-control" } }) 
.ToString().Replace("item.NameDale", "OffersCampaignDale[" + item.ID+ "].NameDale").Replace("item_NameDale", "NameDale-" + item.ID)) 
@Html.ValidationMessageFor(modelItem => item.NameDale, "", new { @class = "text-danger" })