1

I have a viewmodel i called ResultsViewModel which I used to strongly type my create view in asp.net core mvc 1.1. It has 5 dropdowns and a few input fields as shown in my code below:

This is the ViewModel

public class ResultsViewModel
{
    public ResultsViewModel()
    {
    }
    [Display(Name = "Students")]
    public SelectList Students { get; set; }
    [Display(Name = "Subjects")]
    public SelectList Subjects { get; set; }
    [Display(Name = "Term")]
    public Term Terms { get; set; }
    [Display(Name = "Continuous Assessment")]
    public SelectList ContinuousAssessments { get; set; }
    [Display(Name = "Continuous Assessment Score")]
    [DisplayFormat(DataFormatString ="{0:F2}")]
    public double CAScore { get; set; }
    [Display(Name = "School Year")]
    public SelectList Session { get; set; }
    [Display(Name = "Class")]
    public SelectList Class { get; set; }
    [Display(Name = "Average Score")]
    [DisplayFormat(DataFormatString = "{0:F2}")]
    public double AverageScore { get; set; }
    [Display(Name = "Term Total Score")]
    [DisplayFormat(DataFormatString = "{0:F2}")]
    public double TermTotal { get; set; }
}

This is the create View. Its standard with nothing special

@model SwiftSchool.ViewModels.ResultsViewModel
@{
    ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form asp-action="Create" method="post">
    <div class="form-horizontal">
        <h4>Result</h4>
        <hr />
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="Students" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                @Html.DropDownList("Student", Model.Students, "Select Student", new { @class = "form-control", @id = "studentselect" })
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Subjects" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                @Html.DropDownList("Subject", Model.Subjects, "Select Subject", new { @class = "form-control", @id = "subjectselect" })
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Session" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                @Html.DropDownList("Session", Model.Session, "Select School Session",
               new { @class = "form-control", @id = "sessionselect" })
            </div>
        </div>
        <div class="form-group">
            <label asp-for="Class" class="col-md-2 control-label"></label>
            <div class="col-md-4">
                @Html.DropDownList("Class", Model.Class, "Select Student Class",
               new { @class = "form-control", @id = "classselect" })
            </div>
        </div>
        <div class="form-group">
            <label asp-for="ContinuousAssessments" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                @Html.DropDownList("C.A", Model.ContinuousAssessments, "Select C.A",
               new { @class = "form-control", @id = "caselect" })
            </div>
        </div>
        <div class="form-group">
            <label asp-for="CAScore" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="CAScore" class="form-control" readonly id="cascore" />
                <span asp-validation-for="CAScore" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="AverageScore" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="AverageScore" class="form-control" />
                <span asp-validation-for="AverageScore" class="text-danger" />
             </div>
        </div>
        <div class="form-group">
            <label asp-for="TermTotal" class="col-md-2 control-label"</label>
            <div class="col-md-10">
                <input asp-for="TermTotal" class="form-control" />
                <span asp-validation-for="TermTotal" class="text-danger" />
            </div>
        </div>
        <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>
</form>
<div>
    <a asp-action="Index">Back to List</a>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

I have the form submitting and returning my ResultsViewModel to my controller. The two methods that create my dropdown for the view and the method that processes the POST are shown below:

// GET: Result/Create
public IActionResult Create()
{
    var rim = new ResultsViewModel();
    rim.Students = new SelectList(_context.Students.ToList(), "Id", "FullName");
    rim.Subjects = new SelectList(_context.Subjects.ToList(), "Id", "Name");
    rim.ContinuousAssessments = new SelectList(_context.ContinuousAssessment.ToList(), "Id", "Name");
    rim.Class = new SelectList(_context.Classes.ToList(), "Id", "ClassName");
    rim.Session = new SelectList(_context.Sessions.ToList(), "Id", "SessionName");
    return View(rim);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ResultsViewModel model)
{
    if (ModelState.IsValid)
    {
        _context.Add(model);
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }

        return View(model);
    }
}

So when I do a POST of the form after selecting and filling all the fields on the form, I set a break point right where I check if the ModelState is valid and I inspect the viewmodel injected into the Create method and everything is null, especially the dropdowns. So everything just goes south from there. Can someone please let me know what is going on? Cos I find this annoying. I hope it ain't another bug in the framework cos somewhere on line I saw that this was a bug in earlier versions of the framework. Anyways any help will appreciated.

Joseph Izang
  • 766
  • 1
  • 13
  • 43

1 Answers1

1

Your creating dropdownlists that have no relationship to any of the properties in your model.

The first one binds to a property named Student which does not exist. The 2nd binds to a property named Subject which again does not exist. The 3rd binds to property Session which is a collection of complex objects (IEnumerable<SelectListItem>) but a <select> element only posts back a single value - the value of the selected option. Ditto for the 4th.

Your model needs properties to bind to

[Display(Name = "Students")]
[Required("Please select a student")]
public int SelectedStudent { get; set; }

public IEnumerable<SelectListItem> Students { get; set; }

and then in the view its

@Html.DropwDownListFor(m => m.SelectedStudent, Model.Students, "Select Student", new { @class = "form-control" })

Note its unclear why your using new { @id = "studentselect" } to override the default id attribute generated by the method (which is id="SelectedStudent")

Alternatively, using the Tag Helper

<select asp-for="SelectedStudent" asp-items="Model.Students" class="form-control">
    <option>Select Student</option>
</select>

The 3 inputs generated using will bind fine assuming your submitting a valid value for those property types.

Side note: In the POST method, you need to repopulate the SelectLists before you return the model to the view, or they will be null and an exception will be thrown - refer The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable'

Community
  • 1
  • 1
  • Thanks @Stephen, I wish the ASPNET Core docs would make some of these things clear. I set the Id's because I didn't know the id's are set by default. But good going man. Many thanks – Joseph Izang Dec 16 '16 at 14:02