Why passing a form control to a ViewModel is not working in ASP.NET Core 2.0? - asp.net-core-mvc

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

Related

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:

Upload file with Boostrap Modal on Asp.net core 2.1

I want to upload a file and send to email. But I don't see how to do this with Bootsrap Modal.
If you want any other information, tell me.
I would like to send the attached file by email then also integrate it in my database and be able to download it.
/[...]/ ==> Code that I didn't add, not useful.
Edit : Otherwise how to do with Ajax ?
My View :
<form name="contact" method="post" asp-action="Validation">
#if (ViewBag.Alert != null && ViewBag.Alert != "")
{
<div class="alert role="alert">
#ViewBag.Alert
</div>
}
<div class="details">
<h3 class="title">Demande</h3>
<label for="card">Type *</label>
<select required onchange="contact()" class=" form-control" disabled="true" name="type" value="#ViewBag.Form.Type">
<option value="1">Choose 1</option>
<option value="2">Choose 2</option>
</select>
<label for="card">Origine *</label>
<select required class="form-control" name="origine" value="#ViewBag.Form.Origine">
<option value="1">Origine 1</option>
<option value="2">Origine 2</option>
<option value="3">Origine 3</option>
</select>
/*[...]*/
<input type="hidden" name="Id" value="#ViewBag.Form.Id" />
<input type="hidden" name="Price" value="#ViewBag.Form.Price" />
/*[...]*/
<textarea class="form-control" name="Response" placeholder="Response..."></textarea>
<button required class="btn" onclick="modal_response()" data-whatever="getbootstrap" data-toggle="modal" data-target="#modal_contact" type="button">Response</button>
<label for="card" class="right">Response send</label><br />
<button required class="btn_second" onclick="modal_save()" data-whatever="getbootstrap" data-toggle="modal" data-target="#modal_contact_save" type="button">Save</button>
<label for="card-holder" class="right">Save Ok!</label>
/*[...]*/
<div class="form-row">
<button class="btn-primary" onclick="form_confirmation()" type="submit">Save All</button>
<button class="btn-secondary" type="reset" onclick="location.href = '#Url.Action("List", "Control", new { id = ViewBag.Id })';">Close</button>
</div>
</div>
</form>
/* When I click on "Response" a new window open */
<div class="modal fade" id="modal_contact" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<h5 class="modal-title" id="modal_label">Send email to : #ViewBag.Form.Name</h5>
<button type="button" class="close" data-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="modal_contact_form" enctype = "multipart/form-data" method="post" action="#Url.Action("Modal_contact", "Control")">
<div class="form-group">
<input type="file" name="file" accept="application/pdf" /><br /><br />
<textarea class="form-control" name="Description" required placeholder="Content">#ViewBag.Response</textarea>
</div>
<input type="hidden" name="Id" value="ID" hidden />
<input type="hidden" name="Price" value="#ViewBag.Form.Price" hidden />
<input type="hidden" name="Type" value="#ViewBag.Form.Type" hidden />
/*[...]*/
<input type="submit" hidden />
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" data-dismiss="modal" onclick="$('#modal_contact_form').submit();" class="btn-primary">Send Email</button>
</div>
</div>
My Controller :
public class MyController : BaseEntity
{
[Required]
public string Id { get; set; }
public string Price { get; set; }
public string Type { get; set; }
/*[...]*/
/* For File I don't know if it's good?? */
public IformFile File { get; set; }
}
My Model :
public ActionResult Modal_contact(MyController model)
{
/* How to retrieve file ?? */
bool ok = false;
OtherController OC = new OtherController();
/*[...]*/
if (ModelState.IsValid)
{
OC = othercontrollerRepository.Find(model.Id);
OC.Email = this.Session_get("Email");
OC.Description = "";
model.Email = this.Session_get("Email");
model.Status = "1";
model.View = "response";
/*[...]*/
if (mycontrollerRepository.Add(model) && othercontrollerRepository.Update(OC))
{
/* How to send file to email and save file to folder in my project to add in my database ? */
MailManager mail = new MailManager();
Modele modele = new Modele();
modele= modeleRepository.Find("response");
ok = mail.send(modele.Objet, model.Description, OC.Email);
}
return Json(new { Json_result = ok, Redirect = "" });
}
return Json(new { Json_result = false, Redirect = "" });
}
I found my error :
Form before <div>
I tried your code and reproduced your issue. I noticed that you named the IFormFile object as File.
public IFormFile File { get; set; }
But input file you are getting is named file.
<input type="file" name="file" accept="application/pdf"/><br /><br />
Asp.net core model binding will bind the property according to the input name. This is the reason why you couldn't get the IFormFile value in controller.
To solve this issue, you should change the name of the input to File so that they could match each other.
Like below:
<input type="file" name="File" accept="application/pdf"/><br /><br />
Result:

Server side validation problem - float value

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;

Asp-validation-summary="ModelOnly" does not work for compare data-validator

View Does displays Span validation but Validation summary(blank validation-summary div also not present).if i change Asp-validation-summary="All".I am not getting why it is not working with ModelOnly.
My Class
public class RegistrationViewModel
{
[Required]
[EmailAddress]
[MinLength(5)]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[DataType(DataType.Password)]
[Compare("Password")]
[Display(Name = "Confirm Password")]
public string VerifiedPassword { get; set; }
}
view
<form asp-action="Registration">
<div asp-validation-summary="ModelOnly" val class="text-danger"></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="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" required />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="VerifiedPassword" class="control-label"></label>
<input asp-for="VerifiedPassword" class="form-control" required/>
#*<span asp-validation-for="VerifiedPassword" class="text-danger"></span>*#
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-default" />
</div>
</form>
Add this to post method in the controller:
if (!ModelState.IsValid)
{
AddErrorsFromModel(ModelState.Values);
return View();
}
Also add using and AddErrorsFromModel to controller:
using Microsoft.AspNetCore.Mvc.ModelBinding;
private void AddErrorsFromModel(ModelStateDictionary.ValueEnumerable values)
{
foreach (ModelStateEntry modelState in values)
foreach (ModelError error in modelState.Errors)
{
ModelState.AddModelError(string.Empty, error.ErrorMessage.ToString());
}
}

Spring4 + Thymeleaf3 Form Validation : bean name #fields not available in templates

I get the below error in my spring4 + thymeleaf3 application when I try to show validation errors in my form template.
Neither BindingResult nor plain target object for bean name '#fields' available as request attribute
My form is as below.
<form th:action="#{/user/save}" method="post" th:object="${user}">
<ul th:if="${#fields.hasErrors()}">
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>
<div>
<label>Name</label>
<div>
<input type="text" th:field="*{firstName}" placeholder="First Name">
<input type="text" th:field="*{lastName}" placeholder="Last Name">
<div th:if="${#fields.hasErrors('firstName')}" th:errors="${#fields.errors('firstName')}"></div>
<div th:if="${#fields.hasErrors('lastName')}" th:errors="${#fields.errors('lastName')}"></div>
</div>
</div>...
The form is rendered well for the following get request mapping.
#GetMapping("/create")
public String create(ModelMap model) {
model.put("user", new User());
return VIEW_DIR.concat("form");
}
But it gives the above error when the form is submitted with some invalid fields to the following method.
#PostMapping("/save")
public String save(#Valid User user, BindingResult bindingResult, ModelMap model) {
if(bindingResult.hasErrors()) {
return VIEW_DIR.concat("form");
}
userService.save(user);
return "redirect:list";
}
Can you please show me where the error is.
You are setting wrong values for th:errors inside your form element div. th:errors should contain field name. Update your form with this:
<div>
<input type="text" th:field="*{firstName}" placeholder="First Name">
<input type="text" th:field="*{lastName}" placeholder="Last Name">
<div th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"></div>
<div th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"></div>
</div>

Resources