Server side validation problem - float value - validation

I have a problem with data validation in my application.
Im using a Razor Pages and .Net Core 3.0 framework with EF Core orm.
In my model I have two properties:
public float WireCrosssection { get; set; }
public float CableLength { get; set; }
On page, I have inputs for them:
<div class="form-group">
<label asp-for="Cable.WireCrosssection"></label>
<input class="form-control" asp-for="Cable.WireCrosssection" />
<span class="text-danger" asp-validation-for="Cable.WireCrosssection"></span>
</div>
<div class="form-group">
<label asp-for="Cable.CableLength"></label>
<input class="form-control" asp-for="Cable.CableLength" />
<span class="text-danger" asp-validation-for="Cable.CableLength"></span>
</div>
Client side validation is turned on and this validation doesn't report problems with the form but the server side one do (ModelState.IsValid is false).
The number is provided with dot (".").
Any suggestions?

Be sure you have added the validation script in your view.
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
Here is a simple demo like below:
1.Model:
public class Test
{
public float WireCrosssection { get; set; }
public float CableLength { get; set; }
}
2.View:
#model Test
<form asp-action="Index">
<div class="form-group">
<label asp-for="WireCrosssection"></label>
<input class="form-control" asp-for="WireCrosssection" />
<span class="text-danger" asp-validation-for="WireCrosssection"></span>
</div>
<div class="form-group">
<label asp-for="CableLength"></label>
<input class="form-control" asp-for="CableLength" />
<span class="text-danger" asp-validation-for="CableLength"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
3.Controller:
[HttpPost]
public IActionResult Index(Test test)
{
//..
}
4.Result:
Reference:Client Side Validation

Ok, I solved the issue. The problem was a mismatch in culture on the server side and on the client side. Client side validation is written in "en-US" culture. To set the culture on the ASP.NET Core application you need to add following code to the Configure method in the Startup.cs class:
var cultureInfo = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;

Related

Why does ASP.NET Core 7 MVC ModelValidator always set ModelState.IsValid to true when the Model is invalid

I'm converting an ASP.NET 4.8 MVC web site to ASP.NET Core 7 MVC. I'm running into a problem with ModelValidation where my ModelState is always valid, even when it is not. I'm using the pattern that was used in .Net 4x and worked fine. Microsoft Learn link
Controller
[HttpPost]
public async Task<IActionResult> Create(TcPhoneType tcPhoneType)
{
if (ModelState.IsValid)
{
await _phoneTypeService.CreatePhoneType(tcPhoneType);
return RedirectToAction("Index");
}
return View(tcPhoneType);
}
Model.cs
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace App.Models;
public partial class TcPhoneType
{
public string PhoneTypeId { get; set; }
public string PhoneTypeDescription { get; set; }
public virtual ICollection<MemberPhone> MemberPhones { get; } = new List<MemberPhone>();
}
ModelMetadata.cs
using System.ComponentModel.DataAnnotations;
namespace App.Models;
[MetadataType(typeof(TcPhoneTypeMetadata))]
public partial class TcPhoneType
{
}
public class TcPhoneTypeMetadata
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Item is required")]
[StringLength(1)]
public string? PhoneTypeId { get; set; }
[Required(AllowEmptyStrings = false)]
[StringLength(5)]
public string? PhoneTypeDescription { get; set; }
}
Create.cshtml
#model SeasonCourt7.Models.TcPhoneType
#{
ViewBag.Title = "Add Phone Type Code";
}
<h2>#(ViewBag.Title)</h2>
<br>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row mb-3">
<div class="col-sm-2">
<label asp-for="PhoneTypeId" class="col-form-label">Code</label>
</div>
<div class="col-sm-10">
<input asp-for="PhoneTypeId" class="form-control" autofocus />
</div>
<span asp-validation-for="PhoneTypeId" class="text-danger"></span>
</div>
<div class="row mb-3">
<div class="col-sm-2">
<label asp-for="PhoneTypeDescription" class="col-form-label">Description</label>
</div>
<div class="col-sm-10">
<input asp-for="PhoneTypeDescription" class="form-control" />
</div>
<span asp-validation-for="PhoneTypeDescription" class="text-danger"></span>
</div>
<div class="row mb-3">
<div class="offset-sm-2 col-sm-10">
<input type="submit" value="Create" class="btn btn-primary" />
Back
</div>
</div>
</div>
}
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
I set a breakpoint on the if (ModelState.IsValid) line so I could inspect the model and ModelState. I can see the data that is bound to the model is as expected and the ModelState is always valid.
If I pass null data in the model, I should see the ModelState.IsValid = false, but it's always true.
The model.cs is generated by EF and could be regenerated in the future which makes it impractical to add the metadata attributes for validation, as the file could be regenerated in the future.
The only way I've been able to get the ModelValidation to work as expected, is to decorate the model directly with the validation attributes.

How to update value progress bar bootstrap from controller in ASP .NET Core

I have a table that receive email of my users for newsletter .I Show it on my dashboard with count .but now I want to show on progress bar and its percentage per last month
how do I do ? I create another view model for show count of some things
I can show count of them but I need show on progress bar too.
my viewmodel:
public class NewsLetterViewModel
{
public string Phone { get; set; }
public string Email { get; set; }
public DateTime CreateDate { get; set; }
}
You can try to use ViewBag to pass count and percentage.Here is a demo:
Action:
public IActionResult News()
{
ViewBag.Count = 0;
ViewBag.Percentage = 0;
return View();
}
[HttpPost]
public IActionResult News(NewsLetterViewModel n,int Count)
{
//you can pass the count and percentage with ViewBag here
ViewBag.Count= Count+ 1;
ViewBag.Percentage=25;
return View();
}
View:
<div>
Count:#ViewBag.Count
</div>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: #ViewBag.Percentage%;" aria-valuenow="#ViewBag.Percentage" aria-valuemin="0" aria-valuemax="100">#ViewBag.Percentage%</div>
</div>
<form method="post">
<input hidden name="Count" value="#ViewBag.Count" />
<div class="form-group">
<label asp-for="Phone" class="control-label"></label>
<input asp-for="Phone" class="form-control" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreateDate" class="control-label"></label>
<input asp-for="CreateDate" class="form-control" />
<span asp-validation-for="CreateDate" class="text-danger"></span>
</div>
<input type="submit"value="submit" />
</form>
result:

Input field keeps switching between text and date in ASP.NET Core MVC application

I'm having a problem with an input field switching between type=text (when it's not focused) and type=date (when it's focused). I'm using .NET Core 3.1 framework.
Model:
public class DocumentVersionModel
{
public int Id { get; set; }
public int DocumentModelId { get; set; }
[DataType(DataType.Date, ErrorMessage="Date only")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime CreationDate { get; set; }
[DataType(DataType.Date, ErrorMessage="Date only")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime LastChangeDate { get; set; }
[DataType(DataType.Date, ErrorMessage="Date only")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DocumentDate { get; set; }
public string Content { get; set; }
public int Version { get; set; }
public DocumentModel Document { get; set; }
}
The format yyyy-MM-dd is needed for the database and is the one displayed when the input field isn't focused. The format I need to display the dates is mm/dd/yyyy, but I can't change the DateFormatString (and removing ApplyFormatInEditMode = true didn't work).
This is the form in the RazorPage:
<form asp-action="Edit">
<br />
<div class="row justify-content-center">
<div class="col-12 form-group">
<label>Titolo</label>
<input asp-for="Title" class="form-control" />
</div>
<div class="col">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" id="iddoc" />
<div class="form-group">
<label>Data Documento</label>
<input asp-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).DocumentDate" class="form-control" />
<span asp-validation-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).DocumentDate" class="text-danger"></span>
</div>
<div class="form-group">
<label>Data Creazione</label>
<input asp-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).CreationDate" class="form-control" readonly="readonly" />
</div>
<div class="form-group">
<label>Data Ultima Modifica</label>
<input asp-for="DocumentVersion.FirstOrDefault(q => q.Version==Model.ActiveVersion).LastChangeDate" class="form-control" readonly="readonly" />
</div>
</div>
</form>
A little note about the form: if I use type="date" or "datetime" inside the input tag, the type keeps changing between text and date, but if I use "datetime-local" is shown correctly (but it displays the time too which I don't need).
This is the GIF of what happens:
https://imgur.com/YXKVAMO
This is the very bad and inefficient jQuery solution I used to "fix" the problem:
$('#DocumentDate, #LastChangeDate, #CreationDate').attr('type','date'); //when the page loads
$(document).click(function () {
if ($('#DocumentDate').not(':focus') || $('#LastChangeDate').not(':focus') || $('#CreationDate').not(':focus'))
$('#DocumentDate, #LastChangeDate, #CreationDate').attr('type','date');
});
So what is your current problem? Do you not want to use jquery to
achieve it?
I think the method you are using by jquery is implemented very well because you cannot change the date type input control to display the date format you want: link.
Another solution is to use Datepicker plugin for jquery UI, but this cannot avoid using jquery.
Here is the example based jquery UI Datepicker :
#model WebApplication_core_mvc.Models.DocumentVersionModel
#{
ViewData["Title"] = "DateEdit";
}
<h1>DateEdit</h1>
#section Scripts{
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(function () { // will trigger when the document is ready
$('.datepicker').datepicker();
$('.datepicker').datepicker("option", "dateFormat", "mm/dd/yy"); //Initialise any date pickers
});
</script>
}
<form asp-action="Edit">
<br />
<div class="row justify-content-center">
<div class="col-12 form-group">
<label>Titolo</label>
<input asp-for="DocumentModelId" class="form-control" />
</div>
<div class="col">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" id="iddoc" />
<div class="form-group">
<label>Data Documento</label>
#Html.TextBoxFor(model => model.CreationDate, new { #class = "form-control datepicker" })
<span asp-validation-for="CreationDate" class="text-danger"></span>
</div>
<div class="form-group">
<label>Data Creazione</label>
#Html.TextBoxFor(model => model.LastChangeDate, new { #class = "form-control datepicker" })
</div>
<div class="form-group">
<label>Data Ultima Modifica</label>
#Html.TextBoxFor(model => model.DocumentDate, new { #class = "form-control datepicker" })
</div>
</div>
</div>
</form>
Here is the result:

Why passing a form control to a ViewModel is not working in ASP.NET Core 2.0?

I built a simple ASP.NET Core 2.0 application and I have a simple login view. I am using ASP.NET Core Identity which means I am using "IdentityUser" class
#model LoginViewModel
<h2 class="my-4 text-center text-lg-left">Before contacting us, please log in or
<a asp-action="Register" asp-controller="Account"> register!</a></h2>
<form asp-action="Login" asp-controller="Account" method="post" role="form">
<!-- To show form error after submission -->
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserName">User Name</label>
<input type="text" class="form-control" placeholder="Username">
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password">Password</label>
<input type="password" class="form-control" placeholder="Password">
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Log In!</button>
</form>
As you can see, I created a view model called "LoginViewModel" which is going to detect the passed parameters in the submitted form
LoginViewModel
public class LoginViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
}
When a user hits "Login" on the form, my ASP.NET tag helpers indicates that the post request is going to hit the "Login" action within the "Account" controller
Here is my "Login" Action
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
public AccountController(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager)
{
_signInManager = signInManager;
_userManager = userManager;
}
[HttpGet]
public IActionResult Login()
{
return View(new LoginViewModel());
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel loginViewModel)
{
if (!ModelState.IsValid)
{
return View(loginViewModel);
}
var user = await _userManager.FindByNameAsync(loginViewModel.UserName);
if (user != null)
{
var result = await _signInManager.PasswordSignInAsync(user, loginViewModel.Password, false, false);
if (result.Succeeded)
{
return RedirectToAction("Index", "Home");
}
}
ModelState.AddModelError("", "User name/password not found");
return View(loginViewModel);
}
The object "loginViewModel" that I am passing to the "Login" action is null, this means that it is not grabbing the form data on the time the user submits the form.
Any idea what I am missing here, pretty sure it is something stupid?
You're missing the tag helpers on the inputs!!
<div class="form-group">
<label asp-for="UserName">User Name</label>
<input type="text" class="form-control" placeholder="Username"
asp-for="UserName" /> <!-- You're missing this -->
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
My 2 cents:
You don't need to indicate the method="post". That's the default method. Also you might want to specify the area as well, just in case you have multiple controllers with the same name but live in different areas.
<form asp-area="" asp-controller="account" asp-action="login">
....
</form>
When you use tag helper on labels, you don't need to put the text in between. The tag helper will generate that based on your [Display] annotation.
<label asp-for="UserName"></label>
You can use HTML helpers to generate the placeholder text from [Display] as well if you want. That way you don't need to hard code the text on the view.
<div class="form-group">
<label asp-for="UserName"></label>
<input type="text" class="form-control" asp-for="UserName"
placeholder="#Html.DisplayNameFor(x => x.UserName)" />
</div>
You are generating manual html for you inputs and they do not have a name attribute so they do not post a value when the form is submitted.
Change your inputs to use the TagHelpers, which will generate the correct html, including the name attribute and the data-val-* attributes for client side validation
// <input type="text" class="form-control" placeholder="Username">
<input asp-for="UserName" class="form-control" placeholder="Username">
// <input type="password" class="form-control" placeholder="Password">
<input asp-for="Password" class="form-control" placeholder="Username">

Display Friendly Error Message When using Decorators in Models

New to ASP.NET Core.
I have a view:
<form asp-action="LogIn">
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="EmailAddress" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="EmailAddress" class="form-control" style="min-width:350px" />
<span asp-validation-for="EmailAddress" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Password" class="form-control" style="min-width:350px" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Enter" class="btn btn-default" />
</div>
</div>
</div>
</form>
I have this Model for it:
public class Subscription
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long SubscriptionId { get; set; }
[Display(Name = "Email Address")]
[Required]
[RegularExpression(#"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*")]
public string EmailAddress { get; set; }
[NotMapped]
public string Password { get; set; }
}
So, when a User types in an email address that is not validated by the regular expression I get the error message:
How do i over-ride this 'default' error message to say (for example):
Email Address is Invalid
?
You need to add ErrorMessage property to the RegularExpression attribute like this-
[RegularExpression(#"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessage = "Your friendly message goes here")]

Resources