Validation works but message is not displayed - c#

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">×</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?

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();
}
}

Related

ASP.NET MVC Model validation not working on one of the controller constructor

I have model validation for user register,login and update. However the model validation only works on login and register. The update function does not respond either
The model
namespace DDemo.Models
{
public class SysUser
{
[Required(ErrorMessage = "Please enter User ID")]
[Remote(action: "VerifyUserID", controller: "Account")]
public string InvestorId { get; set; }
[Required(ErrorMessage = "Please enter Password")]
[StringLength(20, MinimumLength = 5, ErrorMessage = "Password must be 5 characters or more")]
public string InvestorPw { get; set; }
[Compare("InvestorPw", ErrorMessage = "Passwords do not match")]
public string InvestorPw2 { get; set; }
[Required(ErrorMessage = "Please enter Full Name")]
public string FullName { get; set; }
[Required(ErrorMessage = "Please enter Email")]
[EmailAddress(ErrorMessage = "Invalid Email")]
public string Email { get; set; } //# sign
[Required(ErrorMessage = "Please enter Contact Number")]
[RegularExpression(#"\d{8}", ErrorMessage = "Please enter 8 digit number")]
public string Contact { get; set; }
public DateTime LastLogin { get; set; }
}
}
The controller
[Authorize]
[HttpPost]
public IActionResult UserUpdate(SysUser user)
{
if (!ModelState.IsValid)
{
ViewData["Message"] = "Invalid Input";
ViewData["MsgType"] = "warning";
return View("UserUpdate", user);
}
else
{
string update =
#"
UPDATE Investor
SET InvestorPw = ('SHA1', '{1}'), FullName = '(2)', Email = '{3}', Contact = '{4}'
WHERE InvestorId = '{0}'";
int result = DBUtl.ExecSQL(update, user.InvestorId, user.InvestorPw, user.FullName, user.Email, user.Contact);
if (result == 1)
{
TempData["Message"] = "Account Updated";
TempData["MsgType"] = "success";
}
else
{
TempData["Message"] = DBUtl.DB_Message;
TempData["MsgType"] = "danger";
}
return RedirectToAction("Users");
}
}
The view
#model SysUser
<form asp-controller="Account"
asp-action="UserUpdate"
method="post">
<div class="form-group row">
<div class="offset-sm-0 col-sm-4">
<h2>Update Your Account</h2>
</div>
</div>
</form>
<div class="form-group row">
<label class="control-label col-sm-2" asp-for="InvestorId">User ID :</label>
<div class="col-sm-4">
<input type="text" asp-for="InvestorId" readonly="readonly" class="form-control" />
</div>
<div class="col-sm-3">
<span asp-validation-for="InvestorId" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label class="control-label col-sm-2" asp-for="FullName">Full Name :</label>
<div class="col-sm-4">
<input type="text" asp-for="FullName" class="form-control" />
</div>
<div class="col-sm-3">
<span asp-validation-for="FullName" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label class="control-label col-sm-2" asp-for="Email">Email ID :</label>
<div class="col-sm-4">
<input type="text" asp-for="Email" class="form-control" />
</div>
<div class="col-sm-3">
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label class="control-label col-sm-2" asp-for="InvestorPw">Password :</label>
<div class="col-sm-4">
<input type="password" asp-for="InvestorPw" class="form-control" />
</div>
<div class="col-sm-3">
<span asp-validation-for="InvestorPw" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label class="control-label col-sm-2" asp-for="InvestorPw2">Confirm :</label>
<div class="col-sm-4">
<input type="password" asp-for="InvestorPw2" class="form-control" />
</div>
<div class="col-sm-3">
<span asp-validation-for="InvestorPw2" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label class="control-label col-sm-2" asp-for="Contact">Contact :</label>
<div class="col-sm-4">
<input type="text" asp-for="Contact" class="form-control" />
</div>
<div class="col-sm-3">
<span asp-validation-for="Contact" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="offset-sm-2 col-sm-6">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</div>
#if (ViewData["Message"] != null)
{
<div class="form-group row">
<div class="alert alert-#ViewData["MsgType"]">
<text>#ViewData["Message"]</text>
</div>
</div>
}
enter image description here
enter image description here
As in the picture, model validation works with register but not with update
you have to fix a form tag of your view, move the end of form to below of a submit button and add #Html.ValidationSummary()
<form asp-controller="Account"
asp-action="UserUpdate"
method="post">
#Html.ValidationSummary(true)
<div class="form-group row">
<div class="offset-sm-0 col-sm-4">
<h2>Update Your Account</h2>
</div>
</div>
........
<div class="form-group row">
<div class="offset-sm-2 col-sm-6">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</div>
</form>

ASP .NET Pass DateTime Value from Views to Controller

When in view mode, I try to fill in and get the date, it always turns out to be empty, but for example, the string field is filled in. And if I don't use the ViewModel, but only the Booking model, the date is filled in. I would like the date to be filled in with the viewmodel as well...
Controller:
public async Task<IActionResult> CheckOut3(CheckoutModel broom)
{
if (ModelState.IsValid)
{
_context.Add(broom);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(broom);
}
Model:
public class Booking
{
public int Id { get; set; }
public Room Room { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
[DisplayFormat(ApplyFormatInEditMode =true, DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime? CheckInDate{ get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime? CheckOutDate { get; set; }
}
ViewModel:
public class CheckoutModel
{
public Booking booking {get; set;}
public Room room { get; set; }
}
View:
<form asp-action="Checkout3">
<div class="row">
<!--Grid column-->
<div class="col-lg-6 col-md-12 mb-4">
<label asp-for="booking.CheckInDate">CheckIn</label>
<input asp-for="booking.CheckInDate" class="form-control" type="datetime-local" name="CheckInDate" />
<span asp-validation-for="booking.CheckInDate" class="text-danger"></span>
</div>
<!--Grid column-->
<div class="col-lg-6 col-md-6 mb-4">
<label asp-for="booking.CheckOutDate">CheckOut</label>
<input asp-for="booking.CheckOutDate" class="form-control" type="date" name="CheckOutDate" />
<span asp-validation-for="booking.CheckOutDate" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<input type="submit" value="Checkout" class="btn btn-primary" />
</div>
</form>
The problem is, you're posting CheckInDate and CheckOutDate in the html form, while it should be booking.CheckInDate and booking.CheckOutDate. Remove the name in your view, so name will be set with the value provided in asp-for.
<!--Grid column-->
<div class="col-lg-6 col-md-12 mb-4">
<label asp-for="booking.CheckInDate">CheckIn</label>
<input asp-for="booking.CheckInDate" class="form-control" type="datetime-local" />
<span asp-validation-for="booking.CheckInDate" class="text-danger"></span>
</div>
<!--Grid column-->
<div class="col-lg-6 col-md-6 mb-4">
<label asp-for="booking.CheckOutDate">CheckOut</label>
<input asp-for="booking.CheckOutDate" class="form-control" type="date" />
<span asp-validation-for="booking.CheckOutDate" class="text-danger"></span>
</div>

How to add dropdown list in ASP.NET MVC

public class Product
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string SKU { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool Status { get; set; }
public bool IsMenuItem { get; set; }
public int Count { get; set; }
public int? CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
I want to add 'CategoryId' DropDown List instead of <here> tag
#model OnlineShopArmenia.Models.Product
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Product</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SKU" class="control-label"></label>
<input asp-for="SKU" class="form-control" />
<span asp-validation-for="SKU" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Status" /> #Html.DisplayNameFor(model => model.Status)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsMenuItem" /> #Html.DisplayNameFor(model => model.IsMenuItem)
</label>
</div>
<div class="form-group">
<label asp-for="Count" class="control-label"></label>
<input asp-for="Count" class="form-control" />
<span asp-validation-for="Count" class="text-danger"></span>
</div>
<here>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
you can use this code in your controller:
ViewBag.Categories = new SelectList(List of categories for show in view, "Id", "Name");
if you have categories in your data base you can make a query for get list of categories or you can make a list of categories with Id and Name then pass the list into first parameter of SelectList.
in your View:
<div class="form-group">
<label>Categories :</label>
<select name="CategoryId" asp-for="CategoryId" class="formcontrol" asp-items="ViewBag.Categories">
<option value="">Please choose user category:</option>
</select>
<span asp-validation-for="CategoryId" class="text-danger"></span>
</div>
or generate it by tag helper:
#Html.DropDownListFor(model => Category ,ViewBag.Categories as SelectList, new { #class = "form-group"} )

Compare Attribute fails on Post

I have a Register form which uses the Compare Attribute to compare the password and confirm password. When I enter in different passwords it does what it is supposed to and says "Passwords do not match". The issue comes when I hit the submit button and "Post" it. I hit a breakpoint at "if (!ModelState.IsValid)" where ModelState is not valid. When it reloads the page where it used to say "Passwords do not match" it now says "Could not find a property named Password." I tried doing what this post said to try and put in the answer's code. All it returns is that the error is "null".
Razor Page:
#page
#model ThinBlueLie.Pages.RegisterModel
#{
ViewBag.Title = "Register";
}
<div class="container-fluid h-100 row nogap">
<div class="card border-secondary mx-auto center col-lg-3 col-md-4 p-0" style="margin-top:100px;">
<div class="card-header">
Register
<a class="float-right" asp-page="/Login">Login</a>
</div>
<form method="post">
<div class="card-body text-secondary">
<div class="form-group">
<label asp-for="Users.Email" class="control-label">Email</label>
<input type="email" asp-for="Users.Email" class="form-control">
<span asp-validation-for="Users.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"class="control-label"></label>
<input asp-for="Password" type="password" class="form-control" >
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input type="password" asp-for="ConfirmPassword" class="form-control">
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Users.Username" class="control-label"></label>
<input type="text" asp-for="Users.Username" class="form-control">
<span asp-validation-for="Users.Username" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-primary mb-1" style="float:right" />
</div>
</div>
</form>
<div class="card-footer">
<a>Log in with Google</a>
</div>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
PageModel:
namespace ThinBlueLie.Pages
{
public class RegisterModel : PageModel
{
private readonly DataAccessLibrary.thinblue.ThinbluelieContext _context;
public RegisterModel(DataAccessLibrary.thinblue.ThinbluelieContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
public Users Users { get; set; }
[BindProperty]
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[BindProperty]
[DataType(DataType.Password)]
[Display(Name = "Confirm Password")]
[Compare("Password", ErrorMessage = "Passwords do not match")]
public string ConfirmPassword { get; set; }
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
var errors = ModelState.Values.SelectMany(v => v.Errors);
if (!ModelState.IsValid)
{
return Page();
}
_context.Users.Add(Users);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
Model:
namespace DataAccessLibrary.thinblue
{
public partial class Users
{
public int IdUsers { get; set; }
[BindProperty]
public string Username { get; set; }
[BindProperty]
[Required]
[EmailAddress]
public string Email { get; set; }
public string Password { get; set; }
}
}
I reproduce your error,and I put Password and ConfirmPassword in the same model,and it works.Here is a demo:
PassWordModel:
public class PassWordModel
{
[BindProperty]
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[BindProperty]
[DataType(DataType.Password)]
[Display(Name = "Confirm Password")]
[Compare("Password", ErrorMessage = "Passwords do not match")]
public string ConfirmPassword { get; set; }
}
cshtml.cs:
public class RegisterModel : PageModel
{
public IActionResult OnGet()
{
return Page();
}
public Users Users { get; set; }
[BindProperty]
public PassWordModel passWordModel { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
return RedirectToPage("./Index");
}
}
cshtml:
<div class="container-fluid h-100 row nogap">
<div class="card border-secondary mx-auto center col-lg-3 col-md-4 p-0" style="margin-top:100px;">
<div class="card-header">
Register
<a class="float-right" asp-page="/Login">Login</a>
</div>
<form method="post">
<div class="card-body text-secondary">
<div class="form-group">
<label asp-for="Users.Email" class="control-label">Email</label>
<input type="email" asp-for="Users.Email" class="form-control">
<span asp-validation-for="Users.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="passWordModel.Password" class="control-label"></label>
<input asp-for="passWordModel.Password" type="password" class="form-control">
<span asp-validation-for="passWordModel.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="passWordModel.ConfirmPassword" class="control-label"></label>
<input type="password" asp-for="passWordModel.ConfirmPassword" class="form-control">
<span asp-validation-for="passWordModel.ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Users.Username" class="control-label"></label>
<input type="text" asp-for="Users.Username" class="form-control">
<span asp-validation-for="Users.Username" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-primary mb-1" style="float:right" />
</div>
</div>
</form>
<div class="card-footer">
<a>Log in with Google</a>
</div>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
result:

Required attribute not working when it's a select in .net core mvc form validation?

It's a simple form page. The framework is .NET Core 3.1 and this page is basically coded following the Microsoft tutorial.
Model
public class ProdoctModel
{
[Required]
[StringLength(100)]
public string product { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal price { get; set; }
[Required]
public string locale { get; set; }
[Required]
public string category { get; set; }
public bool importGoods { get; set; }
}
Controller
public ActionResult Create()
{
var countries = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("en-US", "United States"), new KeyValuePair<string, string>("ch-CN", "China"), new KeyValuePair<string, string>("es-MX", "Mexico") };
var categories = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("1", "foods"), new KeyValuePair<string, string>("2", "3C"), new KeyValuePair<string, string>("3", "medicine") };
ViewBag.countries = new SelectList(countries, "Key", "Value");
ViewBag.categories = categories;
return View();
}
view
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<h3 class="text-center">
Add Product
</h3>
<form role="form" asp-controller="PrsExps" asp-action="Create" method="post">
<div class="form-group">
<label asp-for="product"></label>
<input asp-for="product" class="form-control" />
<span class="field-validation-valid text-danger" asp-validation-for="product" ></span>
</div>
<div class="form-group">
<label asp-for="price"></label>
<input asp-for="price" class="form-control" />
<span class="field-validation-valid text-danger" asp-validation-for="price"></span>
</div>
<div class="form-group">
<label asp-for="locale"></label>
<select asp-for="locale" asp-items="#ViewBag.countries">
<option>Please Select...</option>
</select>
<span class="field-validation-valid text-danger" asp-validation-for="locale"></span>
</div>
<div class="form-group">
<label asp-for="category"></label>
#foreach (var catItem in ViewBag.categories)
{
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" asp-for="category" id="category#(catItem.Key)" value="#catItem.Value">
<label class="form-check-label" asp-for="category">#catItem.Value</label>
</div>
}
<span class="field-validation-valid text-danger" asp-validation-for="category"></span>
</div>
<div class="checkbox">
<label>
<input asp-for="importGoods" /> Imported
</label>
</div>
<button type="submit" class="btn btn-primary">
Submit
</button>
</form>
</div>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
Here is a screenshot when I enter notion and click submit button directly. As you can see, the required validation on 'locale' is missing.

Categories