How to validate an input out of model in MVC5 - c#

I have been trying to validate an input which is not properly in the model. I have the following code:
#using (Html.BeginForm("Address", "Locations", FormMethod.Post, new { id =
"mainForm" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="card shadow-sm">
<div class="card-header">
<h3 class="card-title">
Step 1
</h3>
<label>
Search for service location(s)
</label>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputEmail1">Zip code: <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="ZipCode" name="ZipCode" autocomplete="off" autofocus />
#Html.ValidationMessage("ZipCode")
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1">House number:</label>
<input type="text" class="form-control" id="HouseNumber" name="HouseNumber" />
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1">City:</label>
<input type="text" class="form-control" id="City" name="City" />
</div>
</div>
</div>
</div>
<div class="card-footer">
<div class="row">
<div class="col-md-12">
<button class="btn btn-primary" type="submit">
<i class="fas fa-search"></i>
Search for location(s)
</button>
</div>
</div>
</div>
</div>
}
The mode is
#model PagedList.IPagedList<iCRM.Models.Address>
But the name which I gave to the input is not in the model. However the validation is not working at all. And the POST is ignoring my validation.
Can somebody help me out, what I am doing wrong?
Thanks in advance.

You have 2 options.
add ZipCode to the model class iCRM.Models.Address.
Write custom validation with Request.Forms["ZipCode"] and Javascript handler on client side. (#Html.ValidationMessage("ZipCode") you can not call)
This is idea based on option 2 but not a definite answer.
Controller
[HttpGet]
public ActionResult Address()
{
return View();
}
[HttpPost]
public ActionResult Address(AddressModel model)
{
if (String.IsNullOrEmpty(Request.Form["ZipCode"]))
{
ViewBag.ValidationForZipCode = "Problem with ZipCode";
}
return View();
}
View
#model WebApplication1.Models.AddressModel
#{
ViewBag.Title = "Address";
string strValidationForZipCode = "";
if (ViewBag.ValidationForZipCode != null)
{
strValidationForZipCode = ViewBag.ValidationForZipCode.ToString();
}
}
<h2>Address</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken();
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="pull-right">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>
<table>
<tr>
<td>
<div class="form-group">
#Html.LabelFor(model => model.Address1, htmlAttributes: new {
#class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Address1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Address1, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label for="exampleInputEmail1">Zip code: <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="ZipCode" name="ZipCode" autocomplete="off" autofocus />
<input type="hidden" id="hdValidationForZipCode" name="hdValidationForZipCode" value="#strValidationForZipCode" />
</div>
</td>
</tr>
<tr>
<td>
</td>
</tr>
</table>
</div>
}
<script type="text/javascript">
var str1 = document.getElementById("hdValidationForZipCode").value; //$('#hdValidationForZipCode').val();
if (str1 != "")
alert(str1);
</script>
Model
using System.ComponentModel.DataAnnotations;
namespace WebApplication1.Models
{
public class AddressModel
{
[Required]
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
}

Related

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:

Is my ViewModel flow right? How to send it back to controller and refresh it on a controller action?

Is my ViewModel flow right? The 'SubObjectViewModel' class usage is a bad or a good practice? Or should I try other option, like creating a ViewModel only to this class?
And also, how should I return the ViewModel 'ObjectViewModel' to the controller with all its values, change them and refresh values from page view?
My ViewModel
Public class ObjectViewModel{
public string name { get; set; }
public int value { get; set; }
public bool IsCameraPermited { get; set; }
public List<SubObjectViewModel> choosenSubObjects{ get; set; } // need to get it back on controller;
public class SubObjectViewModel
{
public int IdObject { get; set; }
public string Name { get; set; }
public string Config { get; set; }
}
public List<Object> listSub{ get; set; } //list that will be filled on 'Create' Controller
}
My Controller
public IActionResult Create(int id)
{
List<Object> listSubObject = new List<Object>();
listSubObject = _getAllSubObjectsDAO.ListByID(id);
List<Object> choosenObjects= new List<Object>();
choosenObjects = _getChoosenObjectsDAO.ListByID(id);
List<SubObjectViewModel> listSubObject = new List<SubObjectViewModel>();
foreach (Object item in choosenObjects )
{
string config = _configurationDAO.GetConfigurationById(item.configId);
ObjectViewModel .SubObjectViewModel SubObject = new ObjectViewModel .SubObjectViewModel { IdObject = item.Id, Name = item.Name , Config = config };
listSubObject.Add(setorVM);
}
ObjectViewModel objVM = ObjectViewModel{
name ="test",
value = 2,
IsCameraPermited =true,
listSub = listSubObject,
choosenSubObjects = listSubObject
};
return View(objVM);
}
My View
#model Project.Models.ViewModels.ObjectViewModel
... more code
<form asp-action="Create">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#foreach (var item in listSub)
{
<div class="txt">
#item
</div>
}
</div>
</div>
</div>
</form>
Thanks in advance.
how should I return the ViewModel 'ObjectViewModel' to the controller with all its values
To achieve above requirement, you can refer to the following code snippet.
#model ObjectViewModel
#{
ViewData["Title"] = "Create";
var choosenSubObjects = Model.choosenSubObjects;
}
<form method="post">
<div class="row">
<div class="col-sm-6 ctnFullHeight">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ctnFullHeight">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="name " class="form-control" />
<span asp-validation-for="name " class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsCameraPermited" class="control-label"></label>
<input asp-for="IsCameraPermited" type="checkbox" />
</div>
<div class="form-group float-right position-relative">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
<div class="col-sm-6">
<div class="shadowBoxForm formCreateLeft position-absolute col-sm-11 ">
#for (var i = 0; i < Model.choosenSubObjects.Count; i++)
{
<div class="txt">
<div class="form-group">
<label asp-for="#choosenSubObjects[i].IdObject" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].IdObject" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Name" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Name" type="text" />
</div>
<div class="form-group">
<label asp-for="#choosenSubObjects[i].Config" class="control-label"></label>
<input asp-for="#choosenSubObjects[i].Config" type="text" />
</div>
</div>
}
</div>
</div>
</div>
</form>
Controller Actions
[HttpGet]
public IActionResult Create(int id)
{
//code logic here
//...
return View(objVM);
}
[HttpPost]
public IActionResult Create(ObjectViewModel objectViewModel)
{
//code logic here
//...
Test Result

EditorFor losing data on post

I need some help with this, please!
I have this Model:
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
I have also a EditorTemplate
#model MyProject.Models.MyModel
<div class="form-group">
<label class="col-md-2 control-label">#Html.DisplayFor(model => model.Name)</label>
<div class="col-md-10">
<input asp-for="#Model.Value" class="form-control"/>
<span asp-validation-for="#Model.Value" class="text-danger"></span>
</div>
</div>
The Edit View
#using Microsoft.AspNetCore.Mvc.ViewFeatures
#model List<MyProject.Models.MyModel>
#{
ViewData["Title"] = "Edit";
}
<div class="spaceUnderLogo">
<div class="row">
<div class="col-md-12 ">
<h2>Edit</h2>
<form id="manageMyModel" asp-controler="MyController" asp-action="Edit" method="post" class="">
<div class="form-horizontal">
<hr />
#Html.EditorForModel()
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div>
</div>
And MyController has this two methods
[HttpGet]
public async Task<IActionResult> Edit()
{
List<MyModel> model = await GetCurrentInfoAsync();
return View(model);
}
[HttpPost]
public async Task<ActionResult> Edit(List<MyModel> model)
{
// here is where I have the issue
}
As you see I'm having an issue with the post method. When I get the view I send 2 objects: {Id = 1, Name = "A", Value = "9999"} and {Id = 2, Name = "B", Value = null}
Edit B Value property to "8888" and in the Post I'm getting the List only with the Value property like this:
{Id = 0, Name = null, Value = "9999"} and {Id = 0, Name = null, Value = "8888"}
Any idea?
You can use:
#model MyProject.Models.MyModel
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Name)
<div class="form-group">
<label class="col-md-2 control-label">#Html.DisplayFor(model => model.Name)</label>
<div class="col-md-10">
<input asp-for="#Model.Value" class="form-control"/>
<span asp-validation-for="#Model.Value" class="text-danger"></span>
</div>
</div>
The trick here is the:
#Html.HiddenFor(model => model.Id)
This will ensure that the form contains the value when posted, but there is no editor visible on the form. You will need one of these hidden input for any field that is readonly for the model retrieved by a GET in any form submit.

Posting ul elements to a controller

I have a simple email form with TO, CC, BCC and another field for an attachment location i have the view working but i cant workout how to get all 3 ul lists to post to the controller
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-md-2">To</label>
<div class="col-md-10">
<ul name="To" id="email-To"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">Cc</label>
<div class="col-md-10">
<ul name="CC" id="email-Cc"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">Bcc</label>
<div class="col-md-10">
<ul name="Bcc" id="email-Bcc"></ul>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FileName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FileName, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="pull-right">
<button type="submit" class="btn btn-success btn-xs">
<i class="fa fa-save"></i> Create
</button>
</div>
</div>
}
The model:
public System.Guid ID { get; set; }
public string To { get; set; }
public string CC { get; set; }
public string BCC { get; set; }
public string FileName { get; set; }
It will post the filename but not the ul's im guessing i would need to do some kind of json post for this kind of thing im not two sure?

How to generate jQuery template in ASP.NET MVC?

I want to add input to form dynamically with jQuery templates.
My viewModel for rendering form is listed below
public class FormViewModel
{
public int Id { get; set; }
[Required]
[StringLength(25, ErrorMessage = "Max firstname length is 25 symbols.")]
[DisplayName("First name")]
public string FirstName { get; set; }
[Required]
[StringLength(25, ErrorMessage = "Max lastname length is 25 symbols.")]
[DisplayName("Last name")]
public string LastName { get; set; }
[Required]
[Email(ErrorMessage = "Provide correct email address, please.")]
[DisplayName("Email")]
public string Email { get; set; }
[Range(16, 150, ErrorMessage = "Age should be between 16 and 150.")]
[DisplayName("Age")]
public int? Age { get; set; }
public IList<DiscountCode> Discounts { get; set; }
}
This is my model which I use for inputs that will be created dynamically.
public class DiscountCode
{
[Required]
[DisplayName("Code name")]
[StringLength(10, ErrorMessage = "Max name length is 10 symbols.")]
public string Code { get; set; }
[Required]
[DisplayName("Code discount")]
[Integer(ErrorMessage = "The field Percent should be a positive non-decimal number")]
[Range(1,60, ErrorMessage = "The field Percent should be between 1 and 60.")]
public int Percent { get; set; }
}
I have this partial view for rendering DiscountCode inputs
#using DynamicForm.Models
#model FormViewModel
#if (Model != null && Model.Discounts != null)
{
for (int i = 0; i < Model.Discounts.Count; i++)
{
<div class="row">
<input type="hidden" name="Discounts.Index" value="#i" />
<div class="col-md-4 form-group">
<div class="input-group">
#Html.TextBoxFor(m => m.Discounts[i].Code, new { #class = "form-control " })
#Html.ValidationMessageFor(m => m.Discounts[i].Code, string.Empty, new { #class = "help-block" })
</div>
</div>
<div class="col-md-6 form-group">
<div class="input-group">
#Html.LabelFor(m => m.Discounts[i].Percent, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Discounts[i].Percent, new { #class = "form-control " })
#Html.ValidationMessageFor(m => m.Discounts[i].Percent, string.Empty, new { #class = "help-block" })
</div>
</div>
<div class="col-md-2 form-group">
<div class="input-group">
<button type="button" class="btn btn-primary removeDiscountRow">Remove</button>
</div>
</div>
</div>
}
}
And for adding discount inputs I use this code snippet
var data = { index: lastIndex };
var html = $.templates("#discountRow").render(data);
$(html).appendTo($discountsContainer);
and this template
<script id="discountRow" type="text/x-jsrender">
<div class="row">
<input type="hidden" name="Discounts.Index" value="{{: index}}">
<div class="col-md-4 form-group">
<div class="input-group">
<label class="control-label" for="Discounts_{{: index}}__Code">Code name</label>
<input class="form-control " data-val="true" data-val-required="Code is required" data-val-length="Max name length is 10 symbols." data-val-length-max="10"
id="Discounts_{{: index}}__Code" name="Discounts[{{: index}}].Code" type="text" value="">
<span class="field-validation-valid help-block" data-valmsg-for="Discounts[{{: index}}].Code" data-valmsg-replace="true"></span>
</div>
</div>
<div class="col-md-6 form-group">
<div class="input-group">
<label class="control-label" for="Discounts_{{: index}}__Percent">Code discount</label>
<input class="form-control " data-val="true" data-val-required="Percent is required" data-val-number="The field Code discount must be a number."
data-val-range="The field Percent should be between 1 and 60." data-val-range-max="60" data-val-range-min="1"
data-val-regex="The field Percent should be a positive non-decimal number."
data-val-regex-pattern="^-?\d+$" data-val-required="The Code discount field is required."
id="Discounts_{{: index}}__Percent" name="Discounts[{{: index}}].Percent" type="text" value="0" aria-required="true" aria-invalid="false"
aria-describedby="Discounts_{{: index}}__Percent-error">
<span class="help-block field-validation-valid" data-valmsg-for="Discounts[{{: index}}].Percent" data-valmsg-replace="true"></span>
</div>
</div>
<div class="col-md-2 form-group">
<div class="input-group">
<button type="button" class="btn btn-primary removeDiscountRow">Remove</button>
</div>
</div>
</div>
</script>
As you can see I just copy output of razor and insert it in template. So if I change validation in model I'll change template each time. How to generate this template automatic with preserving all data attributes for client-side validation ?
You can generate templte code like you create your input code, but Model.Discounts must have at least one element. See code below. I add DiscountCode to discounts if its empty and change some html attributes to make template display as you want;)
if (Model.Discounts == null || Model.Discounts.Count <= 0)
{
Model.Discounts = new List<DiscountCode> { new DiscountCode() };
}
<script id="discountRow" type="text/x-jsrender">
<div class="row">
<input type="hidden" name="Discounts.Index" value="{{: index}}" />
<div class="col-md-4 form-group">
<div class="input-group">
#Html.LabelFor(m => m.Discounts[0].Percent, new { #class = "control-label", For = "Discounts[{{: index}}].Code" })
#Html.TextBoxFor(m => m.Discounts[0].Code, new { #class = "form-control ", Id = "Discounts_{{: index}}__Code", Name = "Discounts[{{: index}}].Code", Value="" } )
#Html.ValidationMessageFor(m => m.Discounts[0].Code, string.Empty, new { #class = "help-block", Data_Valmsg_For = "Discounts[{{: index}}].Code" })
</div>
</div>
<div class="col-md-6 form-group">
<div class="input-group">
#Html.LabelFor(m => m.Discounts[0].Percent, new { #class = "control-label", For = "Discounts[{{: index}}].Percent" })
#Html.TextBoxFor(m => m.Discounts[0].Percent, new { #class = "form-control ", Id = "Discounts_{{: index}}__Percent", Name = "Discounts[{{: index}}].Percent", Value = "" })
#Html.ValidationMessageFor(m => m.Discounts[0].Percent, string.Empty, new { #class = "help-block", Data_Valmsg_For = "Discounts[{{: index}}].Percent" })
</div>
</div>
<div class="col-md-2 form-group">
<div class="input-group">
<button type="button" class="btn btn-primary removeDiscountRow">Remove</button>
</div>
</div>
</div>
</script>

Categories

Resources