[go: up one dir, main page]

0% found this document useful (0 votes)
35 views63 pages

Chapter 11 Slides

Uploaded by

aisha.adediran0
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views63 pages

Chapter 11 Slides

Uploaded by

aisha.adediran0
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 63

Chapter 11

How to
validate data

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 1
Objectives (part 1)
Applied
1. Given the data validation requirements for a web form, use any of
the validation techniques presented in this chapter to implement
that validation.

Knowledge
2. Describe the default data validation provided by model binding.
3. Describe the use of attributes for data validation.
4. List six data validation attributes.
5. Describe the use of tag helpers for data validation.
6. Describe the use of the IsValid property of a controller’s
ModelState property.
7. Describe the use of CSS to format validation messages.

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 2
Objectives (part 2)
7. Describe the use of a controller’s ModelState property for
checking the validation state of a control and setting a custom
error message.
8. Describe the use of model-level and property-level validation
messages.
9. Describe the use of unobtrusive client-side data validation.
10. Distinguish between client-side validation and server-side
validation.
11. Describe the process of customizing server-side data validation.
12. Describe the process of implementing remote validation.
13. Describe the process of customizing client-side data validation.

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 3
The Movie class
public class Movie
{
public string Name { get; set; }
public int Rating { get; set; }
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 4
A strongly-typed view that posts a Movie object
to an action method
@model Movie
...
<div asp-validation-summary="All" class="text-danger">
</div>

<form asp-action="Add" method="post" class="form-inline">


<div class="form-group">
<label>Name:</label>&nbsp;
<input asp-for="Name" class="form-control" />
</div>&nbsp;
<div class="form-group">
<label>Rating (1 - 5):</label>&nbsp;
<input type="text" asp-for="Rating"
class="form-control" />
</div>&nbsp;
<button type="submit" class="btn btn-primary">Submit
</button>
</form>
...

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 5
An action method that receives a Movie object
from the view
[HttpPost]
public IActionResult Add(Movie movie)
{
if (ModelState.IsValid) {
/* code to add movie goes here */
return RedirectToAction("List", "Movie");
}
else {
return View(movie);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 6
How it looks in the browser when the rating
can’t be cast to an int

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 7
Common data attributes for validation
Required
Range(min, max)
Range(type, min, max)
StringLength(length)
RegularExpression(ex)
Compare(other)
Display(Name = "n")

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 8
The Movie entity class with data attributes
using System.ComponentModel.DataAnnotations;
...
public class Movie
{
[Required]
[StringLength(30)]
public string Name { get; set; }

[Range(1, 5)]
public int Rating { get; set; }
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 9
The view after the user submits invalid data

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 10
The default validation messages
Attribute Message
Required The field [Name] is required.
Range The field [Name] must be between
[minimum] and [maximum].
StringLength The field [Name] must be a string
with a maximum length of [length].
RegularExpression The field [Name] must match the
regular expression '[pattern]'.
Compare '[Name]' and '[OtherName]' do not
match.

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 11
How to replace the default message
with a custom message
[Required(ErrorMessage = "Please enter a name")]
[StringLength(30,
ErrorMessage = "Name must be 30 characters or less.")]
public string Name { get; set; }

[Range(1, 5,
ErrorMessage = "Please enter a rating between 1 and 5.")]
public int Rating { get; set; }

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 12
The Customer class
public class Customer {
[Required(ErrorMessage = "Please enter a name.")]
[RegularExpression("^[a-zA-Z0-9]+$",
ErrorMessage = "Name may not contain special characters.")]
public string Name { get; set; }

[Required(ErrorMessage = "Please enter a date of birth.")]


[Range(typeof(DateTime), "1/1/1900", "12/31/9999",
ErrorMessage = "Date of birth must be after 1/1/1900.")]
public DateTime? DOB { get; set; }

[Required(ErrorMessage = "Please enter a password.")]


[StringLength(25)]
[Compare("ConfirmPassword")]
public string Password { get; set; }

[Required(ErrorMessage = "Please confirm your password.")]


[Display(Name = "Confirm Password")]
public string ConfirmPassword { get; set; }
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 13
A validation tag in a strongly-typed view
that posts a Customer object
<div asp-validation-summary="All" class="text-danger">
</div>
...
<input asp-for="Name" class="form-control" />
<input type="text" asp-for="DOB" class="form-control" />
<input type="password" asp-for="Password"
class="form-control" />
<input type="password" asp-for="ConfirmPassword"
class="form-control" />

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 14
An action method that receives a Customer object
from the view
public IActionResult Index(Customer customer) {
if (ModelState.IsValid) {
// code that adds customer to database
return RedirectToAction("Welcome");
} else {
return View(customer);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 15
The Registration page after the user submits
invalid data

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 16
The HTML emitted for an <input> tag bound
to the Name property
<input type="text" class="form-control"
data-val="true"
data-val-regex="Name may not contain special characters."
data-val-regex-pattern="^[a-zA-Z0-9 ]&#x2B;$"
data-val-required="Please enter a name."
id="Name" name="Name" value="" />

The HTML emitted for that <input> tag


when data validation fails
<input type="text" class="form-control input-validation-error"
data-val="true"
data-val-regex="Name may not contain special characters."
data-val-regex-pattern="^[a-zA-Z0-9 ]&#x2B;$"
data-val-required="Please enter a name."
id="Name" name="Name" value="" />

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 17
The HTML emitted for a summary of valid data
<div class="text-danger validation-summary-valid"
data-valmsg-summary="true">
<ul><li style="display:none"></li></ul>
</div>

The HTML emitted for a summary of invalid data


<div class="text-danger validation-summary-errors"
data-valmsg-summary="true">
<ul>
<li>Please enter a name.</li>
<li>Please enter a date of birth.</li>
</ul>
</div>

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 18
Some CSS styles in the site.css file
.input-validation-error {
border: 2px solid #dc3545; /* same red as text-danger */
background-color: #faebd7; /* antique white */
}

.validation-summary-valid { display: none; }

.validation-summary-errors ul { list-style: none; }

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 19
The view with formatted validation

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 20
Properties of the ModelStateDictionary class
Count
ErrorCount
IsValid
Keys
Values

Methods of the ModelStateDictionary class


AddModelError(key, msg)
GetValidationState(key)

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 21
Code that adds a validation message
to the ModelState property
using Microsoft.AspNetCore.Mvc.ModelBinding;
...
[HttpPost]
public IActionResult Index(Customer customer)
{
string key = nameof(Customer.DOB);

if (ModelState.GetValidationState(key) ==
ModelValidationState.Valid) {
if (customer.DOB > DateTime.Today) {
ModelState.AddModelError(
key, "Date of birth must not be a future date.");
}
}
if (ModelState.IsValid) {
// code that adds customer to database
return RedirectToAction("Welcome");
}
else {
return View(customer);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 22
Two tag helpers used with data validation
asp-validation-summary
asp-validation-for

The values of the ValidateSummary enum


All
ModelOnly
None

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 23
An action method that adds a model-level
validation message
[HttpPost]
public IActionResult Index(Customer customer) {
if (ModelState.IsValid) {
// code that adds customer to database
return RedirectToAction("Welcome");
} else {
ModelState.AddModelError("",
"There are errors in the form.");
return View(customer);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 24
Part of a view that displays both model-level
and property-level messages
<div asp-validation-summary="ModelOnly"
class="text-danger"></div>

<form asp-action="Index" method="post">


<div class="form-group row">
<div class="col-sm-2"><label>Name:</label></div>
<div class="col-sm-4">
<input asp-for="Name" class="form-control" />
</div>
<div class="col-sm-6">
<span asp-validation-for="Name"
class="text-danger"></span>
</div>
</div>
...

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 25
The form in the browser after validation fails

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 26
The jQuery libraries that download by default
with the MVC template

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 27
The jQuery libraries in a Layout view
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/lib/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet" />
<link href="~/css/site.css" rel="stylesheet" />
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/
jquery.validate.min.js">
</script>
<script src="~/lib/jquery-validation-unobtrusive/
jquery.validate.unobtrusive.min.js">
</script>
<title>@ViewBag.Title</title>
</head>

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 28
Some caveats to client-side validation
 It only works correctly with property-level validation, not model-
level validation.
 Any server-side validation doesn’t run until all client-side
validation passes. This can lead to a 2-step process that may
annoy some users.
 Not all data annotations work properly on the client. In the
example below, for instance, the Range annotation for the DOB
field isn’t working as it should.

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 29
The form in the browser with client-side validation

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 30
Classes used to create a custom attribute
ValidationAttribute
ValidationContext
ValidationResult

A virtual method of the ValidationAttribute class


IsValid(object, context)

A constructor of the ValidationResult class


ValidationResult(string)

A field of the ValidationResult class


Success

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 31
A custom attribute that checks if a date
is in the past
public class PastDateAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext ctx)
{
if (value is DateTime) {
DateTime dateToCheck = (DateTime)value;
if (dateToCheck < DateTime.Today) {
return ValidationResult.Success;
}
}

string msg = base.ErrorMessage ??


$"{ctx.DisplayName} must be a valid past date.";
return new ValidationResult(msg);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 32
Code that uses the PastDate attribute
with the default validation message
[PastDate]
public DateTime? DOB { get; set; }

How it looks in the browser

Code that uses the PastDate attribute


with a custom validation message
[PastDate(ErrorMessage =
"Please enter a date of birth in the past.")]
public DateTime? DOB { get; set; }

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 33
A custom attribute that accepts values (part 1)
public class YearsFromNowAttribute : ValidationAttribute
{
private int numYears;
public YearsFromNowAttribute(int years) {
numYears = years;
}
public bool IsPast { get; set; } = false;

protected override ValidationResult IsValid(object value,


ValidationContext ctx)
{
if (value is DateTime) {
// cast value to DateTime
DateTime dateToCheck = (DateTime)value;

// calculate date range


DateTime now = DateTime.Today;
DateTime from;

if (IsPast) {
from = new DateTime(now.Year, 1, 1);
from = from.AddYears(-numYears);
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 34
A custom attribute that accepts values (part 2)
else {
from = new DateTime(now.Year, 12, 31);
from = from.AddYears(numYears);
}

// check date
if (IsPast) {
if (dateToCheck >= from && dateToCheck < now) {
return ValidationResult.Success;
}
}
else {
if (dateToCheck > now && dateToCheck <= from) {
return ValidationResult.Success;
}
}
}
string msg = base.ErrorMessage ??
ctx.DisplayName + " must be a " +
(IsPast ? "past" : "future") + " date within " +
numYears + " years of now.";
return new ValidationResult(msg);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 35
A DOB property that requires a past date
no more than 100 years ago
[YearsFromNow(100, IsPast = true)]
public DateTime? DOB { get; set; }

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 36
A custom attribute that checks more than one
property in a class
public class RequiredContactInfoAttribute :
ValidationAttribute
{
protected override ValidationResult IsValid(object v,
ValidationContext c){
var cust = (Customer)c.ObjectInstance;
if (string.IsNullOrEmpty(cust.PhoneNumber) &&
string.IsNullOrEmpty(cust.EmailAddress))
{
string msg = base.ErrorMessage ??
"Enter phone number or email.";
return new ValidationResult(msg);
}
else {
return ValidationResult.Success;
}
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 37
The method of the IValidatableObject interface
Validate(context)

A constructor of the ValidationResult class


ValidationResult(m, list)

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 38
A custom validation class that checks
more than one field
public class Customer : IValidatableObject {
...
[Required(ErrorMessage = "Please enter a date of birth.")]
public DateTime? DOB { get; set; }

public string PhoneNumber { get; set; }


public string EmailAddress { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext ctx) {


if (DOB > DateTime.Now)
{
yield return new ValidationResult(
"Date of birth can't be in the future.",
new[] { nameof(DOB) });
}
if (string.IsNullOrEmpty(PhoneNumber) &&
string.IsNullOrEmpty(EmailAddress))
{
yield return new ValidationResult(
"Please enter a phone number or email address.",
new[] { nameof(PhoneNumber), nameof(EmailAddress) });
}
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 39
A method of the IClientModelValidator interface
AddValidation(context)

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 40
The updated PastDate attribute
with client-side validation (part 1)
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
...
public class PastDateAttribute : ValidationAttribute, IClientModelValidator
{
private int numYears;
public PastDateAttribute(int years = -1) => numYears = years;

protected override ValidationResult IsValid(object val,


ValidationContext ctx)
{
if (val is DateTime) {
DateTime dateToCheck = (DateTime)val;
if (numYears == -1) { // no limit on past date
if (dateToCheck < DateTime.Today)
return ValidationResult.Success;
} else {
DateTime minDate = DateTime.Today.AddYears(-numYears);
if (dateToCheck >= minDate && dateToCheck < DateTime.Today)
return ValidationResult.Success;
}
}
return new ValidationResult(GetMsg(ctx.DisplayName));
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 41
The updated PastDate attribute (part 2)
public void AddValidation(ClientModelValidationContext c)
{
if (!c.Attributes.ContainsKey("data-val"))
c.Attributes.Add("data-val", "true");
c.Attributes.Add("data-val-pastdate-numyears", numYears.ToString());
c.Attributes.Add("data-val-pastdate",
GetMsg(c.ModelMetadata.DisplayName ?? c.ModelMetadata.Name));
}

private string GetMsg(string name) =>


base.ErrorMessage ?? name + " must be a valid past date" +
(numYears == -1 ? "." : " (max " + numYears + " years ago).");
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 42
A model property with the PastDate attribute
[PastDate(100)]
public DateTime? DOB { get; set; }

The HTML that the PastDate attribute emits


<input type="text" class="form-control" data-val="true"
data-val-pastdate=
"DOB must be a valid past date (max 100 years ago)."
data-val-pastdate-numyears="100" id="DOB" name="DOB" value=""
/>

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 43
The pastdate.js file (part 1)
jQuery.validator.addMethod("pastdate",
function (value, element, param) {
// get date entered by user, confirm it's a date
if (value === '') return false;
var dateToCheck = new Date(value);
if (dateToCheck === "Invalid Date") return false;

// get the number of years


var numYears = Number(param);

// get the current date


var now = new Date();

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 44
The pastdate.js file (part 2)
// check date
if (numYears == -1) {
if (dateToCheck < now) return true;
} else {
// calculate limit
var minDate = new Date();
var minYear = now.getFullYear() - numYears;
minDate.setFullYear(minYear);

if (dateToCheck >= minDate && dateToCheck < now)


return true;
}
return false;
});

jQuery.validator.unobtrusive.adapters.addSingleVal(
"pastdate", "numyears");

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 45
The header section of the layout
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/lib/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet" />

<script src="~/lib/jquery/jquery.min.js"></script>
<script src="~/lib/jquery-validation/jquery.validate.min.js">
</script>
<script src="~/lib/jquery-validation-unobtrusive/
jquery.validate.unobtrusive.min.js"></script>
<script src="~/js/pastdate.js"></script>
<title>@ViewBag.Title</title>
</head>

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 46
Two constructors of the RemoteAttribute class
RemoteAttribute(act, ctl)
RemoteAttribute(act, ctl, area)

A model property with a Remote attribute


[Remote("CheckEmail", "Validation")]
public string Email { get; set; }

The CheckEmail() action method


public JsonResult CheckEmail(string email)
{
bool hasEmail = Utility.CheckEmail(email);
if (hasEmail)
return Json(
$"Email address {email} is already registered.");
else
return Json(true);
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 47
One property of the RemoteAttribute class
AdditionalFields

Remote validation that gets data


from additional fields
The model
[Remote("CheckEmail", "Validation",
AdditionalFields = "Username, Region")]
public string Email { get; set; }
public string Username { get; set; }

The view
<input asp-for="EmailAddress" class="form-control" />
<input asp-for="Username" class="form-control" />
<input type="hidden" name="Region" value="West" />

The CheckEmail() action method


public JsonResult CheckEmail (string email, string username,
string region){
// validation code
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 48
The Registration app with invalid data

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 49
The CSS for the validation class
.input-validation-error {
border: 2px solid #dc3545; /* text-danger */
background-color: #faebd7; /* antique white */
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 50
The Customer class (part 1)
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Mvc;
...
public class Customer
{
public int ID { get; set; } // automatically generated

[Required(ErrorMessage = "Please enter a username.")]


[RegularExpression("^[a-zA-Z0-9 ]+$", ErrorMessage =
"Username may not contain special characters.")]
public string Username { get; set; }

[Required(ErrorMessage = "Please enter an email address.")]


[Remote("CheckEmail", "Validation")]
public string EmailAddress { get; set; }

[Required(ErrorMessage = "Please enter a date of birth.")]


[MinimumAge(13, ErrorMessage =
"You must be at least 13 years old.")]
public DateTime? DOB { get; set; }

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 51
The Customer class (part 2)
[Required(ErrorMessage = "Please enter a password.")]
[Compare("ConfirmPassword")]
[StringLength(25, ErrorMessage =
"Please limit your password to 25 characters.")]
public string Password { get; set; }

[Required(ErrorMessage = "Please confirm your password.")]


[Display(Name = "Confirm Password")]
[NotMapped]
public string ConfirmPassword { get; set; }
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 52
The RegistrationContext class
public class RegistrationContext : DbContext
{
public RegistrationContext(
DbContextOptions<RegistrationContext> options)
: base(options) { }

public DbSet<Customer> Customers { get; set; }


}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 53
The MinimumAgeAttribute class (part 1)
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
...
public class MinimumAgeAttribute : ValidationAttribute,
IClientModelValidator
{
private int minYears;
public MinimumAgeAttribute(int years) {
minYears = years;
}

// overrides IsValid() method of ValidationAttribute base class


protected override ValidationResult IsValid(object value,
ValidationContext ctx) {
if (value is DateTime) {
DateTime dateToCheck = (DateTime)value;
dateToCheck = dateToCheck.AddYears(minYears);
if (dateToCheck <= DateTime.Today) {
return ValidationResult.Success;
}
}
return new ValidationResult(GetMsg(ctx.DisplayName));
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 54
The MinimumAgeAttribute class (part 2)
// implements AddValidation() method of IClientModelValidator
// interface
public void AddValidation(ClientModelValidationContext ctx) {
if (!ctx.Attributes.ContainsKey("data-val"))
ctx.Attributes.Add("data-val", "true");
ctx.Attributes.Add("data-val-minimumage-years",
minYears.ToString());
ctx.Attributes.Add("data-val-minimumage",
GetMsg(ctx.ModelMetadata.DisplayName ??
ctx.ModelMetadata.Name));
}

private string GetMsg(string name) =>


base.ErrorMessage ??
$"{name} must be at least {minYears} years ago.";
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 55
The minumim-age JavaScript file
jQuery.validator.addMethod("minimumage",
function(value, element, param) {
if (value === '') return false;

var dateToCheck = new Date(value);


if (dateToCheck === "Invalid Date") return false;

var minYears = Number(param);

dateToCheck.setFullYear(dateToCheck.getFullYear() +
minYears);

var today = new Date();


return (dateToCheck <= today);
});

jQuery.validator.unobtrusive.adapters.addSingleVal(
"minimumage", "years");

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 56
The Validation controller
public class ValidationController : Controller
{
private RegistrationContext context;
public RegisterController(RegistrationContext ctx) =>
context = ctx;

public JsonResult CheckEmail(string emailAddress)


{
string msg = Check.EmailExists(context,
emailAddress);
if (string.IsNullOrEmpty(msg)) {
TempData["okEmail"] = true;
return Json(true);
}
else return Json(msg);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 57
The Register controller (part 1)
public class RegisterController : Controller
{
private RegistrationContext context;
public RegisterController(RegistrationContext ctx) =>
context = ctx;

public IActionResult Index() => View();

[HttpPost]
public IActionResult Index(Customer customer)
{
if (TempData["okEmail"] == null) {
string msg = Check.EmailExists(
context, customer.EmailAddress);
if (!String.IsNullOrEmpty(msg)) {
ModelState.AddModelError(
nameof(Customer.EmailAddress), msg);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 58
The Register controller (part 2)
if (ModelState.IsValid) {
context.Customers.Add(customer);
context.SaveChanges();
return RedirectToAction("Welcome");
}
else return View(customer);
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 59
The static Check class
public static class Check
{
public static string EmailExists(RegistrationContext ctx,
string email) {
string msg = "";
if (!string.IsNullOrEmpty(email)) {
var customer = ctx.Customers.FirstOrDefault(
c => c.EmailAddress.ToLower() == email.ToLower());
if (customer != null)
msg = $"Email address {email} already in use.";
}
return msg;
}
}

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 60
The layout
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<link rel="stylesheet" type="text/css"
href="~/lib/bootstrap/dist/css/bootstrap.min.css">
<link href="~/css/site.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<header class="bg-primary text-white text-center">
<h1 class="m-3 p-3">Registration</h1>
</header>
<main>
@RenderBody()
</main>
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
@RenderSection("scripts", false)
</body>
</html>

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 61
The Register/Index view (part 1)
@model Customer

@{
ViewData["Title"] = "Registration";
}

@section scripts {
<script
src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/
jquery.validate.unobtrusive.min.js"></script>
<script src="~/js/minimum-age.js"></script>
}

<form asp-action="Index" method="post">


<div class="form-group row">
<div class="col-sm-2"><label>Username:</label></div>
<div class="col-sm-4">
<input asp-for="Username" class="form-control" /></div>
<div class="col">
<span asp-validation-for="Username"
class="text-danger"></span></div></div>

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 62
The Register/Index view (part 2)
<div class="form-group row">
<div class="col-sm-2"><label>Email Address:</label></div>
<div class="col-sm-4">
<input asp-for="EmailAddress" class="form-control" />
</div>
<div class="col">
<span asp-validation-for="EmailAddress"
class="text-danger"></span></div></div>
<div class="form-group row">
<div class="col-sm-2"><label>DOB:</label></div>
<div class="col-sm-4">
<input type="text" asp-for="DOB"
class="form-control" /></div>
<div class="col">
<span asp-validation-for="DOB"
class="text-danger"></span></div></div>
<!-- Password and ConfirmPassword fields -->
<div class="row">
<div class="offset-2 col-sm-4">
<button type="submit" class="btn btn-primary">
Register</button>
</div></div>
</form>

© 2020, Mike Murach & Associates, Inc.


Murach's ASP.NET Core MVC C11, Slide 63

You might also like