Embedded functions in the page
For including functions in the page, you have two options.
Extending HtmlHelper
:
You can extend HtmlHelper
so that you can call Html.getFieldError("field")
. Because ViewBag
is in HtmlHelper
, you won't need to pass that into the function. The best way to demonstrate this is by showing an example.
public static class ErrorValidation
{
public static MvcHtmlString getFieldError(this HtmlHelper<TModel> h, string f)
{
if(h.ViewBag.ValidationErrors.ContainsKey(f))
{
return MvcHtmlString.Create(h.ViewBag.ValidationErrors[f]);
}
return MvcHtmlString.Empty;
}
}
Adding a namespace in your views:
You can include a namespace in your views by adding a line to the Views\Web.config
file. Then, you could use static methods like you planned. This is done by adding a line of something like <add namespace="MyProj.Validation" />
inside of <configuration><system.web.webPages.razor><pages><namespaces>
. In addition, you can leave this out by calling the full reference to your function each time with MyProj.Validation.getFieldError(...)
.
Relying on MVC error handling
You can also use API's already built into MVC, which do allow for customized validation.
Doing error checks through model attributes:
The most simple way to do validation is by adding attributes to your model. If your model had a required field, you can simply add [Required]
above the field in the class that defines your model. A plethora of validation attributes are provided by System.ComponentModel.DataAnnotations
.
If you wanted to do a custom check, you could create your own attribute by implementing abstract class ValidationAttribute
. Simply define the IsValid
function with your custom logic.
If you have validation checks that need to happen on multiple fields in your model at the same time, you can have your model implement IValidatableObject
. And to make this simpler, in the Validate
function you don't need to create a list and add all your errors to that; simply return each ValidationResult
object by putting the keyword yield
at the beginning of the line. This would look like...
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
// Duplicate checks
List<String> fields = new List<String>();
for (var i=0; i<PhoneNumbers.Count; i++)
{
var item = PhoneNumbers[i];
if (PhoneNumbers.IndexOf(item) != PhoneNumbers.LastIndexOf(item))
{
fields.Add("PhoneNumbers["+i+"]");
}
}
if(fields.Count > 0)
{
yield return new ValidationResult("You cannot include duplicate phone numbers.", fields.ToArray());
}
// More validation checks
}
Doing error checks in the controller:
You can also do error checks in the controller, which allows for validation to vary depending on the action. This also lets you use the validation that already happened in the model with the ModelState
object. In order to add errors to ModelState
, simply call ModelState.AddModelError()
. In order to check if a model is valid after all checks are done, you can check ModelState.IsValid
.
Displaying errors in the view:
With validation happening in the ModelState
object, you can display errors in your view by using the Html
object. This allows you to generate a list of errors by calling Html.ValidationSummary()
or display errors for individual properties with Html.ValidationMessageFor(...)
. Here's an extensive example...
for (var x = 0; x < Model.PhoneNumbers.Count(); x++ )
{
<tr>
<td>@Html.EditorFor(m => m.PhoneNumbers.ElementAt(x))</td>
@if(ViewData.ModelState["PhoneNumbers[" + x + "]"].Errors.Any())
{
<td>@Html.ValidationMessageFor(m => m.PhoneNumbers.ElementAt(x))</td>
}
else
{
<td>Ok!</td>
}
</tr>
}