Validate that a certain checkbox is checked before submit(ASP.NET MVC)
Introduction
In this article we will discuss different approaches of performing validation on a checkbox value to be true in an MVC view.This validation should combine security and be user friendly as well
Prepare Scenario
1.Create an MVC application as in the following pictures(name it ApproachForValidatingAcheckbox for example)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class UserAgreementModel
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3.Add a controller class under controllers folder as follows
4.Add a view for submitting an instance of the model as follows
place the following code in the view
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@model ApproachForValidatingAcheckbox.Models.UserAgreementModel
<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">
<p>
This is your agreement text.make sure you click on the checkbox below to show your approval
</p>
</div>
<div class="form-group">
<input type="checkbox" /> I agree
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
5.Go back to the controller and create the following action methods(make sure to import models namespace
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public IActionResult Index()
{
return View();
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(UserAgreementModel userAgreementModel)
{
return RedirectToAction("Index");
}
6.Add a view called index in views\UserAgreement as follows
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
7.Adjust the code of the create action in UserAgreementController to redirect the client to the index action(where a thanks message will be displayed as above)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[HttpPost]
public ActionResult Create(UserAgreementModel userAgreementModel)
{
return RedirectToAction("Index");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
8.Run the application.Replace.When the page displays type UserAgreement/Create in the end of the URL and press enter
Methods of validation
*If we would like to prevent the form from being submitted unless the value of the checkbox is true
,we have the two approaches discussed below
1. The basic approach
The basic approach is to add a client side (i.e, java script code ) to be executed before the form submit
A) Procedure
i) add an event handler to the submit button .The declaration of the submit button will become as follows
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
<input type="submit" value="Create" class="btn btn-primary" onclick="return formValid()" />
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ii)Add a java script function as follows (make sure it is inside the scripts section)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
<script>
function formValid() {
var checked = $("input[type='checkbox']").is(":checked");
if (!checked) {
alert("please show your approval of agreement")
}
return checked
}
</script>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
iii)Now an alert message will be shown if the user does not approve the agreement as below
B)Drawback
The major drawback of this approach is that it relies on client side only.client side validation can be bypassed easily(please refer to discussions like this one for more information https://stackoverflow.com/questions/15855770/why-do-we-need-both-client-side-and-server-side-validation)
2.The second approach
*The second approach is to transform the validation to be server side.
There are two sub approaches
2.1) Bind the checkbox to a boolean property as follows
i)Add a boolean property to the model as follows
//////////////////////////////////////////
public class UserAgreementModel
{
[Required]
public string Name { get; set; }
public bool UserAgreed { get; set; }
}
///////////////////////////
ii) Add a validation satement to the controller action method as follows
if(!userAgreement.UserAgreed)
{
ViewBag.UserDidNotApproveAgreement = "please show your approval of agreement";
return View();
}
iii)Bind the checkbox to the model boolean property as follows
<input type="checkbox" asp-for="UserAgreed">
iv) Add the following div above submit button as follows
<div class="text-danger">@ViewBag.UserDidNotApproveAgreement</div>
*Now if the java script validation function is bypassed
(it becomes as below for example
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function formValid() {
//var checked = $("input[type='checkbox']").is(":checked");
//if (!checked)
//{
// alert("please show your approval of agreement")
//}
//return checked
return true
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
),
the div declared above will be populated with the text "please show your approval of agreement".as below
*This approach has a draw back that it does not get use of the obtrusive validation.
*In obtrusive validation the inputs bound to the model fields are decorated with data-val-attributes once the view is rendered.This is because of the data annotation validation in the model
*You will notice a delay before the validation message shows due to the client server round trip
2.2 )Server side approach with obtrusive validation
i)Add a numeric field to the model as follows (make sure to import System.ComponentModel.DataAnnotations;
////////////////////////////////////////////////////////////////
using System.ComponentModel.DataAnnotations;
public class UserAgreementModel
{
[Required]
public string Name { get; set; }
public bool UserAgreed { get; set; }
[Required(ErrorMessage = "please show your approval of agreement")]
[Range(1,1,ErrorMessage = "please show your approval of agreement")]
public byte UserAgrees { get; set; }
}
ii)Add a hidden field to the the view.Bind the hidden field to the newly-added property mentioned above
///////////////////////////////////////////////////////////////////////////////
<div class="form-group">'
<input type="text" asp-for="UserAgrees" value="0" class="form-control" /> @*if type is hidden validation will not work*@
<span asp-validation-for="UserAgrees" class="text-danger"></span>
</div>
<div class="text-danger">@ViewBag.UserDidNotApproveAgreement</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" onclick="return formValid()" />
</div>
///////////////////////////////////////////////////////////////////////
iii)Add the following function code to the javascript part.this code changes the value of the newly-added property according to the value of the checkbox
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$(function () {
$("input[type='checkbox']").change(function()
{
$("#UserAgrees").val($(this).is(":checked")?"1":"0")
})
})
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
iv)Adjust the validation statement from the controller action as follows
if(!userAgreement.UserAgreed||userAgreement.UserAgrees!=1)
{
ViewBag.UserDidNotApproveAgreement = "please show your approval of agreement";
return View();
}
Now, when trying to submit the form, the validation message will be displayed as in the error message property of the range validation declared within the model.This approach secures the validation as well as it prevents round trip
If you click the agree button you will be redirected to index action as follows
The approach 2.2 was my own and came into my mind when i faced the problem .another way to implement this approach can be found here
https://www.yogihosting.com/server-side-validation-asp-net-mvc/
Approach 2.2 method b
i)add a class to your project as follows
///////////////////////////////////////////////////////////////
public sealed class AgreementRequired : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (Convert.ToBoolean(value) == false)
return new ValidationResult(ErrorMessage);
else
return ValidationResult.Success;
}
}
////////////////////////////////////////////////////////////
ii)decorate the boolean field in the model with this validation attribute
////////////////////////////////////////////////////////////
[ AgreementRequired(ErrorMessage = "please show your approval of agreement")]
public bool UserAgreed { get; set; }
////////////////////////////////////////////////////////////
iii)replace the HTML part of the view with the following
////////////////////////////////////////////////////////////
<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">
<p>
This is your agreement text.make sure you click on the checkbox below to show your approval
</p>
</div>
<div class="form-group">
<input type="checkbox" asp-for="UserAgreed" /> I agree
</div>
<span asp-validation-for="UserAgreed" class="text-danger"></span>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" onclick="return formValid()" />
</div>
</form>
</div>
</div>
///////////////////////////////////////////////////////////////////////////////////////////////
iv)replace controller (post) action method with the following
///////////////////////////////////////////////////////////////////////////////////////////////
[HttpPost]
public IActionResult Create(UserAgreementModel userAgreementModel)
{
if (!ModelState.IsValid)
{
return View();
}
return RedirectToAction("Index");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
Conclusion
Validating an HTML form is a process that should consider performance ,security and user experience together.
Comments
Post a Comment