I have the following code:
In the model:
public class Student {
[Required(ErrorMessage = "name is a required field")]
public string Name { get; set; }
[Required(ErrorMessage = "school is a required field")]
public string School { get; set; }
}
In the controller:
public ActionResult StudentView()
{
return View();
}
[HttpPost]
public ActionResult StudentViewPost(Student model)
{
if(ModelState.IsValid)
{
....
....
}
}
And in my view, i have:
#using (Html.BeginForm()){
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
#Html.TextBoxFor(model => model.School)
#Html.ValidationMessageFor(model => model.School)
}
But when i go to the view page, the validation error messages are already displayed (on load), even before i get a chance to enter any input. Is there any reason why this could be happening? Could .NET be seeing this GET page as a POST page on load somehow and therefore display the error message? I'm not sure why this is happening and any thoughts/ideas would be great.
I have seen some issues in your Html.Beginform()
Normally you should write this begin blog as follow,
Example if your controller name is Student then,
#using (Html.BeginForm("StudentViewPost", "Student", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
#Html.TextBoxFor(model => model.School)
#Html.ValidationMessageFor(model => model.School)
}
You have not specifed the form method which will invoke the validations on Action
use following syntax
#using (Html.BeginForm("ActionName", "Controller", FormMethod.Post))
{
}
Related
I am trying to understand partial views in MVC...
What I am trying to accomplish is to have a master View which renders, say, two partial views.
Each partial view contains a different ViewModel (with DataAnnotations). When I submit the form of one of those partial views, in case there is a server-side validation error, I would like to have the master View show up again with the validation messages on that partial.
Any tips in the right way would be deeply appreciated.
Here you go with the sample solution -
Let create a complex model in following way -
public class Person
{
public Contact contact { get; set; }
public Vehicle vehicle { get; set; }
}
public class Contact
{
[Required]
public string Email { get; set; }
}
public class Vehicle
{
[Required]
public string Name { get; set; }
}
Then lets create a Main controller with an Index action in following way, this action is going to create a simple dummy model and bind it to the Index view -
public class MainController : Controller
{
public ActionResult Index()
{
Person p = new Person();
p.contact = new Contact();
p.vehicle = new Vehicle();
return View(p);
}
}
And Index view is going to be -
#model MVC.Controllers.Person
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("Submit","Main",FormMethod.Post))
{
#Html.EditorFor(x => x.contact, "~/Views/Main/EditorTemplates/Contact.cshtml")
#Html.EditorFor(x => x.vehicle, "~/Views/Main/EditorTemplates/Vehicle.cshtml")
<input type="submit" value="click"/>
}
Here in the above view, instead of using Partial Views, I used Editor Views. Reason is that Partial views gives very hard experience in Model binding the Complex models.
So I created EditorTemplated folder in Main View folder and placed following files in there.
Contact.cshtml -
#model MVC.Controllers.Contact
#Html.LabelFor(model => model.Email, new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
Vehicle.cshtml -
#model MVC.Controllers.Vehicle
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
With the above setup, we can go and run the application and following screen should show up -
And this form is going to POSTed to Submit Action of Main controller, so this is going to be my submit action -
public ActionResult Submit(Person p)
{
if (!ModelState.IsValid)
return View("Index", p);
else
{
// do something
return View();
}
}
When we click button without entering any value, then validation will trigger and we should see error messages as below -
And in normal valid situations, you can submit the form and then run your business logic.
I am uploading and image and verifying its validity as per this post here:
How to validate uploaded file in ASP.NET MVC?
However, my example differs slightly because I am not just receiving the file, I am also receiving some properties for my model. However, my validator always fires, I debugged and found that my file is always null, so validator always fires back 'false'. I don't understand why, my input in view seems to be correct. Any ideas?
namespace PhotoManagement.Models
{
public class Photo
{
public virtual int PhotoId { get; set; }
public virtual int ClientId { get; set; }
public virtual string PhotoDescription { get; set; }
[ImageValidation(ErrorMessage="Please select a PNG/JPEG image smaller than 10 MB")]
[NotMapped]
public HttpPostedFileBase File { get; set; }
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Photo photo)
{
if (ModelState.IsValid)
{
db.Photos.Add(photo);
db.SaveChanges();
// File upload occurs now
var FilePath = Path.Combine(Server.MapPath("~/App_Data/" + photo.ClientId), photo.PhotoId.ToString());
photo.File.SaveAs(FilePath);
return RedirectToAction("Create");
}
else return View();
}
#using (Html.BeginForm(new { enctype = "multipart/form-data" })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Photo for #Session["Name"]</legend>
<div class="editor-field">
#Html.Hidden("ClientId",(int)Session["UserId"])
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PhotoDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PhotoDescription)
#Html.ValidationMessageFor(model => model.PhotoDescription)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.File)
</div>
<div class="editor-field">
<input type="file" name="File" id="File"/>
#Html.ValidationMessageFor(model => model.File)
</div>
You are using a wrong overload of the Html.BeginForm helper.
The correct call is this:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
}
You were calling:
Html.BeginForm(object routeValues)
instead of:
Html.BeginForm(
string actionName,
string controllerName,
FormMethod method,
object htmlAttributes
)
Look at the generated markup in your browser and you will see the fundamental difference.
Instead of
public ActionResult Create(Photo photo)
Try
public ActionResult Create(Photo photo, HttpPostedFileBase file)
EDIT: Don't forget to set the HTTP method to POST in the view:
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { enctype = "multipart/form-data" }))
The file in Model will always give you null. In order to get file :
[HttpPost]
public ActionResult Create(UserViewModel model,
FormCollection formCollection, HttpPostedFileBase file){
/* Your code here */
if(file==null)
{
ModelState.AddModelError("NoFile", "Upload File");
}
}
Here HttpPostedFileBase file will give you the complete object of file uploaded. You can have check condition on the object file. Don't forget to add the below mentioned validation message in view.
#Html.ValidationMessage("NoFile")
I'm trying to create ASP.NET MVC Application with Entity Framework, which has One to Many relationship. For that I have successfully managed to load the appropriate list of items to a dropdown control (in Create view), But when I click the Create button (in Create view) page validation is faild, validation error message is The value '1' is invalid..
Error
Model
public class Post
{
public int Id { get; set; }
...
public virtual Person Author { get; set; }
}
DataBaseContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Post>()
.HasOptional(p => p.Author);
}
Controller
public ActionResult Create()
{
PopulateAuthorDropDownList();
return View();
}
[HttpPost]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("Index");
}
PopulateAuthorDropDownList(post.Author);
return View(post);
}
private void PopulateAuthorDropDownList(object selectedPerson = null)
{
var personQuery = from d in db.People
orderby d.Name
select d;
ViewBag.Author = new SelectList(personQuery, "Id", "Name", selectedPerson);
}
View
<div class="editor-label">
#Html.LabelFor(model => model.Author)
</div>
<div class="editor-field">
#Html.DropDownList("Author", String.Empty)
#Html.ValidationMessageFor(model => model.Author)
</div>
When I check Author table in database there is Record with Id 1, so I guess 1 is a valid value. What am I missing here?
Thanks in advance...
You didn't show how the Author object looks like,
Suppose if it is like this,
public class Author
{
public int Id{get;set;}
public string Name{get;set;}
}
Try this,
<div class="editor-label">
#Html.LabelFor(model => model.Author)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Author.Id, ViewBag.Author, "Select an Author")
#Html.ValidationMessageFor(model => model.Author)
</div>
Managed to fix this by
changing Model to
public class Post
{
public int Id { get; set; }
...
public int Author { get; set; } // changed type to int
}
changing View to
<div class="editor-label">
#Html.LabelFor(model => model.Author)
</div>
<div class="editor-field">
#Html.DropDownList("AuthorId", String.Empty)
#Html.ValidationMessageFor(model => model.Author)
</div>
changing Controller to
private void PopulateAuthorDropDownList(object selectedPerson = null)
{
var personQuery = from d in db.People
orderby d.Name
select d;
ViewBag.AuthorId = new SelectList(personQuery, "Id", "UserName", selectedPerson); //Changed Author to AuthorId
}
and removing
modelBuilder.Entity<Post>()
.HasOptional(p => p.Author);
from DataBaseContext
Anyway Thanks for the answers... :)
You should have something like this:
<div class="editor-label">
#Html.LabelFor(model => model.Author.Name)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Author.Name, (SelectList)ViewBag.AuthorList)
#Html.ValidationMessageFor(model => model.Author.Name)
</div>
Note that you should name ViewBag.AuthorList instead of ViewBag.Author
You have to provide a list of options to DropDownList where you are just passing string.empty.
And i You should use the strongly typed version DropDownListFor:
#Html.DropDownListFor(model => model.Author.Name, Model.AuthorList)
Or maybe even better by not using the name but the Id:
#Html.DropDownListFor(model => model.Author.Id, Model.AuthorList)
And your model:
class Model {
...
public SelectList AuthorList {get;set;}
}
And in your controller:
model.AuthorList = new SelectList(fetchAuthors()
.Select(a => new SelectListItem {
Text = a.Name,
Value = a.Id
}, "Value", "Text");
I'm stuck and after looking this up for hours, I think I need more eyeballs.
The situation is the following:
It's an Asp.Net MVC3 with Entity Framework 4 project. And I have two classes. One ConfigurationFile and another one Action. There is a one-to-many relationship between the two. Here is a simplified view on the code:
public class ConfigurationFile
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
[Column(TypeName = "uniqueidentifier")]
[Required]
public Guid ActionId { get; set; }
public virtual Models.Action Action { get; set; }
}
public class Action
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string ActionValue { get; set; }
}
Then I want to create a new ConfigurationFile, and are my two controller methods (and at this point, this is 95% Visual Studio 10 generated code):
// db is my context class.
//
// GET: /Configuration/Create
public ActionResult Create()
{
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue");
return View();
}
//
// POST: /Configuration/Create
[HttpPost]
public ActionResult Create(Models.ConfigurationFile configurationfile)
{
if (ModelState.IsValid)
{
configurationfile.Id = Guid.NewGuid();
db.ConfigurationFiles.Add(configurationfile);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue", configurationfile.ActionId);
return View(configurationfile);
}
And here is a snippet of my Create view:
#model MyProject.Areas.ConfigurationFile.Models.ConfigurationFile
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Configuration File</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ActionId, "Action")
</div>
<div class="editor-field">
#Html.DropDownList("ActionId", String.Empty)
#Html.ValidationMessageFor(model => model.ActionId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When I open the Create page, I can clearly see that my dropdown for the Action class is fine (correct value -- the Action.Id -- and text -- Action.ActionValue -- ) but when I submit the form, I have the following error: "The parameter conversion from type 'System.String' to type 'MyProject.Models.Action' failed because no type converter can convert between these types."
Help please !!
Right now MVC has no way of connecting your dropdownlist from your view to the ActionId of your ConfigurationFile object.
I would try replacing this line:
#Html.DropDownList("ActionId", String.Empty)
for this
#Html.DropDownListFor(model => model.ActionId, ViewBag.ActionId)
Other than that, I can't think of what else you might have done wrong.
I hope that helps!
This is how I did to circumvent the problem. I just changed my controller this way:
Models.Action act = db.Actions.Find(configurationfile.ActionId);
ModelState.Clear();
configurationfile.Action = act;
TryValidateModel(configurationfile);
And after that, the validation was Ok. A bit hacky (and another possible hit on the DB), but at least, I can keep going.
Suppose, I have models:
public class Person
{
public sting Name {get;set;}
public List<Book> Books {get;set;}
}
public class Book
{
public sting NameBook {get;set;}
}
How represent view for Edit method based on Person model (MVC 3)?
You may try something along the lines of:
#model Person
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
</div>
#Html.EditorFor(x => x.Book)
<button type="submit">Edit</button>
}
and then you will define an editor template for the Book type (~/Views/Shared/EditorTemplates/Book.cshtml) which will be rendered for each element of the Book property collection (which by the way you would have named Books in order to follow standard conventions) on your view model:
#model Book
<div>
#Html.LabelFor(x => x.NameBook)
#Html.EditorFor(x => x.NameBook)
</div>
As far as your controller actions are concerned, it's pretty standard stuff:
public ActionResult Edit(int id)
{
var person = _personRepository.Get(id);
return View(model);
}
[HttpPost]
public ActionResult Edit(Person person)
{
if (!ModelState.IsValid)
{
return View(person);
}
_personRepository.Update(person);
return RedirectToAction("Success");
}