Form Validation in .NET Core MVC

Viewed 3274

I am trying to add some form validation to a few of my fields. I have a dropdown and an input box. Both of the inputs need validation. My goal is to display an error message to the user if either one are not valid. The form allows the user to create a copy of an existing book, with a new author. The dropdown has a list of books from the database, and the user types in the name of the new Author in the text box. The submit button creates a copy of the existing book with a new author.

Using jQuery, jquery.validate.js, and jquery.validate.unobtrusive.js I was able to accomplish displaying the error message to the user, but the check happens as soon as the page loads, so the error messages appear as soon as the page loads. (Maybe I'll need a hidden field, or I will have to modify my HttpGet handler).

Right now if the user tries to submit the form without selecting a book and/or typing in the author's name, the route remains the same, but the form goes away and there is a json object that displays on the screen with a 400 status and it says "The bookName field is required" and "The author field is required". I just want red text next to the input fields that says they cannot be left empty, how can I accomplish this? Thanks!

My Model:

public int ID { get; set; }
[Required]
public string bookName { get; set; }
[Required]
public string Author { get; set; }

My cshtml:

<form method="post" asp-controller="Index" asp-action="Index" role="post">
    <div class="form-group">
        <label asp-for="bookName"></label>
        <select name="bookName" asp-items="@(new SelectList(ViewBag.message, "ID", "bookName"))">
</select>
        <span asp-validation-for="bookName">Please select a book.</span>
    </div>
    <div class="form-group">
        <label asp-for="Author"></label>
        <input asp-for="Author" class="form-control" />
        <span asp-validation-for="Author">Please type in the author's name.</span>
    </div>
    <div class="form-group">
        <input type="submit" value="Submit" class="btn btn-primary" />
    </div>
</form>

My Controller:

[HttpGet("[action]")]
[Route("/Index")]
[Produces("application/json")]
public IActionResult Index()
{
  return View();
}

[HttpPost("[action]")]
[Route("/Index")]
[Produces("application/json")]
public async Task<IActionResult> Index([FromForm] bookModel model)
{
  if(ModelState.IsValid)
   {
      //do stuff
      return View(model)
   }
   else
   {
     //display an error message & remain on same page
   }
}
3 Answers

The attribute [Produces("application/json")] is not needed if your controller is expected to return html from the view. So this attribute should be removed.

Also your validation span should have the attribute asp-validation-for You have as-validation-for which will not link up the validation script correctly.

Correct:

<span asp-validation-for="bookName">Please select a book.</span>

Fixing this should make the error messages show only after the input control has lost focus.

Do you meam you want to show the validate message when click the button?If so,you can change the error message in Model,and delete the message in your<span asp-validation-for=xxx></span> Here is a demo worked:

Model:

public class bookModel
    {
        public int ID { get; set; }
        [Required(ErrorMessage = "Please select a book.")]
        public string bookName { get; set; }
        [Required(ErrorMessage = "Please type in the author's name.")]
        public string Author { get; set; }
    }

Controller:

public class BookController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.message = addMessage();

            return View();
        }
       [HttpPost]
        public ActionResult Index(bookModel model) {
            ViewBag.message = addMessage();
            return View();
        }
        public static List<SelectListItem> addMessage() {
            List<SelectListItem> list = new List<SelectListItem>();
            SelectListItem s1 = new SelectListItem { Text = "book1", Value = "b1" };
            SelectListItem s2 = new SelectListItem { Text = "book2", Value = "b2" };
            SelectListItem s3 = new SelectListItem { Text = "book3", Value = "b3" };
            list.Add(s1);
            list.Add(s2);
            list.Add(s3);
            return list;
        }

       
    }

View:

@{
    ViewData["Title"] = "Index";
}
@model bookModel
<h1>Index</h1>
<form method="post">
    <div class="form-group">
        <label asp-for="bookName"></label>
        <select asp-for="bookName" asp-items="@ViewBag.message"></select>
        <span asp-validation-for="bookName" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Author"></label>
        <input asp-for="Author" class="form-control" />
        <span asp-validation-for="Author" class="text-danger"></span>
    </div>
    <div class="form-group">
        <input type="submit" value="Submit" class="btn btn-primary" />
    </div>
</form>

Result: enter image description here

I suggest using an external library which name is FormHelper. It's super easy to create forms and doing validations with it on ASP.NET Core. You don't need to write javascript code for ajax forms. Just add the asp-formhelper="true" attribute to your form tag.

https://nuget.org/packages/FormHelper

You can see its documentation from the github page: https://github.com/sinanbozkus/formhelper

Related