I want to send the form via AJAX and do its validation via fluentValidation in mvc core. Please guide me about this
Here is a working demo , you could refer to :
Add a reference to the FluentValidation.AspNetCore assembly by installing the appropriate NuGet package:
Install-Package FluentValidation.AspNetCore
Configure FluentValidation in your app’s Startup class by calling the AddFluentValidation extension method inside the ConfigureServices method , and use the AddFromAssemblyContaining method to automatically register all validators within a particular assembly
services.AddMvc()
.AddFluentValidation(fv => {
fv.RegisterValidatorsFromAssemblyContaining<PersonValidator>();
//If you want to disable this behaviour so that FluentValidation is the only validation library that executes,
//you can set the RunDefaultMvcValidationAfterFluentValidationExecutes to false in your application startup routine
fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
Assume that the PersonValidator is defined to validate a class called Person
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(x => x.Id).NotNull();
RuleFor(x => x.Name).Length(0, 10);
RuleFor(x => x.Email).EmailAddress();
RuleFor(x => x.Age).InclusiveBetween(18, 60);
}
}
Controller
[HttpPost]
public async Task<IActionResult> CreatPerson(Person person)
{
if (!ModelState.IsValid)
{ // re-render the view when validation failed.
return BadRequest();
}
//Save the person to the database, or some other logic
_context.Add(person);
await _context.SaveChangesAsync();
var message = "Person successfully created";
return Ok(message);
}
View and related jQuery
#model TestExample.Models.Person
<hr />
<div class="row">
<div class="col-md-4">
<form id="formdata">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<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>
<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="Age" class="control-label"></label>
<input asp-for="Age" class="form-control" />
<span asp-validation-for="Age" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$("#formdata").submit(function (e) {
e.preventDefault();
var form = $(this);
$.ajax(
{
type: "post",
url: "/people/CreatPerson",
data: form.serialize(),
success: function (data) { alert(data); },
error: function (data) { alert(data); }
});
});
</script>
}
For more details on FluentValidation , you could refer to https://fluentvalidation.net/aspnet
Related
Here is the model:
public class UserModel()
{
public string UserName{get;set;}
public string UserPassword{get;set;}
public string UserSource{get;set;}
}
Here is the front-end:
#model Sample.Models.UserModel
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form asp-controller="Login" asp-action="LoginIn.html" method="post">
<div>
<h3>Name</h3>
<span asp-validation-for="UserName"></span>
</div>
<input asp-for="UserName" placeholder="Your username" />
<div>
<h3>Password</h3>
<span asp-validation-for="UserPassword"></span>
</div>
<input asp-for="UserPassword" placeholder="Your password" type="tel" />
<button type="submit">Login in</button>
</form>
The front-end is a Partial View. It is referenced like this:
#await Html.PartialAsync("/Views/Contact/LoginIn.cshtml", new Sample.Models.UserModel() { UserSource = "From website" })
And here is the controller:
[HttpPost]
[ValidateAntiForgeryToken]
[Route("LoginIn.html")]
public IActionResult LoginIn(Sample.Models.UserModel)
{
if (ModelState.IsValid)
{
}
return Redirect();
}
Well, when the form submitted, the controller only can get the UserName/UserPassword but can't get the UserSource unless I add a
<input asp-for="UserSource" />
into front-end.
I don't want to show the UserSource to the user in the front-end(even hide it by CSS).
How can I solve this problem? Thank you.
You can use ajax to post form,here is a demo:
TestFormAction:
public IActionResult TestForm() {
UserModel u= new UserModel { UserSource = "111" };
return View(u);
}
LoginController:
public IActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
[Route("Login/LoginIn.html")]
public IActionResult LoginIn(UserModel u)
{
if (ModelState.IsValid)
{
}
return Ok();
}
TestForm.cshtml:
<form id="myForm" method="post">
<div>
<h3>Name</h3>
<span asp-validation-for="UserName"></span>
</div>
<input asp-for="UserName" placeholder="Your username" />
<div>
<h3>Password</h3>
<span asp-validation-for="UserPassword"></span>
</div>
<input asp-for="UserPassword" placeholder="Your password" type="tel" />
<button type="submit">Login in</button>
</form>
#section scripts{
<script>
$('#myForm').submit(function (event) {
event.preventDefault(); // avoid to execute actual form submit.
var model = {};
model.UserName = $("#UserName").val();
model.UserPassword = $("#UserPassword").val();
model.UserSource =#Model.UserSource;
var token = $('input[name="__RequestVerificationToken"]').val();
$.ajax({
url: '#Url.Action("LoginIn.html", "Login")',
type: 'POST',
data: model,
headers: { "RequestVerificationToken": token },
success: function (data) {
window.location.href = "/Login/Index";
}
});
});
</script>
}
result:
Child model binding is not working properly on Ajax post call in .Net core controller method
I am using below model -
public class UserViewModel
{
public UserViewModel()
{
UserAttribute = new CAMPv2.Models.AutoPoco.UserAttribute();
}
public UserAttribute UserAttribute { get; set; }
}
public class UserAttribute
{
[JsonPropertyName("FirstName")]
public string FirstName { get; set; }
}
Below is the Ajax call -
#using User.Models
#model UserViewModel
#{
ViewData["Title"] = "UserDetails";
}
<form class="kt-form" id="kt_form">
<div class="kt-wizard-v3__content" data-ktwizard-type="step-content" data-ktwizard-state="current">
<div class="kt-form__section kt-form__section--first">
<div class="form-group row required">
<label for="FirstName" class="col-md-3 col-form-label k-font-bold text-md-right control-label">First Name</label>
<div class="col-6">
<input type="text" class="form-control" name="FirstName" placeholder="" required asp-for="UserAttribute.FirstName">
</div>
</div>
</div>
<div class="btn btn-brand btn-md btn-tall btn-wide btn-bold btn-upper" data-ktwizard-type="action-submit" id="btnSubmit">Submit</div>
</div>
</form>
<script>
$("#btnSubmit").click(function () {
var formData = $("#kt_form").serialize();
alert(formData);
$.ajax({
url: "/User/CreateUser/",
data: formData,
type: "POST",
dataType: "json",
success: function (result) {
if (result.success) {
alert('data submitted successfully');
}
},
error: function (result) {
alert('failed to submit data');
},
});
});
</script>
Below is action method -
[HttpPost]
public ActionResult CreateUser(UserViewModel model)
{
try
{
return Json(new { success = true, result = model, errorMessage = "" });
}
catch (WebException ex)
{
return Json(new { success = false, errorMessage = ex.Message });
}
}
Model values are null. Can anyone please let me know what i am missing here? Ajax post call is returning model to action method with null values.
Model values are null. Can anyone please let me know what i am missing here? Ajax post call is returning model to action method with null values.
As far as I know, if you use $("#kt_form").serialize() it will serialize the form into from data according to input tag's name attribute.
But you have set the input tag's name to FirstName. That means the asp.net core model binding will bind the value to FirstName property. But the value is used for UserAttribute's FirstName not the viewmodel's FirstName. This is the reason why your model binding result is null.
To solve this issue, I suggest you could try to use asp.net core tag helper to help you generate the input tag name or you could modify the name property to UserAttribute.FirstName.
You could modify the input as below:
<input type="text" class="form-control" placeholder="" required asp-for="#Model.UserAttribute.FirstName">
Detail view codes:
<form class="kt-form" id="kt_form">
<div class="kt-wizard-v3__content" data-ktwizard-type="step-content" data-ktwizard-state="current">
<div class="kt-form__section kt-form__section--first">
<div class="form-group row required">
<label for="FirstName" class="col-md-3 col-form-label k-font-bold text-md-right control-label">First Name</label>
<div class="col-6">
<input type="text" class="form-control" placeholder="" required asp-for="#Model.UserAttribute.FirstName">
</div>
</div>
</div>
<div class="btn btn-brand btn-md btn-tall btn-wide btn-bold btn-upper" data-ktwizard-type="action-submit" id="btnSubmit">Submit</div>
</div>
</form>
Result:
First thing first i want to apology if this topic has been mentioned before, but i looked for 2 days and never find about my problem.
So, I have a IFormFile script, which is does not throw any error (at least a syntax error) but when i am in the Upload Page, i complete my fields (Name,Description and File) and press Upload button, my OnPost code does not Fire at all and my page just referesh.
This is my Razor Page CREATE.CSHTML
#page
#model Appuntamenti.Models.ViewModel.DocumentCreateViewModel
#{
ViewData["Title"] = "Create";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<div>
<h4>Upload Single file</h4>
</div>
<form method="post" enctype="multipart/form-data" runat="server" asp-action="OnPost" class="mt-3">
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" placeholder="Name..." />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Description" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Description" class="form-control" placeholder="Description..." />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Document" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<div class="custom-file">
<input asp-for="Document" class="form-control custom-file-input" />
<label class="custom-file-label">Choose File..</label>
</div>
</div>
</div>
<button type="submit" class="btn btn-success form-control"></button>
#section Scripts {
<script>
$(document).ready(function ()
{
$('.custom-file-input').on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).next('.custom-file-label').html(fileName);
});
});
</script>
}
</form>
And This is my CREATE.CSHTML.CS page
namespace Appuntamenti.Pages.Documents
{
public class CreateModel : PageModel
{
private readonly ApplicationDbContext _db;
private readonly IHostingEnvironment _hostingEnvironment;
public CreateModel(ApplicationDbContext db, IHostingEnvironment hostingEnvironment)
{
_db = db;
_hostingEnvironment = hostingEnvironment;
}
[HttpPost]
public async Task<IActionResult> OnPostAsync (DocumentCreateViewModel model)
{
if (!ModelState.IsValid)
{
return NotFound();
}
string uniqueFileName = null;
if(model.Document != null)
{
string uploadsFolder = Path.Combine(_hostingEnvironment.WebRootPath, "Documents");
uniqueFileName = Guid.NewGuid().ToString() + "_" + model.Document.FileName;
string filePath = Path.Combine(uploadsFolder, uniqueFileName);
await model.Document.CopyToAsync(new FileStream(filePath, FileMode.Create));
}
DocumentModel newDocument = new DocumentModel
{
Id = model.Id,
Name = model.Name,
Description = model.Description,
DocumentPath = uniqueFileName
};
_db.Add(newDocument);
_db.SaveChanges();
return RedirectToPage("./Index");
}
}
}
And Those are my 2 Models for the IFormFile
public class DocumentModel
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string DocumentPath { get; set; }
}
public class DocumentCreateViewModel
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
public IFormFile Document { get; set; }
}
BAsically i tried to put a Breakpoint on the Post Method but it does not fire at all,
I tried to run the Website and inspect the elements,header and network and everything is ok.
After some browsing i read that the Onpost method with the IFormFile rely on the TokenValidation, i tried to ignore the validation and see if something change but nothing. I really dont know what i am doing wrong.
I hope i made my point and problem clear and please if you need more info just let me know
You mixed up Asp.Net Core MVC and Razor Page.
Follow steps below:
CreateModel
public class CreateModel : PageModel
{
[BindProperty]
public DocumentCreateViewModel DocumentCreateViewModel { get; set; }
//[HttpPost]
public async Task<IActionResult> OnPostAsync()
{
return RedirectToPage("./Index");
}
View
#page
#model CreateModel
#{
ViewData["Title"] = "Create";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<div>
<h4>Upload Single file</h4>
</div>
<form method="post" enctype="multipart/form-data">
<div class="form-group row">
<label asp-for="DocumentCreateViewModel.Name" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="DocumentCreateViewModel.Name" class="form-control" placeholder="Name..." />
<span asp-validation-for="DocumentCreateViewModel.Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="DocumentCreateViewModel.Description" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="DocumentCreateViewModel.Description" class="form-control" placeholder="Description..." />
<span asp-validation-for="DocumentCreateViewModel.Description" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="DocumentCreateViewModel.Document" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<div class="custom-file">
<input asp-for="DocumentCreateViewModel.Document" type="file" class="form-control custom-file-input" />
<label class="custom-file-label">Choose File..</label>
</div>
</div>
</div>
<button type="submit" class="btn btn-success form-control"></button>
#*<input type="submit" value="Submit" />*#
</form>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$(document).ready(function () {
$('.custom-file-input').on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).next('.custom-file-label').html(fileName);
});
});
</script>
}
For more information about Razor page, refer Introduction to Razor Pages in ASP.NET Core
i am using .net core with razor pages and i have a model as a list of object as below :
public class Member
{
public List<User> member { get; set; }
}
public class User
{
public String fname { get; set; }
public String lname { get; set; }
}
and i have a form to fill the object as below :
<form asp-controller="Population" asp-action="AddMember" method="post" class="form-horizontal" role="form" style="font-family:Cairo-Bold">
<div class="new-help add-form-container">
<input asp-for="member.LastName" type="text" />
<input asp-for="member.Firstname" type="text" />
</div>
and the user can add a object dynamically from the page when he clicks on a button i duplicate for him for div to add a new member as below :
<div class="row">
<div class="col-lg-12">
<a class="clone-button"><u> Add another</u></a>
</div>
</div>
$(function () {
$(".clone-button").on('click', function () {
var ele = $(this).closest('.new-help').clone(true);
$(this).closest('.new-help').after(ele);
});
});
how can i bind the text fields in the form in order to return to the controller the list of members added on submit?
You should generate element with index number like this code :
$(function () {
$("#add").click(function (e) {
e.preventDefault();
var i = $(".items").length;
var n = '<input type="text" class="items" name="ListItems[' + i + '].Name" />';
$("#item-list").append(n);
});
});
Please see this link for more information:
link
What I find is that List field in model needs to begin with a capital letter.Otherwise, I could not get a correct model binding.
public class Member
{
public int MemberId { get; set; }
public List<User> Users { get; set; }
}
Create view
#model AddMember.Models.Member
<form asp-action="Create" method="post">
<div class="form-group" id="item-list">
Add
<br/>
<input type="text" asp-for="Users" class="items" name="Users[0].fname"/>
<input type="text" asp-for="Users" class="items" name="Users[0].lname" />
</div>
<input type="submit" value="Create" class="btn btn-default" />
</form>
#section Scripts {
<script>
$(function () {
$("#add").click(function (e) {
e.preventDefault();
var i = ($(".items").length) / 2;
var n = '<input type="text" class="items" name="Users[' + i + '].fname" />' +
'<input type="text" class="items" name="Users[' + i + '].lname" />'
$("#item-list").append(n);
});
});
</script>
}
Controller:
[HttpPost]
public async Task<IActionResult> AddMember(Member member)
My Entity Framework passes a string like this to the database "1b2ef80d-038a-49d8-973b-fc783a53b6a3" instead of the text i placed into the input field, which was "text". How can i only Insert the exact value into the table?
The Database table i'm currently testing is just one column and is set to VARCHAR(400).
Context class:
modelBuilder.Entity<Contract>(entity =>
{
entity.HasKey(e => e.Contract1)
.HasName("Contract$PrimaryKey");
entity.Property<string>(e => e.Contract1)
.HasColumnName("Contract")
.HasColumnType("VARCHAR(400)")
.IsUnicode(false)
.HasMaxLength(400);
});
Model class:
[Key]
[Column(TypeName = "VARCHAR(400)")]
[StringLength(400)]
public string Contract1 { get; set; }
View page:
<form asp-action="Create">
<div class="form-horizontal">
<h4>Contract</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
#Html.LabelFor(model => model.Contract1, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.EditorFor(model => model.Contract1)
#Html.ValidationMessageFor(model => model.Contract1)
<span class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
</body>
</html>
And Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Contract")] Contract Contract)
{
if (ModelState.IsValid)
{
_context.Add(Contract);
await _context.SaveChangesAsync();
return RedirectToAction("Create");
}
ViewData["Contract"] = new SelectList(_context.Contract, "Contract", "Contract", Contract.Contract1);
return View(Contract);
}
}
}
i had to add the class, instead of just the context.Add i had to make it context.class.Add:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(Contract Contract)
{
if (ModelState.IsValid)
{
_context.Contract.Add(Contract);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewData["Contract"] = new SelectList(_context.Contract, "Contract", "Contract", Contract.Contract1);
return View(Contract);
}
}
}