asp.net-mvcrazorasp.net-mvc-5mvc-editor-templates

complex type model wont pass list propery


hi guys i am having trouble with my mvc app. its a simple quiz app and i am stuck at creating create view for question model.

I have Question and Option model with appropriate view models(in my case they are QustionDTO and OptionDTO) and i want to make cshtml create view for Question with list of Options.like this but when i submit form, my list of options is null. this is my Question and Option model

 public class Question
 {
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public int Id { get; set; }
     [Required]
     public string QuestionText { get; set; }
     public virtual ICollection<Option> Options { get; set; }
 }

public class Option
{
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public int Id { get; set; }
     [Required]
     [Display(Name ="Answer text")]
     public string OptionText { get; set; }
     [Required]
     public bool IsCorrect { get; set; }
}

this is my DTO models

public class QuestionDTO
{
    public int Id { get; set; }
    public string QuestionText { get; set; }
    public List<OptionDTO> Options { get; set; }
}
public class OptionDTO
{
    public int Id { get; set; }
    public string OptionText { get; set; }
    public bool IsCorrect { get; set; }
}

and this is my view with editor template located in "~/views/shared/editortemplate/OptionDTO.cshtml"

@model Quiz.BusinessEntites.QuestionDTO

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>QuestionDTO</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.QuestionText, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.QuestionText, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.QuestionText, "", new { @class = "text-danger" })
            </div>
        </div>
        <table class="table" style="width:50%">
            @for (int i = 0; i < 3; i++)
            {
                @Html.EditorFor(model=>model.Options[i])
            }
        </table>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

this is OptionDTO editor template

@using Quiz.BusinessEntites
@model Quiz.BusinessEntites.OptionDTO
<tr>
    <th class="col-md-2">
        @Html.DisplayNameFor(m => m.OptionText)
    </th>
    <th class="col-md-2">
        @Html.DisplayNameFor(m => m.IsCorrect)
    </th>
</tr>
<tr>
    <td class="col-md-2">
        @Html.EditorFor(m => m.OptionText)
    </td>
    <td class="col-md-2">
        @Html.EditorFor(m => m.IsCorrect)

    </td>
</tr>

from the image above u can see that options list is null. if u have any suggestion it will be appreciated.


Solution

  • In your http post action method, the Bind attribute with Include list is telling the Model binder to bind only "Id","QuestionText" and "IsCorrect" properties of QuestionDto object from the posted form data. So the model binder will not bind the Options property value.

    Remove the Bind attribute from your Http post action method. There is no need to use the Bind attribute if your view model is specific to your view, means you have only properties needed for your view (In your case it looks like so)

    public ActionResult Create(QuestionDTO model)
    {
      // to do :return something
    }
    

    If you want to use a non view specific view model, but still want to use Bind attribute to specify only subset of properties, Include just those properties. In your case, your code will be like

    public ActionResult Create([Bind(Include="Id,QuestionText",Options"] QuestionDTO model)
    {
      // to do :return something
    }
    

    Also you should editer template view should be in a directory called EditorTemplates , not EditorTemplate