Data Annotations Extensions EqualTo and Compare not working - asp.net-mvc-3

I am using MVC3 and razaor view engine
I am trying to make sure that password and confirm password should match but this is not working for me. It just keeps on telling me "password" and "confirm password" do not match.
I have used "compare' and "equalto", both are resulting in the same error.
Business entity:
namespace Project.BusinessEntities
{
public partial class RegisterPasswordUpdate
{
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public string UserId { get; set; }
}
}
Validation:
using System.ComponentModel.DataAnnotations;
using DataAnnotationsExtensions;
using System.Web.Mvc;
using Project.Resources;
namespace Project.BusinessEntities
{
[MetadataType(typeof(RegisterPasswordUpdate.RegisterPasswordUpdateMetaData))]
public partial class RegisterPasswordUpdate
{
public class RegisterPasswordUpdateMetaData
{
[Required(ErrorMessage = ValidationMessageConstants.ResponseRequired)]
[StringLength(16, MinimumLength = 8)]
[RegularExpression(#"[A-Za-z0-9]*", ErrorMessage = ValidationMessageConstants.AlphaNumericOnly)]
public string Password { get; set; }
[Required(ErrorMessage = ValidationMessageConstants.ResponseRequired)]
[StringLength(16, MinimumLength = 8, ErrorMessage = ValidationMessageConstants.MinimumLength)]
[RegularExpression(#"[A-Za-z0-9]*", ErrorMessage = ValidationMessageConstants.AlphaNumericOnly)]
[EqualTo("Password", ErrorMessage = ValidationMessageConstants.ConfirmPassword)]
public string ConfirmPassword { get; set; }
}
}
}
Use:
#Html.TextBoxFor(x => x.PasswordUpdate.Password, new { maxlength = "16", size = "16" })
#Html.ValidationMessageFor(x => x.PasswordUpdate.Password)
#Html.TextBoxFor(x => x.PasswordUpdate.ConfirmPassword, new { maxlength = "16", size = "16" })
#Html.ValidationMessageFor(x => x.PasswordUpdate.ConfirmPassword)
html:
<input data-val="true" data-val-length="The field Password must be a string with a minimum length of 8 and a maximum length of 16." data-val-length-max="16" data-val-length-min="8" data-val-regex="Only alphanumeric (A-Z a-z 0-9) values are allowed" data-val-regex-pattern="[A-Za-z0-9]*" data-val-required="Response required!" id="PasswordUpdate_Password" maxlength="16" name="PasswordUpdate.Password" size="16" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="PasswordUpdate.Password" data-valmsg-replace="true"></span>
<input data-val="true" data-val-equalto="Password and confirm password do not match" data-val-equalto-other="*.Password" data-val-length="Minimum length not met" data-val-length-max="16" data-val-length-min="8" data-val-regex="Only alphanumeric (A-Z a-z 0-9) values are allowed" data-val-regex-pattern="[A-Za-z0-9]*" data-val-required="Response required!" id="PasswordUpdate_ConfirmPassword" maxlength="16" name="PasswordUpdate.ConfirmPassword" size="16" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="PasswordUpdate.ConfirmPassword" data-valmsg-replace="true"></span>
html ids on the page:
PasswordUpdate_Password
PasswordUpdate_ConfirmPassword
html names on the page:
PasswordUpdate.Password
PasswordUpdate.ConfirmPassword
What is missing here?

It is a bug and the following post helped fix it:
http://forums.asp.net/t/1716181.aspx/1
The problem lies in missing single quotes in theunobtrusive validation code. This line is incorrect:
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
and should be replace by
element = $(options.form).find(":input[name='" + fullOtherName + "']")[0];

Related

Display validation error message - compare one input against another

Entered Max value must be greater than Min. Right now my code displays error message when Max is same as Min (using compare). Is there validator that can be used to compare one input against another?
MyData.cs:
public class MyData
{
[Required]
public double Min { get; set; }
[Compare("Min", ErrorMessage = "checks for matching min value")]
public double Max { get; set; }
}
Form.razor:
<div class="modal-body">
<EditForm EditContext="#context">
<DataAnnotationsValidator />
<label class="form-label" for="Min">Min</label>
<input class="form-control" #bind=model.Min type="text">
<label class="form-label" for="Max">Max</label>
<input class="form-control" #bind=model.Max type="text">
<ValidationMessage For="#(() => model.Max)" />
</EditForm>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" #onclick="() => Done()">Apply</button>
</div>
#code {
private MyData model = new MyData();
private EditContext context;
protected override void OnInitialized()
{
model = (MyData)(modalDialog?.ModalRequest.InData ?? new MyData());
context = new EditContext(model);
}
private void Done()
{
if (#model.Max < #model.Min)
{
context.Validate(); #*this displays error message*#
}
else
{
modalDialog?.Close(ModalResult.OK(model));
}
}
To validate for greater than or less than against another property instead of a value using Data Annotations you require to create a custom validation attribute as shown below:
GreaterThan attribute
// Custom attribute for validating greater than other property
public class GreaterThan : ValidationAttribute
{
private readonly string _comparisonProperty;
public GreaterThan(string comparisonProperty)
{
_comparisonProperty = comparisonProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ErrorMessage = ErrorMessageString;
var currentValue = (double)value; // cast to double same as property type
var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
if (property == null)
throw new ArgumentException("Property with this name not found");
var comparisonValue = (double)property.GetValue(validationContext.ObjectInstance); // cast to property type
// comparison condition
if (currentValue < comparisonValue)
return new ValidationResult(ErrorMessage);
return ValidationResult.Success;
}
}
LessThan attribute
You can use the same code above to create for LessThan attribute by changing the name and comparison condition to currentValue > comparisonValue.
Below is an example on how to use Data Annotations to validate your model and display validation errors in the form. It includes GreaterThan custom validation attribute together with other common validation attribute.
Demo
Class:
public class MyData
{
[Required]
[MaxLength(40, ErrorMessage = "Name should be less than 40 characters")]
[MinLength(4, ErrorMessage ="Name should be more than 4 characters")]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime? BirthDate { get; set; }
[Required]
public double Min { get; set; }
[GreaterThan("Min", ErrorMessage = "Max must be greater than Min")]
public double Max { get; set; }
[Required(ErrorMessage = "Password is required.")]
public string Password { get; set; }
[Required(ErrorMessage = "Confirmation Password is required.")]
[Compare("Password", ErrorMessage = "Password and Confirmation Password must match.")]
public string ConfirmPassword { get; set; }
}
Razor:
Note: you can upgrade the styling for your form fields and validation message to your liking.
#page "/"
#using BlazorApp1.Models
<EditForm Model="#myData" OnValidSubmit="#HandleValidSubmit">
<DataAnnotationsValidator/>
<ValidationSummary/>
<p>
<label for="Name">Name: </label>
<InputText id="Name" #bind-Value="myData.Name"/>
<ValidationMessage For="() => myData.Name"/>
</p>
<p>
<label for="Min">Min: </label>
<InputNumber id="Min" #bind-Value="myData.Min"/>
<ValidationMessage For="() => myData.Min"/>
</p>
<p>
<label for="Max">Max: </label>
<InputNumber id="Max" #bind-Value="myData.Max"/>
<ValidationMessage For="() => myData.Max"/>
</p>
<p>
<label for="BirthDate">BirthDate: </label>
<InputDate id="BirthDate" #bind-Value="myData.BirthDate"/>
<ValidationMessage For="() => myData.BirthDate"/>
</p>
<p>
<label for="Password">Password: </label>
<InputText id="Password" #bind-Value="myData.Password"
type="password"/>
<ValidationMessage For="() => myData.Password"/>
</p>
<p>
<label for="ConfirmPassword">ConfirmPassword: </label>
<InputText id="ConfirmPassword" #bind-Value="myData.ConfirmPassword"
type="password"/>
<ValidationMessage For="() => myData.ConfirmPassword"/>
</p>
<button type="submit">Submit</button>
</EditForm>
#code {
private readonly MyData myData = new();
private void HandleValidSubmit()
{
// Save the data
}
}
Output:

What is the best way to get a configured value to a class library for validation?

I currently have a Blazor app that references a class library. One of the pages in the web app is used for updating an instance of a class model in the class library. To validate I'm using Validation attributes on the class model. One of the fields for input is email which, for our software, is validated via a configurable regular expression (because each of our sites can be different).
I think the best way to do this is using a custom ValidationAttribute but I don't know the best way to get a value from the web app's app settings to the custom Validation class.
The following code is an example of what I'm trying to accomplish:
Blazor page:
<EditForm Model="#name" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label for="FirstName">First:</label>
</div>
<div>
<input id="FirstName" class="form-control profile-form-control" #bind-value="name.FirstName" #bind-value:event="oninput" type="text" maxlength="30" autocomplete="off" />
</div>
<div>
<label for="LastName">Last:</label>
</div>
<div>
<input id="LastName" class="form-control profile-form-control" #bind-value="name.LastName" #bind-value:event="oninput" type="text" maxlength="100" autocomplete="off" />
</div>
<div>
<label for="Email">Email:</label>
</div>
<div>
<input id="Email" class="form-control profile-form-control" #bind-value="name.Email" #bind-value:event="oninput" type="text" maxlength="100" autocomplete="off" />
</div>
<div>
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</EditForm>
Model (in separate class library):
public class Person
{
[Required(ErrorMessage = "First name required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last name required")]
public string LastName { get; set; }
[Required(ErrorMessage = "Email required")]
[EmailFromRegexValidator(ErrorMessage = "Email not valid")]
public string Email { get; set; }
}
Custom Validation:
public class EmailFromRegexValidator : ValidationAttribute
{
private const string defaultEmailValidationRegex = "^[\\w-]+(\\.[\\w-]+)*#([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*?\\.[a-zA-Z]{2,6}|(\\d{1,3}\\.){3}\\d{1,3})(:\\d{4})?$";
protected override ValidationResult IsValid(object value, ValidationContext context)
{
string emailRegexString = null;
var configurationBuilder = new ConfigurationBuilder();
var path = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
if (File.Exists(path))
{
configurationBuilder.AddJsonFile(path, false);
var root = configurationBuilder.Build();
emailRegexString = root.GetSection("AppConfiguration").GetSection("EmailRegex").Value;
}
emailRegexString = emailRegexString ?? defaultEmailValidationRegex;
Regex emailRegex = new Regex(emailRegexString);
if (value is string && emailRegex.IsMatch(value as string))
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(FormatErrorMessage(context.DisplayName));
}
}
}
The above code works but building configuration from a file path within a class library does not feel optimal. So I was curious if anyone had any better ideas for how to get a configurable value to the EmailFromRegexValidator?
Thanks!
Welcome! You can use ValidationContext to access a service that provides your configuration value.
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var appConfig = (AppConfiguration) validationContext
.GetService(typeof(AppConfiguration));
string emailRegexString = appConfig.EmailRegex;
...
}
public class AppConfiguration
{
public string EmailRegex { get; set; }
}
You can bind AppConfiguration from the config file using to make it available in your DI container:
services.Configure<AppConfiguration>(configuration.GetSection("AppConfiguration"))

How can I get the selected item from a Telerik UI for ASP.NET Core DropDownList

I'm using a kendo-dropdownlist tag helper from the Telerik UI for ASP.NET Core library. So far I have been able to bind the values that can be selected, but I can't figure out how to get the selected item when a post request is sent.
I have a login form:
#page
#model PITS.Areas.Authentication.Pages.Login2Model
#{
}
<form method="post">
<input class="form-control k-textbox" asp-for="UserName" type="text" />
<input class="form-control k-textbox" asp-for="Password" type="password" />
<kendo-dropdownlist name="administraties"
filter="FilterType.Contains"
placeholder="Selecteer Administratie"
style="width: 100%;"
bind-to="Model.Organizations">
</kendo-dropdownlist>
<input type="submit" class="btn btn-primary pull-right" value="Login">
</form>
and a PageModel
public class Login2Model : PageModel
{
[BindProperty]
public string UserName { get; set; }
[BindProperty]
public string Password { get; set; }
[BindProperty]
public IEnumerable<SelectListItem> Organizations { get; set; }
public void OnGet()
{
this.Organizations = _getOrganizations();
}
private IList<SelectListItem> _getOrganizations()
{
return new List<SelectListItem>
{
new SelectListItem {Value = Guid.NewGuid().ToString(), Text = "Google"},
new SelectListItem {Value = Guid.NewGuid().ToString(), Text = "Apple"},
new SelectListItem {Value = Guid.NewGuid().ToString(), Text = "Microsoft"}
};
}
}
I would expect an attribute on the kendo-dropdownlist taghelper but I haven't found it yet. Could someone tell me how to get the selected item?
This assumes you are using Razor.
Use the "for" property to bind the kendo-dropdownlist to a page model property.
for="MySelection"
Then in your page model.
public string MySelection { get; set; }

How to use CheckBoxFor in MVC forms with other form controls

Basically, i have a form with a textbox, radio button and a check box control. now i face problem with the checkbox control when i submit my page
I have a model like this
public class PersonDetails
{
public int personID { get; set; }
public string PersonName { get; set; }
public string Gender { get; set; }
public List<Education> Education { get; set; }
public string EmailID { get; set; }
public string Address { get; set; }
}
public class Education
{
public string Qualification { get; set; }
public bool Checked { get; set; }
public List<Education> GetQualification()
{
return new List<Education>{
new Education {Qualification="SSC",Checked=false},
new Education {Qualification="HSC",Checked=false},
new Education {Qualification="Graduation",Checked=false},
new Education {Qualification="PostGraduation",Checked=false}
};
}
}
and i have a view like this
#using (Html.BeginForm("GetDetails", "User", FormMethod.Post, new { id = "person-form" }))
{
<div class="col-xs-12">
<label>Person Name</label>
#Html.TextBoxFor(x => x.PersonName)
</div>
<div class="col-xs-12">
<label>Gender</label>
#Html.RadioButtonFor(x => x.Gender, "Male")
#Html.RadioButtonFor(x => x.Gender, "Female")
</div>
<div class="col-xs-12">
<label>Education</label>
#{
Html.RenderPartial("Qualification", new LearnAuthentication.Controllers.Education().GetQualification());
}
</div>
<div class="col-xs-12">
<input type="submit" value="Submit" />
</div>
}
and the partial view like this
#model List<LearnAuthentication.Controllers.Education>
<br />
#for (int i = 0; i < Model.Count(); i++)
{
#Html.HiddenFor(x => Model[i].Qualification)
#Html.CheckBoxFor(x => Model[i].Checked)
#Html.DisplayFor(x => Model[i].Qualification)
<br />
}
and my action method is this
[HttpPost]
public ActionResult GetDetails(PersonDetails personDetails)
{
return View();
}
now when i run my app i tend to get all the information but when i submit the page i get this property with null values
public List Education { get; set; }
can any of you guys help me on what i am doing wrong or could you direct me to the right path on how to achieve this.
Your use of a partial to generate the controls for Education is generating inputs such as
<input type="hidden" name="[0].Qualification" ... />
<input type="hidden" name="[1].Qualification" ... />
but in order to bind, they need to have name attributes which match your model
<input type="hidden" name="Education[0].Qualification" ... />
<input type="hidden" name="Education[1].Qualification" ... />
Rename you partial to Education.cshtml (to match the name of the class) and move it to your /Views/Shared/EditorTemplates folder (or /Views/yourControllerName/EditorTemplates if you want a specific template just for that controller)
Then change the partial to
#model LearnAuthentication.Controllers.Education
#Html.HiddenFor(m => m.Qualification)
#Html.LabelFor(m => m.Checked)
#Html.CheckBoxFor(m => m.Checked)
#Html.DisplayFor(m => m.Qualification)
and in the main view replace
<label>Education</label>
#{ Html.RenderPartial("Qualification", new LearnAuthentication.Controllers.Education().GetQualification()); }
with
<span>Education</span> // its not a label
#Html.EditorFor(m => m.Education)
which will correctly generate the correct html for each item in your collection
Side note: Other alternatives which would work would be to change the POST method signature to
[HttpPost]
public ActionResult GetDetails(PersonDetails personDetails List<Education> educationDetails)
or to pass the HtmlFieldPrefix to the partial as explained in getting the values from a nested complex object that is passed to a partial view

MVC Razor View: How to render a list of text boxes for a List<Task> in the model?

I have a List<Task> in my model. This list contains 2 tasks(say) in it
public List<Task> Tasks { get; set; }
public class Task {
public Task()
{
Title="";
Total= 0;
}
public string Title{ get; set; }
public int Total{ get; set; }
}
Now in my razor view, I want render 2 text boxes for each of the Tasks in the List<Tasks> of my model.
I didn't loop, just placed direct text boxes like:
#Html.TextBoxFor(m => m.Tasks[0].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[0].Total, new { #maxlength = "2"})
<br>
#Html.TextBoxFor(m => m.Tasks[1].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[1].Total, new { #maxlength = "2"})
This renders the form fine, but clicking the submit button doesn't do anything in FF.
However it posts fine in IE9.
View source shows this html generated like this:
<input id="Tasks_0__Title" maxlength="50" name="Tasks[0].Title" type="text" value="" />
<input data-val="true" data-val-number="The field Total must be a number." data-val-required="The Total field is required." id="Tasks_0__Total" maxlength="2" name="Tasks[0].Total" type="text" value="" />
This HTML doesn't look right. It has name="Tasks[0].Total" which seems odd.
How should I do this so that I can access the text box values from List<> in my controller after post?
Thanks
EDIT:
I just kept one row for test. This is the html I see in FF.
<input id="Tasks_0__Title" type="text" value="" name="Tasks[0].Title" maxlength="50">
<input id="Tasks_0__Total" type="text" value="" name="Tasks[0].Total" maxlength="2" data-val-required="The Total field is required." data-val-number="The field Total must be a number." data-val="true">
This doesn't post when I click the submit button.
Now if I change name="Tasks[0].Title" to name="Tasks_0__Title" and name="Tasks_0__Total"
in FIREBUG it posts fine.
If I delete name completely it also posts fine in FF
You should use Tasks[0].Total and Tasks[1].Total instead of Items:
#Html.TextBoxFor(m => m.Tasks[0].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[0].Total, new { #maxlength = "2"})
<br/>
#Html.TextBoxFor(m => m.Tasks[1].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[1].Total, new { #maxlength = "2"})
name="Tasks[0].Total" is not odd. That's exactly how the input should be named in order for the model binder to fetch the value back in the POST action. See this blog post for the wire format used by the default model binder when dealing with lists and dictionaries.
This being said I would recommend you using editor templates => instead of writing those 5 lines of code in your view replace them with:
#Html.EditorFor(x => x.Tasks)
and then inside the corresponding editor template (~/View/Shared/EditorTemplates/Task.cshtml) which will be automatically rendered for each element in the Tasks collection:
#model Task
#Html.TextBoxFor(m => m.Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Total, new { #maxlength = "2"})
<br/>
Now you can leave the editor templates worry about proper naming convention, etc...
As far as your POST action is concerned:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// model.Tasks should be correctly bound here
...
}
In case you want to have multiple elements with textboxes.
for (int i = 0; i < Model.Students.Count; i++)
{
#Html.HiddenFor(modelIem => Model.Students[i].StudentId)
<tr>
<td>
#Html.DisplayFor(modelItem => Model.Students[i].Student.FirstNames)
</td>
<td>
#Html.DisplayFor(modelItem => Model.Students[i].Student.LastNames)
</td>
<td>
#Html.TextBoxFor(modelItem => Model.Students[i].Score, new { #type = "number" })
</td>
</tr> }
This is the ViewModel
public class MyModel
{
public List<StudentGrade> Students { get; set; }
}
public class StudentGrade {
public ApplicationUser Student { get; set; }
[Range(1, 100)]
public int? Score { get; set; } = null;
public string Description { get; set; } = null;
public string StudentId { get; set; }
}
At the End it will look like this.

Resources