mvc validationMessage is not working while its in IEnumerable<T> - asp.net-mvc

I have a class :
public class Cust
{
[Required(ErrorMessage ="NameField Req")]
public string Name { get; set; }
}
I use this class :
public class CustModel
{
public IEnumerable<Cust> CustList { get; set; }
}
.Cshtml
#model WebApplication2.Models.CustModel
#Html.EditorFor(m => m.CustList)
#Html.ValidationMessageFor(val => val.CustList)
When I hit on Submit button if (ModelState.IsValid){} is working but why my error message is not displayed ?

I managed to re-produce your code, assuming your view:
Please, pay attention to the for loop:
#model MVC_SteckOverflow.Models.ViewModels.CustModel
#using MVC_SteckOverflow.Models.ViewModels
#{
ViewBag.Title = "ValidateIenumerable";
}
<style>
.field-validation-error {
color:#F00;
}
.input-validation-error {
border: 1px solid red;
background-color: #f9e2e2b3;
}
</style>
<h2>ValidateIenumerable</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CustModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#for (int i = 0; i != Model.CustList.Count(); i++)
{
#Html.EditorFor(x => x.CustList.ToList()[i].Name)
#Html.ValidationMessageFor(x => x.CustList.ToList()[i].Name)
}
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section scripts{
#Scripts.Render("~/bundles/jqueryval")
}
according to the following actions in the Controller:
public ActionResult ValidateIenumerable()
{
CustModel custModel = new CustModel {
CustList = new List<Cust> {
new Cust{ Name = "A"},
new Cust{ Name = "B"},
new Cust{ Name = "C"},
},
};
return View(custModel);
}
[HttpPost]
public ActionResult ValidateIenumerable(CustModel custModel)
{
return View(custModel);
}
The code above, is tested, built and run successfully.
Hope this helped ... :)

Related

Using a one model to populate the dropdown for the view on another model

I've been striking out on how to handle this situation. I have a model, the project model, and it has numerous items that need to be dropdowns for the user to change the selected item in question. I started out simple with the ProjectType value, which should have the selectable values populated form the ProjectTypes table. Here is the ViewModel:
public class ProjectViewModel
{
public APT_Projects Project { get; set; }
public System.Linq.IQueryable<APT_ProjectTypes> projecttypes { get; set; }
public APT_ClientTypes ClientTypes {get; set;}
}
Here is the controller:
public ActionResult Edit(int id = 0)
{
APT_Projects apt_projects = db.APT_Projects.Find(id);
if (apt_projects == null)
{
return HttpNotFound();
}
ProjectViewModel Project = new ProjectViewModel();
var apt_projecttypes = from a in db.APT_ProjectTypes
select a;
Project.Project = apt_projects;
Project.projecttypes = apt_projecttypes;
return View(Project);
}
and finally the view:
#model APTII_MVC.ViewModel.ProjectViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>APT_Projects</legend>
<div class="editor-label" style="float: left; width: 500px;" >
#Html.LabelFor(model => model.Project.Project_ID)
</div>
<div class="editor-field" style="float: left; width: 500px;" >
#Html.EditorFor(model => model.Project.Project_ID)
#Html.ValidationMessageFor(model => model.Project.Project_ID)
</div>
<div class="editor-label"">
#Html.LabelFor(model => model.Project.ProjectType_ID)
</div>
<div class="editor-field"">
#Html.DropDownListFor(model => model.Project.ProjectType_ID, Model.projecttypes)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
When I do it in this manner, .net doesn't like the Drowpdownlistfor, but I'm unclear how to use these values in a manner that the dropdownlistfor would accept. What is my mistake?
Edit: Relevent Error for the Dropdownlistfor.
'System.Web.Mvc.HtmlHelper<ViewModel.ProjectViewModel>' does not contain a definition for 'DropDownListFor' and the best extension method overload 'System.Web.Mvc.Html.SelectExtensions.DropDownListFor<TModel,TProperty>(System.Web.Mvc.HtmlHelper<TModel>, System.Linq.Expressions.Expression<System.Func<TModel,TProperty>>, System.Collections.Generic.IEnumerable<System.Web.Mvc.SelectListItem>)' has some invalid arguments c:\Visual Studio 2012\Projects\Test_MVC\Test_MVC\Views\Project\Edit.cshtml
you might need a List<SelectListItem> or SelectList().. so try converting your projecttypes object to
#Html.DropDownListFor(model => model.Project.ProjectType_ID, new SelectList(Model.projecttypes,"ID","Type")
or edit your viewmodel to
public SelectList projecttypes { get; set; }
and your controller code to
Project.projecttypes = new SelectList(db.APT_ProjectTypes,"ID","Type");
just guessing on your value/text field names

How to make file upload required in asp.net mvc?

I am uploading file using asp.net mvc with file upload required but unable to upload file using this. How to make file upload required with validation using ASP.NET MVC?
Here is my Model class code.
public class Slider
{
public int SliderId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string FileURL { get; set; }
}
Here is my Create Controller:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create([Bind(Include = "SliderId,Title,FileURL")] HttpPostedFileBase file, Slider slider)
{
if (ModelState.IsValid)
{
if (file != null)
{
string fil = System.IO.Path.GetFileName(file.FileName);
string path = System.IO.Path.Combine(Server.MapPath("~/Content/Uploads/Slider/"), fil);
file.SaveAs(path);
slider.FileURL = "/Content/Uploads/Slider/" + file.FileName;
}
db.Sliders.Add(slider);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(slider);
}
Here is my View:
#model Test.Models.Slider
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Create", "SliderManager", FormMethod.Post, new { enctype = "multipart/Form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Create</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title,"Title*", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<label for="file">Upload Image for Slide*:</label>
</div>
<div class="col-md-10">
<input type="file" name="file" id="file" style="width:50%" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Changes to make
1st model
public class Slider
{
public int SliderId { get; set; }
[Required]
public string Title { get; set; }
public string FileURL { get; set; }
}
Removed required on file Url as this is not coming from user but you should be populating it
2nd Upload action
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create(HttpPostedFileBase file, Slider slider)
{
//Add validation if file is not present and fail model
if (file == null)
{
ModelState.AddModelError("FileURL", "Please upload file");
}
if (ModelState.IsValid)
{
if (file != null)
{
string fil = System.IO.Path.GetFileName(file.FileName);
string path = System.IO.Path.Combine(Server.MapPath("~/Content/Uploads/Slider/"), fil);
file.SaveAs(path);
slider.FileURL = "/Content/Uploads/Slider/" + file.FileName;
}
//db.Sliders.Add(slider);
//db.SaveChanges();
return RedirectToAction("Index");
}
return View("~/Views/Home/Index.cshtml", slider);
//return View(slider);
}
Also I am not sure why you have specified additional bindings, but I guess you had some reason for that
3rd the view
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/Form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Create</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title, "Title*", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<label for="file">Upload Image for Slide*:</label>
</div>
<div class="col-md-10">
<input type="file" name="file" id="file" style="width:50%" />
#Html.ValidationMessageFor(x=>x.FileURL)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
I have added validation message. This validation message of course can have its own property and I would modify it so it fits with your business logic.
<input type="file" name="file" id="file" style="width:50%" />
#Html.ValidationMessageFor(x=>x.FileURL)
I think the solution from this tutorial is better because it doesn't need an extra property:
https://www.aspsnippets.com/Articles/Fileupload-validation-using-Model-Data-Annotations-in-ASPNet-MVC.aspx
Model:
public class FileModel
{
[Required(ErrorMessage = "Please select file.")]
public HttpPostedFileBase PostedFile { get; set; }
}
View:
#model FileUpload_Validation_MVC.Models.FileModel
#{
Layout = null;
}
<div>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<span>Select File:</span>
#Html.TextBoxFor(m => m.PostedFile, new { type = "file"})
<br/>
#Html.ValidationMessageFor(m => m.PostedFile, "", new { #class = "error" })
<hr/>
<input type="submit" value="Upload"/>
}
</div>

posting data from partial view on main view then submitting to controller

I have a MVC model as follows
public class ListSampleModel
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int SampleId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<PointOfContact> lstPoc { get; set; }
}
public class PointOfContact
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int PocID { get; set; }
public string EmailAddress { get; set; }
public string PhoneNumber { get; set; }
}
What I have done is, create "PointOfContact" as a partial view on a jquery dialog and on "save" button click it shows the data on the main view in labels (I will have multiple point of contacts), now on submit I want this data along with the property values of ListSampleData to be posted back to the controller.
The issue is, the data related to simple properties are returned back but the list is always null.
below is my View
#model MVCDataFlowSample.Models.ListSampleModel
#using (Html.BeginForm("Create", "ListSample", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>ListSampleModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div id="dialog1" class="ui-dialog" style="background-color:gray;"></div>
<div id="data">
</div>
<p>
<input type="button" value="Add More..." id="btnAdd" />
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Javascript on Main View
<script type="text/javascript">
$(document).ready(function () {
$('#btnAdd').on('click', function () {
$('#dialog1').dialog({
autoOpen: true,
position: { my: "center", at: "center", of: window },
width: 1000,
resizable: false,
title: 'Add User Form',
modal: true,
open: function () {
$(this).load('#Url.Action("PocPartial", "ListSample")');
},
buttons: {
"Save User": function () {
addUserInfo();
$(this).dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
}
});
return false;
});
function addUserInfo(email, phone) {
var text = "<div id='EmailAddress'>Email Address:" + $("#EAddress").val() + "</div><div id='PhoneNumber'>Phone Number:" + $("#PhNo").val() + "</div>";
$("#data").append(text);
}
});
</script>
Partial View
#model MVCDataFlowSample.Models.PointOfContact
<div>
#Html.Label("EmailAddress:")
<div>
#Html.TextBoxFor(x => x.EmailAddress, new { id = "EAddress" })
</div>
#Html.Label("PhoneNumber:")
<div>
#Html.TextBoxFor(x => x.PhoneNumber, new { id = "PhNo" })
</div>
</div>
any help will be appreciated.
The contents of DIV elements are not submitted as form data. If you'd like that data to be submitted, add it to the DOM as hidden INPUT elements in addition to your DIVs. You'll also need to format their names correctly so that MVC knows how to bind them. See this article for how complex objects are bound in MVC.
I posted partial view data to the main page's post action You can modify the idea to any of your suits
Partial View
<select name="Country">
<option>Indian</option>
<option>Africa</option>
</select>
<select name="State">
<option>Kerala</option>
<option>TamilNadu</option>
</select>
<select name="City">
<option>Thrissur</option>
<option>Palakkad</option>
</select>
Index Page
#{
ViewBag.Title = "IndexTestPost";
}
<h2>IndexTestPost</h2>
#using(Html.BeginForm()){
#Html.Partial("~/Views/_PartialPagePostCountry.cshtml");
<input type="submit" />
}
Class To Catch Post Data
public class CountryCityState
{
public string Country { get; set; }
public string State { get; set; }
public string City { get; set; }
}
Controller
public class TestPostPartialController : Controller
{
// GET: TestPostPartial
public ActionResult IndexTestPost()
{
return View();
}
[HttpPost]
public ActionResult IndexTestPost(CountryCityState CtnCtySta)
{
return View();
}
}

Add ID from URL in MVC

Im just starting out with MVC and i'm in the learning phase. I'm currently stuck with this problem. In my soulution i got these two tables.
TopicTable
-TopId
-TopName
ContentTable
-ContId
-TopId
-Content
I want while i'm in the Topic Detailes-view to be able to create new content.
In the detailes.cshtml file i've added:
#Html.ActionLink("Create New Content", "Create", "Content", new { ID = Model.TopicId}, null)` which will display in URL: localhost/Content/Create/1
In my ContentController ive got this, and i get this error when submitting "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details."
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(int Id)
{
ContentModel content = new ContentModel();
if (ModelState.IsValid)
{
content.TopId = Id;
db.ContentModel.Add(content);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(content)
}
The View:
#model WebApplication2.Models.ContentModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>JobOfferModel</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Content, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
</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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Model:
public class Topic
{
[Display(Name = "Id")]
public virtual int TopicId{ get; set; }
[Display(Name = "Name")]
public virtual string TopicName{ get; set; }
}
public class Content
{
[Display(Name = "Id")]
[Display(Name = "Id")]
public virtual int ContentId{ get; set; }
[Display(Name = "TopicId")]
public virtual string TopicId{ get; set; }
[AllowHtml]
[Display(Name = "Innihald")]
public virtual string Content { get; set; }
}
So when i'm creating new content the topId value will automaticly be added to the content table.
Would love some help,, Gracias
#Html.ActionLink() is a call to a GET method. You need a GET to return a view that renders the form, and a POST method to receive and save the form data
public ActionResult Create(int ID)
{
Content model = new Content();
model.TopicId = ID; // note TopicId should be int, not string
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Content model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// save your model and redirect.
}
View (divs etc omitted)
#model WebApplication2.Models.Content
....
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(m => m.TopicId) // for postback
#Html.LabelFor(model => model.Content, new { #class = "control-label col-md-2" })
#Html.TextBoxFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
<input type="submit" value="Create" class="btn btn-default" />
}
....

ASP.NET MVC 3 Binding to a Collection inside an Object

I have a model with an object that contains a collection like this:
namespace API.Example.Models
{
public class OrderTest
{
public string UserName { get; set; }
public string Token { get; set; }
public POCO.Order Order { get; set; }
}
}
namespace Supertext.API.POCO
{
public class Order
{
public List<TranslationGroup> Groups = new List<TranslationGroup>();
}
public class TranslationGroup
{
public string GroupId { get; set; }
}
}
The Order object contains a collection called Groups.
In the view I display the collection like this (with the index like explained in several examples)
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
#for (int i = 0; i < Model.Order.Groups.Count; i++)
{
#Html.LabelFor(m => Model.Order.Groups[i].GroupId)
#Html.TextBoxFor(m => Model.Order.Groups[i].GroupId)
}
And this is the Controller method that gets called:
[HttpPost]
public ActionResult Index(Models.OrderTest model)
The HTML of the UserName element:
<input id="UserName" name="UserName" style="width:300px;" type="text" value="">
and the GroupId element:
<input id="Order_Groups_0__GroupId" name="Order.Groups[0].GroupId" type="text" value="1">
I can access the UserName, but there is nothing in the collection.
What am I missing?
And whats the difference between using m.UserName and Model.Order.Groups (I mean m and Model). Is that my issue?
Each property of POCO entity use like a property managed by the CLR. Let the CLR manage the create of instance and etc.. or you can generate conflicts that can throw issues like you have.
Change your Order code to this:
public class Order
{
public List<TranslationGroup> Groups { get; set; }
}
--EDIT
I create a new project and add the following class:
public class TranslationGroup
{
public string GroupId { get; set; }
}
public class Order
{
public List<TranslationGroup> Groups { get; set; }
}
public class OrderTest
{
public string UserName { get; set; }
public string Token { get; set; }
public Order Order { get; set; }
}
Here my code behind of the OrderTestController:
public ActionResult Index()
{
var orderTest = new Models.OrderTest()
{
UserName = "Vinicius",
Token = "12as1da58sd558",
Order = new Models.Order()
{
Groups = new List<Models.TranslationGroup>()
{
new Models.TranslationGroup() { GroupId = "a54s"},
new Models.TranslationGroup() { GroupId = "a87d"},
new Models.TranslationGroup() { GroupId = "2gf4"}
}
}
};
return View(orderTest);
}
[HttpPost]
public ActionResult Index(Models.OrderTest model)
{
return View();
}
And Index View:
#model TestCollection.Models.OrderTest
#{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>OrderTest</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Token)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Token)
#Html.ValidationMessageFor(model => model.Token)
</div>
#for (int i = 0; i < Model.Order.Groups.Count; i++)
{
<div class="editor-label">
#Html.LabelFor(m => Model.Order.Groups[i].GroupId)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => Model.Order.Groups[i].GroupId)
</div>
}
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
So, if you run, and go to OrderTest view, you 'll see all attributes filled, and when you click in create, all things will be binded (the collection as well).

Resources