I have a form, to submit a bid.
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "Login" }))
{
#Html.ValidationSummary(true, "Gelieve alle velden in te vullen.")
#Html.LabelFor(m => m.Bid)<br />
#Html.TextBoxFor(m => m.Bid)<br />
#Html.LabelFor(m => m.Name)<br />
#Html.TextBoxFor(m => m.Name)<br />
#Html.LabelFor(m => m.Email)<br />
#Html.TextBoxFor(m => m.Email)<br />
#Html.HiddenFor(model => model.Car_id, new { value = ViewBag.car.id })
<input type="submit" value="Bied" class="button" />
}
And I want to set the value of the hiddenfor to the id of the car (I get it with the viewbag), but it doesn't work as seen here:
<input data-val="true" data-val-number="The field Car_id must be a number." data-val-required="Het veld Car_id is vereist." id="Car_id" name="Car_id" type="hidden" value="" /> <input type="submit" value="Bied" class="button" />
What is the correct way of doing this? Or are there other ways of passing a value to my code? I just need the Car_id in the Postback method..
even thought what Raphaƫl Althaus said is correct using the hard coded string is always a pain during refactoring. so try this
#{
Model.Car_id = ViewBag.car.id;
}
#Html.HiddenFor(model => model.Car_id)
by this way it will still be part of your model and lot more cleaner.
either Car_id is not a part of your model, then can't use HiddenFor, but have to use Hidden
something like
#Html.Hidden("Car_id", ViewBag.car.id)
assuming you've got something in ViewBag.car.id, the error you get seems to mean that there's nothing in there.
or it's part of your model and you shouldn't need a ViewBag. You should just set its value in the action related to that View.
#Html.HiddenFor(model => model.Car_id, htmlAttributes: new { #Value = ViewBag.Car_id })
Related
Trying to implement data validation and error handling on a simple contact form. When I add the check for ModelState.IsValid I'm in a chicken and egg situation. I have looked at other similar questions and am just not getting this. Moving from Web Forms to MVC and struggling. Trying to toggle HTML elements based on what's happening - success/error message, etc. RIght now, not even the validation is working.
Right now I'm just trying to get server-side validation working but would welcome advice on how to add client-side validation also; for example, is it necessary to use jQuery for this or is there something baked in?
View:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
if (ViewData["Error"] == null && ViewData["Success"] == null)
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
<input type="text" name="email" placeholder="Email..."
class="contact-email" id="contact-email">
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
<input type="text" name="subject" placeholder="Subject..."
class="contact-subject" id="contact-subject">
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
<textarea name="message" placeholder="Message..."
class="contact-message" id="contact-message"></textarea>
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
else if (ViewData["Error"] == null && ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
else if (ViewData["Error"] != null)
{
<h3>Oops!</h3>
<p>
We apologize. We seem to be having some problems.
</p>
<p>
Please come back and try again later. Alternatively,
call us anytime at (xxx) xxx-xxxx.
</p>
}
}
Model:
public class ContactModel
{
[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
[Required(ErrorMessage = "Subject is required")]
public string Subject { get; set; }
[Required(ErrorMessage = "Message is required")]
public string Message { get; set; }
}
Controller:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ViewData["Error"]
= "Something went wrong - please try again later.";
return View("Error");
}
}
return View();
}
Error View:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
UPDATE - 05/09/2017
Per Guruprasad's answer, if ModelState.IsValid evaluates to false, then no validation error messages are being reported on the form.
Note I had to change the AddModelError signature to not use the "Extension ex" parameter:ModelState.AddModelError("Error", "Server side error occurred"); as I do not want system errors being reported to users.
Note also that at this point I am only trying out validation on the server side (have yet to work through client-side validation).
I have updated the Contact.cshtml view as follows as no model errors were being displayed - I have included the Bootstrap .has-error and .help-block CSS rules for the validation errors:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group has-error">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email",
placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(model => model.Email, String.Empty,
new { #class="help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text",
name = "subject",
placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(model => model.Subject, String.Empty,
new { #class = "help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message",
placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(model => model.Message, String.Empty,
new { #class = "help-block" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to
call us at (xxx) xxx-xxxx at any time.
</p>
}
}
There are multiple things you need to understand here. Let me go point by point.
Its good to know that you have your model designed, but how your view gets to know that it has a model to bind for itself and when posting the form contents, how would server comes to know that, there is a model to be received. So on the first instance, you need to construct your view binding the model. To bind a model in a view, you need to first get a reference/declare it at the top, letting view know that, ok, here is a model for you to generate my view.
Well, you have ValidationSummary to true, then I would suggest that, instead of using ViewData to pass error message, you can use ModelState.AddModelError and let ValidationSummary take care of that. As a side note, you might also want to take care of this issue and you can resolve the same with answers mentioned in the same post. If you are not using or do not want to use Html.ValidationSummary, then you can stick to your current view.
Now, to display Success message, you can either use TempData or ViewData and follow the same structure as you have in your view now. Here is one more post to let you work on that.
Last and most important on View part is binding model properties to View elements. Use Razor View extension helpers to generate View for your model. You have #Html.TextBoxFor,#Html.TextAreaFor etc., You also have #Html.TextBox, #Html.TextArea which is not for binding model properties, but just to generate a plain HTML view. You can add other html properties within these helpers as shown in the updated view below. I would suggest to dig down more on the overloads available for these helpers.
So here is your updated view.
#model SOTestApplication.Models.ContactModel #*getting model reference*#
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#*Usage of helpers and html attributes*#
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
Controller Side validation
Not much to say on this part as it looks good. But based on few of my points above, I would suggest you to add ModelState.AddModelError instead of using ViewData for error messages. Eliminate your if conditions in view, so that contact form remains, even after postback. Now if you want to persist the values after server side validation, then just pass back the model to your view in your post method. Updated Controller would be:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ModelState.AddModelError("Server side error occurred", ex.Message.ToString());
}
}
return View(contactModel); //this will persist user entered data on validation failure
}
Client Side Validation
As far as this portion is considered, you have few more things to set up in your application.
You need to add Html.EnableClientValidation(true); and Html.EnableUnobtrusiveJavaScript(true); to your application. There are various possible ways to add this. You can add this on Web.config file under appSettings for global implication Or you can add this in particular view as mentioned in below updated View example.
Global Implication in Web.Config ex:
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
If you have noticed your BundleConfig.cs file under App_Start directory, you would have seen below entries created by default. These are the jquery stuffs responsible for your Client Side validation.
jQuery and jQueryVal entries
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
Next Step is to add reference to these files/use #section Scripts to render these bundles either in _Layout.cshtml or in any specific view. When you include this in _Layout.cshtml. these scripts/bundles are rendered wherever you use this layout with other views. So basically, its your call on where to render these.
For example here, I would render these in Contact.cshtml view soon after adding reference to model.
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
One Last thing to make this work here is that you need to use #Html.ValidationMessageFor razor extension and let MVC do the binding of error messages on particular properties. Also for these error messages to be displayed in the View, you need to specify ErrorMessage for each property in your model as you are doing it now with Required(ErrorMessage=... for each properties in model. There are more to know about these stuffs if you explore it in detail.
Your updated view with proper validations added.
#model SOTestApplication.Models.ContactModel
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
#using (Html.BeginForm("Contact", "Contacts", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(m => m.Message)
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
}
Hope I have clarified most of your doubts with these points. Happy Coding.. :)
Hi I am trying to code a simple blog with using ASP.NET MVC 5 Framework. I have done CRUD operations of Posts. I mean I can add new articles and manage them. When I wanted to add comments to articles, I stuck. Comments will be added to Details pages of articles. So I should add Create comment page to Details page.
I used Code First model and I have two models. Articles and Comments. I decided to use partial views to enter comments. But result is an error:
The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.Article but this dictionary requires a model item of type 'Blog.Models.Comment'. Comments can not be added. I created 2 PartialViews, one of them is _CreateComments PartialView and other one is _Index PartialView
Details View:
#model Blog.Models.Article
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>Article</h4>
<hr />
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.Title)
</dt>
<dd>
#Html.DisplayFor(model => model.Title)
</dd>
<dt>
#Html.DisplayNameFor(model => model.Author)
</dt>
<dd>
#Html.DisplayFor(model => model.Author)
</dd>
<dt>
#Html.DisplayNameFor(model => model.Date)
</dt>
<dd>
#Html.DisplayFor(model => model.Date)
</dd>
<dt>
#Html.DisplayNameFor(model => model.ArticleContent)
</dt>
<dd>
#Html.DisplayFor(model => model.ArticleContent)
</dd>
</dl>
</div>
<div class="jumbotron">
#Html.Partial("~/Views/Comments/_CreateComments.cshtml", new Blog.Models.Comment())
#Html.Partial("~/Views/Comments/_Index.cshtml", new List<Blog.Models.Comment> { new Blog.Models.Comment() })
</div>
_CreateComment PartialView
#model Blog.Models.Comment
#using (Html.BeginForm("Create"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Comment</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Date, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CommentContent, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CommentContent, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CommentContent, "", new { #class = "text-danger" })
</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>
_Index PartialView
#model IEnumerable
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Date)
</th>
<th>
#Html.DisplayNameFor(model => model.CommentContent)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.CommentContent)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CommentId }) |
#Html.ActionLink("Details", "Details", new { id=item.CommentId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CommentId })
</td>
</tr>
}
</table>
You have to use a ViewModel for that purpose. In POST, it will get the needed information. And in GET you will send the needed information.
For example: in GET you will send the article and needed areas. But in POST you will get the comment and commenter's name and date etc.
There are numerous issues with your code.
#using (Html.BeginForm()) means it will post back to the
Details() method (assuming that's the action which generated the
view), so it would need to be #using (Html.BeginForm("Create"))
The controls your generating will have name attributes such as
name="Item2.Date" which have no relationship to your Comment
class (Comment does not have a property named Item which is a
complex object with a property named Date)
The default value of a DateTime property is 1.1.0001 00:00:00
meaning that you have not initialized the value (e.g. Date =
DateTime.Today;) but its not clear why you would need the user to
enter a date anyway - surely that would be set to today's date in
the controller immediately before you save the comment.
You have not indicated what scripts
#Scripts.Render("~/bundles/jqueryval") is generating, but assuming
you using the jQueryUI datepicker, you will need at least
jquery-{version}.js and jquery-ui-{version}.js (plus jquery.validate.js and jquery.validate.unobtrusive.js for validation)
You have a table in your form which will not display anything other
that the initial values of a new Comment (and exactly the same as
is being displayed in the textboxes) so it seems a bit pointless
Note also your view does not display any existing comments for an article which seems unusual.
There a numerous ways so solve this including ajax so the user could stay on the same page and continue to add more comments, but based on what appears to be your current UI, then the model in Details view should be just Article and use a partial to render a form for a new Comment
Controller
public ActionResult Details(int? id)
{
....
Articles article = db.Articles.Find(id);
....
return View(article);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Comment model)
{
if (ModelState.IsValid)
{
model.Date = DateTime.Today; // set date before saving
db.Comments.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
....
}
View
#model yourAssembly.Article
// Render properties of Article
....
// Add form for creating a new comment
#Html.Partial("_Comment", new yourAssembly.Comment)
// Add required scripts including jquery ui
and the partial view (_Comment.cshtml)
#model yourAssembly.Comment
#using (Html.BeginForm("Create"))
{
#Html.LabelFor(m => m.Content, new { #class = "control-label col-md-2" })
#Html.TextBoxFor(m=> .m.Content, new { #class = "form-control" } })
#Html.ValidationMessageFor(m=> m.Content, new { #class = "text-danger" })
<input type="submit" value="Create" class="btn btn-default" />
}
Side notes:
Do not use Tupple in your views, especially for a view that
involves editing since it generates name attributes which have no
relationship to your model. Always use view models.
You do not generate a control for the CommentId property so there
is no point including it in you [Bind] attribute (and in fact
means someone could post back a value and result in your code
throwing an exception.
Using <dl>, <dt> and <dd> tags are not appropriate in your
view (they are for A description list, with terms and
descriptions). Use <div> and <span> elements.
I need to use the required attribute, so the field shoud not be empty
<input type="text" class="form-control" id="inputName" required>
in the razor view, more specifically in this example
#Html.EditorFor(model => model.exclure.libelle, new { htmlAttributes = new { #class = "form-control" } })
You can specify in htmlAttributes with TextBoxFor() this way:
#Html.TextBoxFor(m=>m.exclure.libelle,new{ #class = "form-control",required="required" })
When I use
#{Html.RenderPartial("Login");}
inside my main view, the #Html.ValidationSummary() doesn't work, but when I copy the code from "Login" inside main view, it works.
Why is that, and how do I display validation messages from the partial view?
Here is partial view "Login":
#model NyNo.Models.LoginModel
#using (Html.BeginForm())
{
<fieldset>
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.TextBoxFor(m => m.UserName, new { #placeholder = "Username" })
#Html.ValidationMessageFor(m => m.UserName)
#Html.PasswordFor(m => m.Password, new { #placeholder = "Password" })
#Html.ValidationMessageFor(m => m.Password)
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe, new { #class = "checkbox" })
<input type="submit" class="button" value="Log in" />
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Hope you understand, thanks!
Unfortunately, this cannot work. A Partial is just a string.
While RenderPartial actually 'writes' the partial markup rather than sending a string back to the View Generator, it does not rebind your View to a new model. If you want Validation Summary to work it must be bound to a model in your main View.
Your problem could be related to this (maybe you aren't showing the ViewData passed in the RenderPartial()): Pass Additional ViewData to an ASP.NET MVC 4 Partial View While Propagating ModelState Errors
I was having a similar issue and I solved it this way: ValidationSummary inside a partial view not showing errors
I just want to add a placeholder for my generated input element.
This is what being generated
<input class="text-box single-line" data-val="true" data-val-required="The Username field is required." id="Username" name="Username" type="text" value="">
This is the code that generates HTML output
#Html.EditorFor(model => model.Username)
Of course I can just remove the generated code and write it by myself just including the necessary fields, however maybe it is possible to just pass some kind of parameter to EditorFor that will add placeholder attribute to it?
You can use #Html.TextBoxFor instead of #HtmlEditorFor and then write htmlAttributes like this:
#Html.TextBoxFor(model => model.Username, new { placeholder = "Username..." })
Try
#Html.EditorFor(m => model.Username, new { data_val_attribute = "value" });
Most of the helpers will have an overload that accepts an anonymous object as the last parameter for specifying attributes in the generated markup.
#Html.EditorFor(model => model.Username, new { placeholder = "Username..." }) should work, unless your model is not empty. Have you checked if your model is empty?