2014-10-02 2 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; } 
} 

В HttpGet от 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


Теперь я просто не могу заставить его опубликовать список вопросов в модели. Можно ли даже опубликовать список разных типов объектов?


Edit: Ниже приводится список данных в пост, что я обнаружил с помощью Fiddler:

enter image description here

+0

Я не вижу никаких причин, почему она не должна работать. Пока имена входов правильно установлены, вы должны получить данные. однако ваша проблема может возникнуть из-за привязки модели по умолчанию. Ваш сложный тип может быть слишком сложным для _skills_. Вы должны сначала проверить, правильно ли установлены имена, а затем проверить данные, которые отправляются на ваш сервер, и написать собственное связующее устройство. –

+0

@AndreiV - Я проверил имена и значения, которые размещены на странице с помощью скрипача и добавили его в вопрос выше. Значения, похоже, существуют. Считаете ли вы, что мне нужно написать модельное связующее? (никогда не делал этого раньше) – Carel

+1

Не видя точного массива POST, я просто догадываюсь, но: если вы пытаетесь привязать модель к любой коллекции, индексы не могут пропустить число, или связующее устройство модели пропускает все после. Итак, если ваши POSTED-значения были чем-то вроде Вопросов [1] .SelectedAnswer и т. Д., У вас будут проблемы с привязкой модели по умолчанию. –

ответ

23

После долгих исследований я нашел два решения:

One заключается в том, чтобы написать HTML, который имеет жестко закодированные идентификаторы и имена Два - это преобразование вашего ICollection/IEnumerable в массив или список (то есть IList-то с индексом) и иметь A rray в вашей BindingModel в вашем POST-действии контроллера.

Благодаря сообщению Phil Haack's (@ haacked) 2008 http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/ Это все еще актуально для того, как по умолчанию ModelBinder работает сегодня для MVC. (NB: ссылки в статье Фила к образцу porject и расширение методов сломаны)

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> 

Post массив выглядит как:

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) 
    } 
    ... 
} 

Коллекция должна быть либо список или массив для этой работы.

2

Удостоверяя, что вы делаете ваш просмотр в порядке, чтобы Model.Questions[i] оказывает на заказ.

Например, Model.Questions[0], Model.Questions[1], Model.Questions[2]. Я заметил, что если порядок неверен, то связывание с mvc-моделью свяжет только первый элемент.

+0

Это было опубликовано как ответ, но он не пытается ответить на вопрос. Это должно быть редактирование, комментарий, другой вопрос или вообще исключить. –

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" }) 
Смежные вопросы