Search code examples
c#jqueryvalidationasp.net-coremodel-binding

Validation works but message is not displayed


I am trying to validate specific fields from my model before the form is submitted. It works but message doesn't show, this is because name attributes doesn't match.

This is my model:

    public class TaskList
{
    [Key]
    public int Id { get; set; }

    [MaxLength(200, ErrorMessage = "Subject field can't have more than 250 characters.")]
    [Required(ErrorMessage = "Subject field cannot be empty.")]
    [Column(TypeName = "nvarchar")]
    public string Subject { get; set; }
    [MaxLength(1000, ErrorMessage = "Details field can't have more than 1000 characters.")]
    [Column(TypeName = "nvarchar")]
    public string Details { get; set; }
    [Required(ErrorMessage = "Please select a module.")]
    public TaskLabels Label { get; set; }
    [Required]
    [Column(TypeName = "date")]
    public DateTime Created { get; set; }
    [Column(TypeName = "date")]
    public DateTime? DueDate { get; set; }
    [Column(TypeName = "date")]
    public DateTime? Completed { get; set; }
}

This is controller action:

 [HttpPost]
    public IActionResult TaskList(TaskList newTask)
    {
            _ticketRepo.AddTask(newTask);
            return RedirectToAction("TaskList");
    }

This is my viewmodel:

    public class TaskListViewModel
{
    public TaskList ToDo { get; set; }
    public IEnumerable<TaskList> TasksList { get; set; }
}

And this is my form:

<div class="modal fade" id="modal-add">
<div class="modal-dialog modal-lg">
    <form class="form-horizontal" asp-controller="home" asp-action="TaskList" method="post">
        <div class="modal-content">
            <div class="modal-header bg-success">
                <h4 class="modal-title">Add New To Do Item</h4>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <div class="card-body">
                    <div class="form-group row">
                        <label asp-for="ToDo.Subject" class="col-sm-2 col-form-label">Subject</label>
                        <div class="col-sm-10">
                            <input type="text" asp-for="ToDo.Subject" name="Subject" class="form-control" placeholder="Subject">
                            <span asp-validation-for="@Model.ToDo.Subject" class="text-danger"></span>
                        </div>
                    </div>
                    <div class="form-group row">
                        <label asp-for="ToDo.Details" class="col-sm-2 col-form-label">Details</label>
                        <div class="col-sm-10">
                            <textarea asp-for="ToDo.Details" name="Details" class="form-control" placeholder="Provide some details about the task."></textarea>
                            <span asp-validation-for="ToDo.Details" class="text-danger"></span>
                        </div>
                    </div>
                    <div class="form-group row">
                        <label asp-for="ToDo.Label" class="col-sm-2 col-form-label">Module</label>
                        <div class="col-sm-10">
                            <select name="ToDo.Label" asp-for="Label" asp-items="Html.GetEnumSelectList<TaskLabels>()" class="form-control select2 select2-danger" data-dropdown-css-class="select2-danger" style="width: 100%;">
                                <option value="" selected="selected">Please select one</option>
                            </select>
                            <span asp-validation-for="ToDo.Label" class="text-danger"></span>
                        </div>
                    </div>
                    <div class="form-group row">
                        <label asp-for="ToDo.DueDate" class="col-sm-2 col-form-label">Date:</label>
                        <div class="col-sm-10 input-group date" id="duedate" data-target-input="nearest">
                            <input type="text" name="DueDate" asp-for="ToDo.DueDate" class="form-control datetimepicker-input" data-target="#duedate" />
                            <div class="input-group-append" data-target="#duedate" data-toggle="datetimepicker">
                                <div class="input-group-text"><i class="fa fa-calendar"></i></div>
                            </div>
                        </div>
                    </div>
                    <div asp-validation-summary="All" class="text-danger"></div>
                </div>
                <!-- /.card-body -->
            </div>
            <div class="modal-footer justify-content-between">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                <button type="submit" class="btn btn-primary">Save changes</button>
            </div>
        </div>
        <!-- /.modal-content -->
    </form>
</div>
<!-- /.modal-dialog -->
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script><script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

In my form, if I remove name attribute from input fields, validation works, validation messages are shown but model binding doesn't work and I get null exception error for my properties. What am I doing wrong?


Solution

  • Of course I had to instantiate viewmodel in my controller action! When I did that and removed all name attributes from input fields in my view, everything worked just fine!

        [HttpPost]
        public IActionResult TaskList(TaskListViewModel model)
        {
    
            if (ModelState.IsValid)
            {
                TaskList taskList = new TaskList
                {
                    Created = DateTime.Now.Date,
                    Subject = model.ToDo.Subject,
                    Details = model.ToDo.Details,
                    DueDate = model.ToDo.DueDate,
                    Label = model.ToDo.Label
                };
    
                _ticketRepo.AddTask(taskList);
                return RedirectToAction("TaskList");
            }
            else
            {
                return View();
            }
        }