Form validation - Field X must be a number - validation

I'm having a problem with the forms validation of asp.net core when using a decimal field in my viewmodel, where the input's value receives a number with a comma but upon the submit it doesn't permit it..
ViewModel:
public class MyViewModel
{
public decimal Price { get; set; }
}
Razor page:
<div class="form-group row">
<label asp-for="Price" req-asterisk="true" class="col-md-3 col-lg-2 col-form-label"></label>
<div class="col-md-9 col-lg-10">
<input asp-for="Price" class="form-control" rows="4" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
</div>
So, if for example the Price property takes 4000, the input takes 4000,00 and if I click submit it says "The field Price must be a number."

Considering your Price field needs to contain the value, you will need to use a string to access your value. Here is a sample of what you can do with a string property with some memory saving options:
public class MyViewModel
{
private string _priceDisplay;
[DataType(DataType.Currency)]
public string PriceDisplay {
get => _priceDisplay ?? _price.ToString();
set => _priceDisplay = value;
}
private decimal? _price;
public decimal Price
{
get => _price ?? decimal.Parse(PriceDisplay);
set => _price = value;
}
}
You can now map your input to PriceDisplay

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"))

Displaying the appropriate fields for the selected unit in the data record form in ASP.NET Core MVC application

Scenario: there are units to request a job, and the fields to be filled in the job request registration form differ according to these units. What method should be followed to realize this scenario?
I only have one registration form. Fields in the registration form
Unit to be requested,....etc Among the units to be requested by the user, for example Technical Services, Biomedical... etc. units can be selected.If the user chooses technical service in the registration form, they should see different fields to fill in. If he chooses biomedical, they should see different fields.
registration form design,
registration form cshtml
Correction in the Question
We have an application where employees in an enterprise can report problems such as electrical failure, wall painting, computer hardware failure to the technical units and these problems can be monitored by the technical units. The fields to be filled while forwarding the question to the relevant department differ according to the department to which the question will be forwarded.
For example: When a problem is reported to the computer technical service, the requested information is different.
If a problem is to be reported to the biomedical unit, different information must be entered.
For this reason, different forms were asked to be designed and the user was asked to fill in the relevant information from these forms after choosing the support unit regarding the problem to be reported.
I may have explained it wrong to Rena while explaining my question. But as a result, rena actually posted some code so I can call other views from a single view. I'm trying to implement what Rena did.
Here is a simple demo:
Model:
public class job
{
public int Id { get; set; }
public string JobName { get; set; }
}
public class RegisterForm
{
public string JobName { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
View:
Index.cshtml(located in /Views/Home/Index.cshtml):
model RegisterForm
<form>
<div class="form-group">
<label asp-for="JobName" class="control-label"></label>
<select asp-for="JobName" asp-items="ViewBag.Jobs" class="form-control">
<option>Choose A Job</option>
</select>
<span asp-validation-for="JobName" class="text-danger"></span>
</div>
<div id="result"></div>
</form>
#section Scripts
{
<script>
$('#JobName').on('change', function () {
$.ajax({
url: '#Url.Action("ReturnPartial", "Home")',
type: 'GET',
data: { JobName: $("#JobName").val() },
success: function (res) {
$("#result").html(res); //add this...
},
error: function () {
}
});
});
</script>
}
PartialView:
Biomedical.cshtml(located in /Views/Home/Biomedical.cshtml):
#model RegisterForm
<h1>Biomedical RegisterForm</h1>
<div class="form-group">
<label asp-for="Age" class="control-label"></label>
<input asp-for="Age" class="form-control" />
<span asp-validation-for="Age" class="text-danger"></span>
</div>
Technical.cshtml(located in /Views/Home/Technical.cshtml):
#model RegisterForm
<h1>Technical RegisterForm</h1>
<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>
Controller:
public class HomeController : Controller
{
public IActionResult Index()
{
var jobs = new List<job>()
{
new job(){Id=1,JobName="Technical"},
new job(){Id=2,JobName="Biomedical"}
};
ViewBag.Jobs = new SelectList(jobs, "JobName", "JobName");
return View();
}
public IActionResult ReturnPartial(string JobName)
{
return PartialView(JobName);
}
}
Result:

How do i put validation on checkbox with MVC3 razor?

I have created the register page on mvc3 razor. I want to put the validation on user notification field. Below is my code.
[Required]
[Display(Name = "Student Notification ?")]
[Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]
public Boolean UserNotification { get; set; }
Below is my register page view
<div class="editor-label">
#Html.LabelFor(model => model.UserNotification)
</div>
<div class="editor-label">
#Html.CheckBoxFor(model =>model.UserNotification)
#Html.ValidationMessageFor(model => model.UserNotification)
</div>
<p>
<input type="submit" value="Register" />
</p>
So when i will click the button, there should be validation message there ..
You need to change your datatype of the property UserNotification. Change:
public Boolean UserNotification { get; set; }
To:
public bool UserNotification { get; set; }
There is a lot difference between Boolean and bool.

MVC3: Portions of Model Not Reconstituted on Postback

Portions of my models are not being correctly reconstructed on postback.
Models
public class DemographicsModel
{
public List<QuestionModel> Questions { get; set; }
}
public abstract class QuestionModel
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public string Title { get; set; }
}
public abstract class ChooseQuestionModel : QuestionModel
{
public abstract List<SelectListItem> Items { get; set; }
}
public class ChooseManyQuestionModel : ChooseQuestionModel
{
[Required]
[DataType("CheckBoxList")]
public override List<SelectListItem> Items { get; set; }
}
Views
ChooseManyQuestionModel.cshtml
#model X.Y.Z.ChooseManyQuestionModel
<div class="Form Wide NoLabel">
<div class="Title">#this.Model.Title</div>
#Html.TypeStamp()
#Html.EditorFor(m => m.ID)
#Html.EditorFor(m => m.Title)
#Html.EditorFor(m => m.Items)
</div>
CheckBoxList.cshtml
#model IEnumerable<SelectListItem>
#if (!this.Model.IsNullOrEmpty())
{
foreach (var item in this.Model)
{
<div>
#Html.HiddenFor(m => item.Value)
#Html.HiddenFor(m => item.Text)
#Html.CheckBoxFor(m => item.Selected)
#Html.LabelFor(m => item.Selected, item.Text)
</div>
}
}
I believe the issue lies within CheckBoxList.cshtml since these items are not being re-constituted on postback.
HTML Output
<div class="Form Wide NoLabel">
<div class="Title">Question title displays here?</div>
<input id="Questions_1___xTypeStampx_" name="Questions[1]._xTypeStampx_" type="hidden" value="Hrxh2HjDRorBAZWo18hsC0OvbJwyswpDkfTBfNF2NC8=" />
<input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="Questions_1__ID" name="Questions[1].ID" type="hidden" value="76" />
<input id="Questions_1__Title" name="Questions[1].Title" type="hidden" value="Question title displays here?" />
<div>
<input id="Questions_1__Items_item_Value" name="Questions[1].Items.item.Value" type="hidden" value="148" />
<input id="Questions_1__Items_item_Text" name="Questions[1].Items.item.Text" type="hidden" value="Organization Type 1" />
<input data-val="true" data-val-required="The Selected field is required." id="Questions_1__Items_item_Selected" name="Questions[1].Items.item.Selected" type="checkbox" value="true" /><input name="Questions[1].Items.item.Selected" type="hidden" value="false" />
<label for="Questions_1__Items_item_Selected">Organization Type 1</label>
</div>
</div>
</div>
Controller
public class AccountController : BaseController
{
public ActionResult Demographics()
{
return this.View(new DemographicsModel());
}
[HttpPost]
public ActionResult Demographics(DemographicsModel model)
{
return this.View(model);
}
}
On postback, the DemographicsModel is populated with the correct types (I'm using MvcContrib to handle abstract type binding). The List<Question> is populated with all of the correct data including the ID and Title of each question from the hidden fields. However, List<SelectListItem> within each question is set to null.
Update 1
The issue is definitely occurring because the fields are not named correctly. For instance, the "item" field names are being generated like this:
Questions_1__Items_item_Value
When they should really look like this (addition of item index and removal of erroneous "item"):
Questions_1__Items_1__Value
Similarly, the field IDs are being generated like this (addition of item index and removal of erroneous "item"):
Questions[1].Items.item.Value
Instead of:
Questions[1].Items[0].Value
Using Fiddler with the correct IDs being posted back, the model is constructed correctly with all radio buttons and checkboxes in place.
Try the following.
In ChooseManyQuestionModel.cshtml, change #Html.EditorFor(m => m.Items) to:
#Html.EditorForModel(m => m.Items)
Then, in CheckBoxList.cshtml, change #model IEnumerable<SelectListItem> to:
#model SelectListItem
Finally, in each item, modify each lambda expression, and change item to m, then remove the foreeach loop. This will allow the Editor to iterate through the collection, and should give you correct id generation for each element.
When foreach loop is used the ids generated in HTML are all same.
When for look is used the ids generated with the for loops index so binding is happening correctly and all the data is available after post back.
In this scenario, it seems the Helper class is not doing what you want it to do. I would suggest writing your own helper class to name your inputs exactly as you require them to be.

Resources