How to create POST method in Asp.net core - c#

I am working on my college website project we created APIs for store data and retrieve data from the Database. Now I am integrating this API in to the admin panel but I don't know how to create a POST method or NewsAdd method.
This is my controller code.
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using BACKEND_HTML_DOT_NET.Models;
using Newtonsoft.Json;
using System.Text;
using RestSharp;
namespace BACKEND_HTML_DOT_NET.Controllers
{
public class News : Controller
{
private string apiBaseUrl = "https://localhost:44374/api";
HttpClient hc = new HttpClient();
private List<NewsVM> newsVMList = new List<NewsVM>();
public IActionResult NewsList()
{
var restClient = new RestClient(apiBaseUrl);
var restRequest = new RestRequest("/GetAllNewsDetails", Method.Get);
restRequest.AddHeader("Accept", "application/json");
restRequest.RequestFormat = DataFormat.Json;
RestResponse response = restClient.Execute(restRequest);
var content = response.Content;
var user = JsonConvert.DeserializeObject<ServiceResponse<List<NewsVM>>>(content);
newsVMList = user.data;
return View(newsVMList);
}
public IActionResult NewsView(int id=0)
{
NewsVM newsVM = new NewsVM();
newsVM = newsVMList.Where(m => m.Id == id).FirstOrDefault();
return View(newsVM);
}
public IActionResult NewsAdd()
{
return View();
}
}
}
This is my cshtml page
#model List<BACKEND_HTML_DOT_NET.Models.NewsVM>
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "NewsAdd";
}
<div class="content-wrapper">
<!-- <h3 style="margin-left: 15px;">DEPARTMENT</h3> -->
<section class="content" style="margin-top:20px;">
<div class="row">
<div class="col-md-12">
<a href="#Url.Action("NewsList","News")">
<ol class="breadcrumb float-sm-right">
<li style="margin-left: 00px;"><i class="nav-icon fas fa-minus-circle"></i></li>
<li style="margin-left: 00px;">Back to Details</li>
</ol>
</a>
</div>
<div class="col-md-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Add New News</h3>
</div>
<!-- /.card-header -->
<!-- form start -->
<form id="newsaddform" asp-action="NewsAdd" asp-controller="News" method="post" role="form">
#using(Html.BeginForm()){
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="exampleInputText">ENTER TITLE OF NEWS</label>
<input type="text" name="title" class="form-control" id="exampleInputEmail1" placeholder="ENTER TITLE.. ">
</div>
</div>
<div class="col-md-6">
<label for="description">ENTER DESCRIPTION OF NEWS</label>
<textarea type="text" name="description" class="form-control" id="description" rows="4" placeholder="Write content"></textarea>
</div>
</div>
</div>
<div class="card-footer">
<div class="input-group col-12">
<div class="col-sm-6"><button type="submit" id="sub" class="btn btn-primary float-right col-md-2">Submit</button></div>
<div class="col-sm-6"><button type="reset" class="btn btn-default col-md-2">Clear</button></div>
</div>
</div>
}
</form>
</div>
</div>
<!-- /.card-body -->
</div>
</section>
</div>
#section script{
<script type="text/javascript">
$(document).ready(function() {
//console.log( "ready!" );
$('#newsaddform').validate({
rules: {
"title": {
required: true
},
"description": {
required: true
}
},
messages: {
"title": {
required: "this field is required"
},
"description": {
required: "this field is required"
}
}
});
$('#newsaddform').submit(function(e) {
//prevent Default functionality
e.preventDefault();
if($('#newsaddform').valid()){
var dataForm = new FormData($('#newsaddform')[0]);
$.ajax({
url: '#Url.Action("NewsAdd","News")',
type: 'post',
crossDomain: true,
enctype: 'multipart/form-data',
dataType: 'application/json',
processData: false,
contentType: false,
data: dataForm,
success: function(data) {
console.log(data);
}
});
}
});
});
</script>
}
This is the model class
using System;
using System.Collections.Generic;
namespace BACKEND_HTML_DOT_NET.Models
{
public class NewsVM
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public bool? IsDeleted { get; set; }
public DateTime? CreatedDate { get; set; }
public string CreatedDateInt { get; set; }
public DateTime? UpdatedDate { get; set; }
public string UpdatedDateInt { get; set; }
}
}

When you select form submit(method="post"),you can send the post request directly by clicking the Submit button(type="submit"), this will submit the content of your input box by default,and don't need to send the post request through Ajax.
Below is my test code,you can refer to it:
View:
#model _2022072601.Models.NewsVM
<div class="content-wrapper">
<!-- <h3 style="margin-left: 15px;">DEPARTMENT</h3> -->
<section class="content" style="margin-top:20px;">
<div class="row">
<div class="col-md-12">
<a href="#Url.Action("NewsList","News")">
<ol class="breadcrumb float-sm-right">
<li style="margin-left: 00px;"><i class="nav-icon fas fa-minus-circle"></i></li>
<li style="margin-left: 00px;">Back to Details</li>
</ol>
</a>
</div>
<div class="col-md-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Add New News</h3>
</div>
<!-- /.card-header -->
<!-- form start -->
<form id="newsaddform" asp-action="NewsAdd" asp-controller="Home" method="post" role="form">
#using(Html.BeginForm()){
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="exampleInputText">ENTER TITLE OF NEWS</label>
<input type="text" name="title" class="form-control" id="exampleInputEmail1" placeholder="ENTER TITLE.. ">
</div>
</div>
<div class="col-md-6">
<label for="description">ENTER DESCRIPTION OF NEWS</label>
<textarea type="text" name="description" class="form-control" id="description" rows="4" placeholder="Write content"></textarea>
</div>
</div>
</div>
<div class="card-footer">
<div class="input-group col-12">
<div class="col-sm-6"><button type="submit" id="sub" class="btn btn-primary float-right col-md-2">Submit</button></div>
<div class="col-sm-6"><button type="reset" class="btn btn-default col-md-2">Clear</button></div>
</div>
</div>
}
</form>
</div>
</div>
<!-- /.card-body -->
</div>
</section>
</div>
Controller:
[HttpGet]
public IActionResult NewsAdd()
{
return View();
}
[HttpPost]
public JsonResult NewsAdd(NewsVM newsVM)
{
return Json(newsVM);
}
Test Result:
For more details, please refer to this link.

Related

Bind Image src to IFormFile asp.net core

I am starting to learn Asp.Net core. I have a model class which is saved to the database as follows,
public class BlogData
{
public int Id { get; set; }
public string BlogTitle { get; set; }
public string BlogContent { get; set; }
public DateTime PostedDateTime { get; set; }
public byte[] ImageData { get; set; }
}
Now I created an other class as follows,
public class BlogDataWithStringImage : BlogData
{
private IFormFile _actualImage;
public IFormFile ActualImage
{
get => _actualImage;
set
{
if (value != null)
{
using var fs1 = value.OpenReadStream();
using var ms1 = new MemoryStream();
fs1.CopyTo(ms1);
ImageData = ms1.ToArray();
}
_actualImage = value;
}
}
}
Problem: My intention is when some value gets bound to the ActualImage property, I want to convert it to byte Array. Problem is, when I keep a breakpoint on the set block of (ActualImage property in BlogDataWithStringImage class) the breakpoint never gets hit even when I choose the image on the View. Please help.
#model KGSBlog.Models.BlogDataWithStringImage
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>BlogData</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="BlogTitle" class="control-label"></label>
<input asp-for="BlogTitle" class="form-control" />
<span asp-validation-for="BlogTitle" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="BlogContent" class="control-label"></label>
<input asp-for="BlogContent" class="form-control" />
<span asp-validation-for="BlogContent" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PostedDateTime" class="control-label"></label>
<input asp-for="PostedDateTime" class="form-control" />
<span asp-validation-for="PostedDateTime" class="text-danger"></span>
</div>
<div class="col-md-12">
<div class="form-group">
<label asp-for="ActualImage" class="control-label"></label>
<div class="custom-file">
<input asp-for="ActualImage" class="custom-file-input" id="customFile">
<label class="custom-file-label" for="customFile">Choose file</label>#*Choosing an image never sets the ActualImage property*#
</div>
<span asp-validation-for="ActualImage" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Ok I figured it out. I was refering to this post in case if it helps someone, Image upload asp.net core
I added the following script inside the Scripts section as in the above post and it works fine.
<script type="text/javascript">
$(".custom-file-input").on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
</script>

Data is not binding in ViewModel Nested Class for POST method

When saving the details from the view, the details is not binding into the nested class (BOUser) in ViewModel.The nested class data is showing null(Refer the debug result image). I am using .net core 6 in Mac for the development. Please help me out what I am missed in this coding.
Controller Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using NearMeBuy.Models.Masters.ViewModels;
namespace NearMeBuyWebApp.Areas.Admin.Controllers
{
[Area("Admin")]
public class TestOPSController : Controller
{
public IActionResult Index()
{
RegisterVM registerVM = new RegisterVM()
{
ApplicationRoles = null,
BOUser = new(),
DepartmentMaster = null,
Mode = 1
};
return View(registerVM);
}
[HttpPost]
public IActionResult Index(RegisterVM vM )
{
if (ModelState.IsValid)
{
return View("Hi");
}
return View();
}
}
}
View Model Class
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.Rendering;
using NearMeBuy.Models.Tokens.User;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NearMeBuy.Models.Masters.ViewModels
{
public class RegisterVM
{
public int Mode { get; set; }
//public RegisterModel RegisterModel { get; set; }
public BOUser BOUser { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> ApplicationRoles { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> DepartmentMaster { get; set; }
}
}
BOUser Class
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.EntityFrameworkCore;
namespace NearMeBuy.Models.Masters
{
public class BOUser
{
public string UserId { get; set; }
[Required(ErrorMessage = "First Name can't be empty")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last Name can't be empty")]
public string LastName { get; set; }
[Required(ErrorMessage = "Email can't be empty")]
public string Email { get; set; }
[Required(ErrorMessage = "Phone can't be empty")]
public string PhoneNo { get; set; }
[Required(ErrorMessage = "Username can't be empty")]
public string UserName { get; set; }
[Required(ErrorMessage = "Account Confirmation can't be empty")]
public bool AccountConfirmed { get; set; } = false;
/// <summary>
/// The user type field to store the type of user 1: for BackOffice User , 2: for Supplier Registration, 3: for End Users/ Customers.
/// </summary>
[Required(ErrorMessage = "User Type Field can't be empty")]
[Comment("The user type field to store the type of user 1: for BackOffice User , 2: for Supplier Registration, 3: for End Users/ Customers., 0: for Administrator")]
public Int16 UserType { get; set; }
[Required(ErrorMessage = "Password can't be empty")]
public string Password { get; set; }
[ValidateNever]
public string? returnUrl { get; set; }
}
}
View
#model RegisterVM
#{
ViewData["Title"] = "Register Page";
}
<form id="registrationForm" asp-controller="TestOPS" asp-action="Index" method="post">
<div class="card">
<div class="card-header">
<h5 class="card-title">
#{
#if (#Model.Mode == 1)
{
<b>Create</b>
}
else if (#Model.Mode == 2)
{
<b>Edit</b>
}
else if (#Model.Mode == 3)
{
<b>Delete</b>
}
}
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-12">
<input type="hidden" id="hdnMode" asp-for="Mode" />
<input type="hidden" id="hdnUserId" asp-for="BOUser.UserId" />
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>First Name</label>
</div>
<div class="col-lg-4 form-group">
<input class="form-control form-control-sm" asp-for="#Model.BOUser.FirstName" name="firstname" />
<span asp-validation-for="BOUser.FirstName" class="text-danger"></span>
</div>
<div class="col-lg-2">
<label>Last Name</label>
</div>
<div class="col-lg-4 form-group">
<input class="form-control form-control-sm" asp-for="BOUser.LastName" name="lastname" />
<span asp-validation-for="BOUser.LastName" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>Email</label>
</div>
<div class="col-lg-4 form-group">
<input class="form-control form-control-sm" asp-for="BOUser.Email" name="email" />
<span asp-validation-for="BOUser.Email" class="text-danger"></span>
</div>
<div class="col-lg-2">
<label>Phone No.</label>
</div>
<div class="col-lg-4 form-group">
<input class="form-control form-control-sm" asp-for="BOUser.PhoneNo" name="phoneno" />
<span asp-validation-for="BOUser.PhoneNo" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>User Name</label>
</div>
<div class="col-lg-4 form-group">
<input class="form-control form-control-sm" asp-for="BOUser.UserName" name="username" />
<span asp-validation-for="BOUser.UserName" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>Password</label>
</div>
<div class="col-lg-4 form-group">
<input class="form-control form-control-sm" asp-for="BOUser.Password" name="password" id="txtPassword" />
<span asp-validation-for="BOUser.Password" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>Confirm Password</label>
</div>
<div class="col-lg-4 form-group">
<input class="form-control form-control-sm" name="confirmPassword" />
<span asp-validation-for="BOUser.Password" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>Roles</label>
</div>
<div class="col-lg-5">
<table>
#if (Model.ApplicationRoles != null)
{
#foreach (var roles in Model.ApplicationRoles)
{
<tr>
<td>
<input id="#roles.Value" type="checkbox" name="roles" value="#roles.Value" checked="#roles.Selected" asp-for="#roles.Selected" />
</td>
<td>
<label asp-for="#roles.Value">#roles.Text</label>
</td>
</tr>
}
}
</table>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>Department</label>
</div>
<div class="col-lg-5">
<table>
#if (Model.DepartmentMaster != null)
{
#foreach (var dept in Model.DepartmentMaster)
{
<tr>
<td>
<input id="#dept.Value" type="checkbox" name="departments" value="#dept.Value" checked="#dept.Selected" asp-for="#dept.Selected" />
</td>
<td>
<label asp-for="#dept.Value">#dept.Text</label>
</td>
</tr>
}
}
</table>
</div>
</div>
<div class="row">
<div class="col-lg-2">
<label>Approved</label>
</div>
<div class="col-lg-4">
<input type="checkbox" asp-for="BOUser.AccountConfirmed" />
</div>
</div>
</div>
<div class="card-footer text-right">
<input type="submit" class="btn btn-secondary" value="Save" />
</div>
</div>
</form>
#section Scripts{
<script src="~/js/regLogin.js"></script>
}
Debug Result
enter image description here
Remove a name attirbute from ALL web controls, for example should be just
<input class="form-control form-control-sm" asp-for="BOUser.LastName" />

Model not posting from View to Controller

I have a view with a model that post data to controller but after submitting, posted model has default values. How can I resolve this issue?
My Model:
public partial class SaleModel
{
[Required]
public byte CategoryId { get; set; }
[Required]
public int UserId { get; set; }
[Required]
[Range(1, int.MaxValue)]
public int ProductId { get; set; }
[Required]
[StringLength(11)]
[RegularExpression("^(989|09|9)[012349][0-9]\\d{7}$")]
public string Mobile { get; set; }
[Required]
[Range(5000, 10000000]
public int Amount { get; set; }
[Required]
public float DiscountPercent { get; set; }
}
I have to submit button in view and string submit in Controller for that:
#using EKhadamat.Models.Sales
#using EKhadamat.Services.Operators
#using EKhadamat.Core.Models.Products
#model SaleModel
#inject IOperatorService operatorService
#{
ViewData["Title"] = "شارژ ارزان";
var category = Category.Charge;
}
#using (Html.BeginForm("Sale", "Topup", FormMethod.Post))
{
<div class="card border border-primary shadow view-height bg-light">
<div class="card-body">
<div class="row">
<div class="col-lg-6">
<h1 class="text-primary">
شارژ ارزان
<label id="lblDescription"></label>
</h1>
<h6 class="text-info mb-3"><span class="fas fa-hand-point-left fa-fw ml-1"></span>با 2 درصد تخفیف، سیم کارت اعتباری خود را شارژ کنید.</h6>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">
<i class="fas fa-mobile-alt fa-fw text-primary"></i>
</span>
</div>
<input asp-for="Mobile" class="form-control" type="number" placeholder="#Html.DisplayFor(model => model.Mobile)" />
<button ID="LNK_Charge_Contacts" class="btn btn-info" title="لیست مخاطبین" CommandArgument="TB_Charge_Mobile">
<span class="fas fa-address-book fa-fw fa-inverse"></span>
</button>
</div>
<div class="mb-2">
<span asp-validation-for="Mobile" class="text-danger text-desc"></span>
</div>
...
<button id="btnSubmit" type="button" class="btn btn-block btn-primary mb-5">بررسی نهایی</button>
<div id="divReceipt" class="d-none">
<div class="row">
<div class="col-12">
<div class="alert alert-success mb-2" role="alert">
<h4>
<span>پیش‌فاکتور</span>
<span class="fas fa-receipt fa-fw float-left"></span>
</h4>
<hr />
<label id="lblTitle"></label>
<br />
<label id="lblOperator"></label>
<br />
<label id="lblMobile"></label>
<br />
<label id="lblCount"></label>
<hr />
<div class="text-center">
<span class="fas fa-2x fa-coins fa-fw ml-1 text-gold"></span>
<label id="lblPayment"></label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-sm-6">
<button id="btnCredit" type="submit" name="submit" value="Credit" class="btn btn-block btn-info mb-2" disabled="#Model.UserId <= 0 ? 'disabled' : ''">خرید اعتباری</button>
</div>
<div class="col-lg-6 col-sm-6">
<button id="btnCash" type="submit" name="submit" value="Cash" class="btn btn-block btn-primary mb-2">خرید نقدی</button>
</div>
</div>
<button id="btnReturn" type="button" class="btn btn-block btn-secondary mb-5">بازگشت و ویرایش</button>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-body">
<img id="imgCharge" src="~/img/charge.jpg" class="img-fluid" alt="شارژ ارزان ایرانسل، شارژ ارزان همراه اول و شارژ ارزان رایتل" />
<div class="ribbon animated infinite pulse">
<span>2 درصد تخفیف</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
#section Scripts {
<partial name="_ValidationScriptsPartial" />
<script>
$("#rblOperators").change(function () {
var checkedRadio = $("#rblOperators input:checked");
var operatorId = checkedRadio.val();
var ddl = $("##Html.IdFor(model => model.ProductId)");
$.ajax({
cache: false,
url: "#(Url.Action("GetTopupListByOperator", "Topup"))",
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
"operatorId": operatorId
},
success: function (data, textStatus, jqXHR) {
ddl.html('');
$.each(data, function (i, option) {
ddl.append($("<option></option>").val(option.id).html(option.name).data("desc", option.desc));
});
}
})
});
--jquery code for show/hide elements or get data from controller
</script>
}
My controller action:
[HttpPost]
public virtual IActionResult Sale(SaleModel saleModel, string submit)
{
switch (submit)
{
case "Cash":
break;
case "Credit":
break;
}
return View();
}
for example after submitting form, productid=0 and mobile is empty.
I have a button before submit buttons and on click form validation occurred and model was being cleaned. I changed button type to submit and used form validation in controller.

ASP.Net Core MVC FormForm method receives null values

I have an application which simulates a clothing site.I try to build my C# object with the data that the user writes in the form and the products they bought on the site.The products are represented by a JSON object.I use 2 classes: one for the shopping cart and one for the product.
public class Product
{
[Key]
public int id { get; set; }
public string productName { get; set; }
public string productPrice { get; set; }
public string quantity { get; set; }
}
public class ShoppingCart
{
[Key]
public int? id { get; set; }
public List<Product> productList { get; set; }
public string clientName { get; set; }
public string clientAddress { get; set; }
public string clientMail { get; set; }
}
I use a controller which has the method "Save" which uses the [FromForm] attribute.After the objects is binded from the client side I add it to my database.The problem is that I get "null" values for every property in my ShoppingCart object that is sent to the method.Moreover in my browser, the data is sent correctly to the server:
screenshot from network tab in chrome
The controller that I use looks like this:
[Route("SendItems/Save")]
[ApiController]
public class SendItemsController : Controller
{
private AppDbContext _db;
public SendItemsController(AppDbContext db)
{
_db = db;
}
[HttpPost]
[Consumes("application/json")]
public async Task<IActionResult> Save([FromForm] ShoppingCart s)
{
await _db.ShoppingCarts.AddAsync(s);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
[HttpGet("~/ThankYou/Index")]
public IActionResult Index()
{
return View();
}
}
My html for the form is written like this:
#model ShoppingCart
<div class="form-group row">
<div class="col-4">
<label id="clienId"></label>
</div>
<div class="col-6">
<input asp-for="id" id="idClient" type="hidden" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientProds"></label>
</div>
<div class="col-6">
<input asp-for="productList" id="inputProducts" type="hidden" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientName"></label>
</div>
<div class="col-6">
<input asp-for="clientName" id="inputName" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientAddress"></label>
</div>
<div class="col-6">
<input asp-for="clientAddress" id="inputAddress" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientMail"></label>
</div>
<div class="col-6">
<input asp-for="clientMail" id="inputMail" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-3 offset-4">
<button class="btn btn-primary" id="orderB" asp-controller="SendItems" action="Save" type="submit">ORDER</button>
</div>
</div>
</form>
Also another issue is that if I don't use this piece of javascript the client is not redirected anymore to the "ThankYou" page :
var orderB = document.getElementById("orderB");
orderB.addEventListener("click", function () {
var inputName = document.getElementById("inputName").value;
var inputAddress = document.getElementById("inputAddress").value;
var inputMail = document.getElementById("inputMail").value;
var auxArray = [];
for (var i = 0; i < productsAux.length; i++) {
if (productsAux[i]!="") {
auxArray[i-1] = { "productName": productsAux[i].titlu, "productPrice": productsAux[i].pret, "quantity": localStorage.getItem(productsAux[i].titlu) };
}
}
document.getElementById("inputProducts").value = JSON.stringify(auxArray);
var shoppingCart = {
productList: auxArray,
clientName: inputName,
clientAddress: inputAddress,
clientMail: inputMail
};
$.ajax({
type: "POST",
data: JSON.stringify(shoppingCart),
url: "senditems/save",
contentType: "application/json;charset=utf-8",
})
})
Here is a demo worked:
Cart.cshtml(You need to change the bind of `productList,and then you don't need to use ajax to pass data to controller):
<form method="post" asp-controller="SendItems" asp-action="Save">
<div class="form-group row">
<div class="col-4">
<label id="clienId"></label>
</div>
<div class="col-6">
<input asp-for="id" id="idClient" type="hidden" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientProds"></label>
</div>
<div class="col-6">
#for (var i = 0; i < Model.productList.Count(); i++)
{
<input type="hidden" asp-for="#Model.productList[i].id" />
<input type="hidden" asp-for="#Model.productList[i].productName" />
<input type="hidden" asp-for="#Model.productList[i].productPrice" />
<input type="hidden" asp-for="#Model.productList[i].quantity" />
}
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientName"></label>
</div>
<div class="col-6">
<input asp-for="clientName" id="inputName" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientAddress"></label>
</div>
<div class="col-6">
<input asp-for="clientAddress" id="inputAddress" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label id="clientMail"></label>
</div>
<div class="col-6">
<input asp-for="clientMail" id="inputMail" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-3 offset-4">
<button class="btn btn-primary" id="orderB" type="submit" >ORDER</button>
</div>
</div>
</form>
Controller(SendItems):
[HttpPost]
public async Task<IActionResult> Save(ShoppingCart s)
{
return RedirectToAction("Index");
}
[HttpGet("~/ThankYou/Index")]
public IActionResult Index()
{
return View();
}
public IActionResult Cart()
{
ShoppingCart s = new ShoppingCart { id = 1, clientAddress = "address", clientName = "name1", clientMail = "123#123", productList = new List<Product> { new Product { id = 1, productName = "p1", productPrice = "10", quantity = "1" }, new Product { id = 2, productName = "p12", productPrice = "12", quantity = "3" } } };
return View(s);
}
result:

Razor Page - Form with multiple handlers/server-side validation not populating asp-validation-for tags on submit

I have a form that is used to submit one of two objects. Using Fluent Validation, each object has separate validation rules, which are handled by executing ModelState.Clear() and TryValidateModel(objectName) in the respective post handler. Validation is working correctly, but is only displaying on the asp-validation-summary tag, not on the asp-validation-for tags that accompany each field. Does anyone have any idea how to get around this? (Using 2 forms is not an option.) Here is code you can use to replicate the issue:
TestValidation.cshtml:
#page
#model MyApp.Namespace.TestValidationModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>TestValidation</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<h1>Test Validation</h1>
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">ObjectToValidate1 - posts with empty handler, validates server side.</div>
<div class="card-body">
<div class="form-group">
<label class="control-label">Borrower Type</label>
<select asp-for="ObjectToValidate1.Item" asp-items="#Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
<span asp-validation-for="ObjectToValidate1.Item" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label"></label>
<label asp-for="ObjectToValidate1.RequiredString" class="control-label businessName"></label>
<input asp-for="ObjectToValidate1.RequiredString" class="form-control autofocus" />
<span asp-validation-for="ObjectToValidate1.RequiredString" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="control-label"></label>
<input asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="form-control" />
<span asp-validation-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" asp-page-handler="">Submit</button>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">ObjectToValidate2 - posts with handler of TestValidation, validates serverside</div>
<div class="card-body">
#*<div class="form-group">
<label class="control-label">Borrower Type</label>
<select asp-for="ObjectToValidate2.Item" asp-items="#Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
<span asp-validation-for="ObjectToValidate2.Item" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label"></label>
<label asp-for="ObjectToValidate2.RequiredString" class="control-label businessName"></label>
<input asp-for="ObjectToValidate2.RequiredString" class="form-control autofocus" />
<span asp-validation-for="ObjectToValidate2.RequiredString" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="control-label"></label>
<input asp-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="form-control" />
<span asp-validation-for="ObjectToValidate2.RequiredStringIfItem1Selected" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" asp-page-handler="TestValidation">Submit</button>
</div>*#
</div>
</div>
</div>
</div>
</form>
#*<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>*#
</body>
</html>
TestValidation.cshtml.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace MyApp.Namespace
{
public class TestValidationModel : PageModel
{
[BindProperty]
public InputValues ObjectToValidate1 { get; set; }
[BindProperty]
public InputValues ObjectToValidate2 { get; set; }
public List<SelectListItem> DDLItems { get; set; }
public void OnGet()
{
ObjectToValidate1 = new InputValues();
ObjectToValidate2 = new InputValues();
InitializeDDLItems();
}
public ActionResult OnPost()
{
ModelState.Clear();
TryValidateModel(ObjectToValidate1);
if (ModelState.IsValid)
{
// Refresh current page
return RedirectToPage("./TestValidation");
}
InitializeDDLItems();
return Page();
}
public ActionResult OnPostTestValidation()
{
ModelState.Clear();
TryValidateModel(ObjectToValidate2);
if (ModelState.IsValid)
{
// Refresh current page
return RedirectToPage("./TestValidation");
}
InitializeDDLItems();
return Page();
}
private void InitializeDDLItems()
{
DDLItems = new List<SelectListItem>
{
new SelectListItem("-- Select an Item -- ", ""),
new SelectListItem("Item 1", "1"),
new SelectListItem("Item 2", "2"),
};
}
}
public class InputValues
{
[Display(Name ="Item")]
public int Item { get; set; }
[Display(Name = "Required String")]
public string RequiredString { get; set; }
[Display(Name = "Conditional Required String")]
public string RequiredStringIfItem1Selected { get; set; }
}
public class Validator : AbstractValidator<InputValues>
{
public Validator()
{
RuleFor(i => i.Item).NotEmpty().WithMessage("Item is required");
RuleFor(i => i.RequiredString).NotEmpty().WithMessage("Required String is required");
When(i => i.Item.Equals(1), () =>
{
RuleFor(e => e.RequiredStringIfItem1Selected).NotEmpty().WithMessage("Conditional Required String is required if Item 1 selected");
});
}
}
}
You need to add the following javascript files which support client-side validation :
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryvalidate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
The jQuery Unobtrusive Validation script is a custom Microsoft front-end library that builds on the popular jQuery Validate plugin. Without jQuery Unobtrusive Validation, you would have to code the same validation logic in two places: once in the server-side validation attributes on model properties, and then again in client-side scripts.
Reference :https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.0#client-side-validation
The answer: Don't use ModelState.Clear(). Doing so clears out the ModelState to the extent that, even after running TryValidateModel(modelname), the resulting ModelState doesn't have enough to enable the validation tags to render. So, here is a simplified version of my earlier sample, with ModelState.Clear() commented out and ModelState.ClearValidationState() run for each object that we want to validate server side. To run for my original code, above, I would ClearValidationState for the fields that are not relevant to the current OnPost method.
The Razor Page:
#page
#model MyApp.Namespace.TestValidation2Model
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>TestValidation</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<h1>Test Validation</h1>
<form method="post">
#*<div asp-validation-summary="All" class="text-danger"></div>*#
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">ObjectToValidate1 - posts with empty handler, validates server side.</div>
<div class="card-body">
<div class="form-group">
<label class="control-label">Borrower Type</label>
<select asp-for="ObjectToValidate1.Item" asp-items="#Model.DDLItems" class="form-control" onchange="changeBorrowerType();"></select>
<span asp-validation-for="ObjectToValidate1.Item" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label"></label>
<label asp-for="ObjectToValidate1.RequiredString" class="control-label businessName"></label>
<input asp-for="ObjectToValidate1.RequiredString" class="form-control autofocus" />
<span asp-validation-for="ObjectToValidate1.RequiredString" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="control-label"></label>
<input asp-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="form-control" />
<span asp-validation-for="ObjectToValidate1.RequiredStringIfItem1Selected" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success" asp-page-handler="">Submit</button>
</div>
</div>
</div>
</div>
</div>
</form>
#*<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>*#
</body>
</html>
The PageModel:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace MyApp.Namespace
{
public class TestValidation2Model : PageModel
{
[BindProperty]
public InputValues2 ObjectToValidate1 { get; set; }
public List<SelectListItem> DDLItems { get; set; }
public void OnGet()
{
ObjectToValidate1 = new InputValues2();
InitializeDDLItems();
}
public ActionResult OnPost()
{
ModelState.ClearValidationState(nameof(ObjectToValidate1.RequiredString));//.Clear();
ModelState.ClearValidationState(nameof(ObjectToValidate1.Item));//.Clear();
ModelState.ClearValidationState(nameof(ObjectToValidate1.RequiredStringIfItem1Selected));
//ModelState.Clear();
TryValidateModel(ObjectToValidate1);
var jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(ModelState);
if (ModelState.IsValid)
{
// Refresh current page
return RedirectToPage("./TestValidation2");
}
InitializeDDLItems();
return Page();
}
private void InitializeDDLItems()
{
DDLItems = new List<SelectListItem>
{
new SelectListItem("-- Select an Item -- ", ""),
new SelectListItem("Item 1", "1"),
new SelectListItem("Item 2", "2"),
};
}
}
public class InputValues2
{
[Display(Name = "Item")]
public int Item { get; set; }
[Display(Name = "Required String")]
public string RequiredString { get; set; }
[Display(Name = "Conditional Required String")]
public string RequiredStringIfItem1Selected { get; set; }
}
public class Validator2 : AbstractValidator<InputValues2>
{
public Validator2()
{
RuleFor(i => i.Item).NotEmpty().WithMessage("Item is required");
RuleFor(i => i.RequiredString).NotEmpty().WithMessage("Required String is required");
When(i => i.Item.Equals(1), () =>
{
RuleFor(e => e.RequiredStringIfItem1Selected).NotEmpty().WithMessage("Conditional Required String is required if Item 1 selected");
});
}
}
}

Categories

Resources