whenever I am submitting the form without entering the required fields instead of giving an immediate client-side validation error it is going to the Httppost Actionresult Index method allowing to submit the form. After roundtrip to server-side then giving errors. I have added reference of jquery.validate.js, unobtrusive.js both libraries, and also set ClientValidationEnabled and UnobtrusiveJavaScriptEnabled value true inside web.config. YOUR KIND HELP WILL BE HIGHLY APPRECIATED
HTML :
#model binaryquest.web.CustomModels.ContactVM
#{
ViewBag.Title = "Home Page";
}
<div id="contact" class="form">
<div class="container">
<div class="row">
<div class="col-lg-12">
<h2>CONTACT</h2>
<ul class="list-unstyled li-space-lg">
<li class="address">Don't hesitate to give us a call or just use the contact form below</li>
</ul>
</div> <!-- end of col -->
</div> <!-- end of row -->
<div class="row">
<div class="col-lg-8 offset-lg-2">
<!-- Contact Form -->
<form action="/Home/Index" method="post">
#Html.AntiForgeryToken()
#Html.ValidationMessage("expired", new { #class = "text-danger" })
#Html.ValidationMessage("CaptchaFail", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "required" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "required" })
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.Message, htmlAttributes: new { #class = "required" })
#Html.EditorFor(model => model.Message, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Message, "", new { #class = "text-danger" })
</div>
<div class="form-group checkbox">
<input type="checkbox" id="cterms" value="Agreed-to-Terms" required>I have read and agree to Leno's stated conditions in Privacy Policy.
<div class="help-block with-errors"></div>
</div>
#Html.CheckBoxFor(model => model.IsTermsAccepted, new { htmlAttributes = new { #class = "form-control" } })
#Html.LabelFor(model => model.IsTermsAccepted, htmlAttributes: new { #class = "required" })
#Html.ValidationMessageFor(model => model.IsTermsAccepted, "", new { #class = "text-danger" })
<div class="form-group">
<button type="submit" class="form-control-submit-button">SUBMIT MESSAGE</button>
</div>
<div class="form-message">
<div id="cmsgSubmit" class="h3 text-center hidden"></div>
</div>
</form>
<!-- end of contact form -->
</div> <!-- end of col -->
</div> <!-- end of row -->
</div> <!-- end of container -->
</div>
#section scripts{
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/jquery.validate.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.js"></script>
}
MODEL :
public class ContactVM
{
[Required(ErrorMessage = "You must provide Name")]
[DisplayName("Name")]
public string Name { get; set; }
[Required(ErrorMessage = "You must provide an Email address")]
[DisplayName("Email")]
[EmailAddress]
public string Email { get; set; }
[Required(ErrorMessage = "You must provide Message")]
[DisplayName("Your Message")]
public string Message { get; set; }
[Display(Name = "Terms and Conditions")]
[Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the terms and conditions!")]
public bool IsTermsAccepted { get; set; }
}
CONTROLLER:
public ActionResult Index()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Index(ContactVM model)
{
string sendGridKey ="";
if (ModelState.IsValid)
{
SendMail(model, sendGridKey);
return RedirectToAction("Thanks", "Home");
}
return View(model);
}
You need to use the loaded javascript. Add to your view the following:
<script>
$(document).ready(function() {
$.validator.unobtrusive.parse($("#myForm"));
}
function onSubmit(e) {
$("#myForm").validate(); // this will validate the form and show the validation messages
if($("#myForm").valid()) {
$("#myForm").submit(); // submits the form
}
// stop the postback
e.preventDefault();
}
</script>
Then on your form element:
<form id="myForm" onsubmit="onSubmit();" action="/Home/Index">
Seems validation scripts not loaded. Did you loaded in _layout.cshtml?
#RenderSection("scripts", required: false)
And try adding validation summery in starting of form
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
You can visit this link below
https://www.tutorialsteacher.com/articles/enable-client-side-valiation-in-mvc
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/Scripts/jquery.validate.min.js")
#Scripts.Render("~/Scripts/jquery.validate.unobtrusive.min.js")
Related
I am new to ASP.NET MVC 5 and having some issues with binding only the multi-select section of the body during POST when submitting the form. The form renders correctly, with checkboxes being correctly selected. The form is scaffolded using visual studio (except the multi-select)
public class EditViewModel
{
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "LastName")]
public string LastName { get; set; }
public IList<RolesViewModel> Roles { get; set; }
}
public class RolesViewModel
{
public string RoleId { get; set; }
public string RoleName { get; set; }
public bool Selected { get; set; }
}
[HttpGet]
public async Task<ActionResult> Edit(string Id)
{...
var model = Mapper.Map<ApplicationUser, EditViewModel>(user);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(EditViewModel model)
{
}
#model BloggingService.Web.Models.EditViewModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Edit</title>
</head>
<body>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Edit</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Roles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#for (int i = 0; i < Model.Roles.Count; i++)
{
#Html.CheckBoxFor(x => #Model.Roles[i].Selected,"test1");
<div class="form-check">
<input type="hidden" asp-for="#Model.Roles[i].RoleId" />
<input type="hidden" asp-for="#Model.Roles[i].RoleName" />
<input type="checkbox" asp-for="#Model.Roles[i].Selected" class="form-check-input" checked="#Model.Roles[i].Selected" />
<label class="form-check-label" asp-for="#Model.Roles[i].Selected">
#Model.Roles[i].RoleName
</label>
</div>
}
</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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Would really appreciate any insight.
I have managed to find the correct and cleaner approach for accomplishing such tasks by utilizing the HTML helper methods inside my for loop and now the data are able to bind correctly during data-binding.
#Html.HiddenFor(x => #Model.Roles[i].RoleId);
#Html.HiddenFor(x => #Model.Roles[i].RoleName);
#Html.CheckBoxFor(x => #Model.Roles[i].Selected);
I am a beginner in ASP.Net MVC 5 and I want to know how to display validation summary "Header Message" on top of page above all the errors.
Below is what I have till now:
View:
#model WebApplication3.Models.Form
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Form</h4>
#* first parameter false means show all the errors *#
#* second parameter means the message to display as Header on top*#
#Html.ValidationSummary(false,"Fix below error", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "*", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.age, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.age, new { htmlAttributes = new { #class = "form-control" } })
#*star meaning show the star sign to keep show field required. *#
#Html.ValidationMessageFor(model => model.age, "*", 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>
}
#* CLIENT SIDE VALIDATION*#
#section Scripts
{
#Scripts.Render("~/bundles/jqueryval")
}
Model
public class Form
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[Remote("IsAgeUnique", "Form", ErrorMessage = "Age is not unique")]
public int age { get; set; }
}
P.S:
I have used #Html.ValidationMessageFor(prop, "*") wildcard for each property to display star message side to the UI field.
Issue:
When the page loads the error header is already there displayed on the page. Functionality wise everything is working fine. But during initial page load why the "Header message gets displayed"
You can try to add css rule like following to make header error part unvisible initially.
Validation summary text has validation-summary-valid class initially. If there are some errors it becomes validation-summary-errors so your initial value dont have any error, I think you can use css rule
.validation-summary-valid {
display:none;
}
My Controller Method:
[HttpGet]
public ActionResult NewInventory()
{
return View();
}
[HttpPost]
public async Task<ActionResult> NewInventory(string bookID, string ttlin, string lowin, string Outnow)
{
// test values passed, etc.....
}
So far only the "lowin" value is being passed correctly. All the other values are set to "0" (I believe due to the datatypes being set to "not null" in SQL DB). Why is this?
I assume because only one value is passed correctly and no exceptions are thrown, then the view page code is missing the other the fields to pass.
View Code :
#model LibraryMS.Inventory
#{
ViewBag.Title = "newinventory";
}
<h2>newinventory</h2>
#using (Html.BeginForm("NewInventory","BookInfo", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Inventory</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.BookID, "BookID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.BookID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.BookID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TotalIn, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TotalIn, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TotalIn, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LowIn, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LowIn, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LowIn, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Out, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Out, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Out, "", 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>
By looking, the values are being passed.
Turns out that the controller method's parameters needs to be spelled the same as what is going to be passed to it. For example:
With the autogenerated form using html helper #Beginform, all the fields are present. But the parameters in the controller method are not the same as the inventory's class fields.
public partial class Inventory
{
public string BookID { get; set; }
public short TotalIn { get; set; }
public short LowIn { get; set; }
public short Out { get; set; }
public virtual BookInfo BookInfo { get; set; }
}
Compared to the paramters:
[HttpPost]
public async Task<ActionResult> NewInventory(string bookID, string ttlin, string lowin, string Outnow)
{
// test values passed, etc.....
}
The fix was to ofcoarse make the params the same, capitalization does not matter.
[HttpPost]
public async Task<ActionResult> NewInventory(string bookID, string totalin, string lowin, string Out)
{
// test values passed, etc.....
}
It was a simple mistake but took me some time to figure this out. Hopefully this helped someone else!
I have an object called Job and one of the properties is a List of Steps:
public class Job
{
[Display(Name = "Id")]
public int? JobId { get; set; }
[Required]
public string Name { get; set; }
public List<Step> Steps { get; set; }
public Job()
{
Steps = new List<Step>();
}
}
public class Step
{
public int? StepId { get; set; }
public int JobId { get; set; }
[Required]
public string Name { get; set; }
}
I have a JobController with the following action to perform the update:
// PUT: /Job/Edit/5
[HttpPut]
public ActionResult Edit(Job model)
{
// Logic to update model here
}
Based on a the answer to this question I updated my UI (using the Bootstrap template that comes with MVC5) to:
#using (Html.BeginForm())
{
#Html.HttpMethodOverride(HttpVerbs.Put)
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.JobId)
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<h3>Steps</h3>
<div>
#foreach (var item in Model.Steps)
{
<div class="form-group">
#Html.Hidden("Steps[" + stepIndex + "].StepId", item.StepId)
#Html.LabelFor(modelItem => item.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input class="form-control text-box single-line" data-val="true" data-val-required="The Name field is required."
id="#String.Format("Steps_{0}__Name", stepIndex)" name="#String.Format("Steps[{0}].Name", stepIndex)" type="text" value="#item.Name" />
#Html.ValidationMessageFor(modelItem => item.Name, "", new { #class = "text-danger" })
</div>
</div>
stepIndex += 1;
<hr />
}
</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>
}
As you can see I have to manually build the input tag opposed to using Html.EditorFor. The reason is that I need to control name of the id so that it passes the Index into the id and name. I would assume there is a better approach that would allow MVC to render the correct values using labelFor, EditorFor and ValidationMessageFor.
The questions I have are:
Is there a set of controls I can use with MVC5 that allows me to render complex child objects without going through these extra steps?
If no on 1, then is there a better approach than manually create input tag?
Option 1: Replace the foreach loop with for:
#for (int i = 0; i < Model.Steps.Count; i++)
{
<div class="form-group">
#Html.HiddenFor(m => m.Steps[i].StepId)
#Html.TextBoxFor(m => m.Steps[i].Name, new { #class = "form-control text-box single-line" })
...
</div>
}
Option 2: Create an editor template called Step.chtml for the Step class and use EditorFor:
Step.chtml
#model Step
<div class="form-group">
#Html.HiddenFor(m => m.StepId)
#Html.TextBoxFor(m => m.Name, new { #class = "form-control text-box single-line" })
...
</div>
Main View
<h3>Steps</h3>
<div>
#Html.EditorFor(m => m.Steps)
<div>
In both these ways the framework will give the inputs correct names and ids.
Looks like complicated the things, try the below.
1. Create a new editor template (which is a view) named 'Step.cshtml' under the EditorTemplates folder with the model Step.
2. In that do the below code, Step.cshtml
#model Step
<div class="form-group">
#Html.HiddenFor(model => model.StepId)
#Html.LabelFor(modelItem => modelItem.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Name, htmlAttributes: new { #class = "form-control text-box single-line" })
#Html.ValidationMessageFor(modelItem => modelItem.Name, "", new { #class = "text-danger" })
</div>
3. Remove the foreach statement from your view, and instead call the editor template as,
#Html.EditorFor(model => model.Steps)
simnilar to the answer of this question
Html.BeginForm with html attributes asp.net mvc4
I have a viewmodel for a view that contains collections that are used to populate drop downs and lists. so i dont watn to return them, i just want to return the model object. Well actually i just want to return 4 fields in that model - but that's the next problem.
I've dodged that rpeviously by doing this appraoch but im having no luck unless i submit the entire viewmodel which on this form is ridiculous as 95% of info is discarded.
Anyway the problem i get here is that i cannot get the game event that is returned in the create post to be anything other than null. The gameEvent parameter on create is NULL.
Also kinda suprised i haven't been able to find a ton of info on this.
The controller:
public ActionResult Create()
{
...
var createEventViewModel = new CreateEventViewModel()
{
Places = places,
Characters = characters,
Event = new GameEvent()
};
return this.View(createEventViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Name,Description,EventType,Duration")] GameEvent gameEvent)
{
...
}
The View:
#model Sisyphus.Web.Models.CreateEventViewModel
#{
ViewBag.Title = "Create Event";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Event</h2>
<div class="row">
<div class="col-lg-8">
<section id="createEvent">
#using (Html.BeginForm("Create", "Event",
new
{
GameEvent = Model.Event
}, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Event.Name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Event.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Event.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Event.Description, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextAreaFor(m => m.Event.Description, 10, 30, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Event.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Event.Duration, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Event.Duration, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Event.Duration, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Event.EventType, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(m => m.Event.EventType)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create Event" class="btn btn-default" />
</div>
</div>
}
</section>
</div>
</div>
The Model:
public class GameEvent
{
public string Name { get; set; }
public string Description { get; set; }
public int Duration { get; set; }
public EventType EventType { get; set; }
}
The viewmodel: (edited down have removed members that are irrelevant
public class CreateEventViewModel
{
public GameEvent Event { get; set; }
}
Edit:
Ok i just tried this
#using (Html.BeginForm("Create", "Event",
new RouteValueDictionary()
{
{"GameEvent", Model.Event}
}, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
Game event is now not null (All values in it are) - so not really any closer
Your inputs for postback are based on class CreateEventViewModel, for example
#Html.TextBoxFor(m => m.Event.Name, ...
#Html.TextAreaFor(m => m.Event.Description, ...
which would generate the following html
<input id="Event_Name" name="Event.Name" value=....
However the parameter of your post action method is typeof GameEvent, not CreateEventViewModel. If you inspect the Response.Form.Keys you will see Event.Name, Event.Description etc, but class GameEvent has properties Name, Description etc so the values cant be matched up by the ModelBinder
You need to change your post method to
public ActionResult Create(CreateEventViewModel model)
{
GameEvent event = model.GameEvent;
// do whatever with GameEvent
You should also remove new {GameEvent = Model.Event} from theHtml.BeginForm` method
Note I excluded the BindAttibute because I don't think its necessary in this case - you appear to want all the properties of GameEvent, and unless you create inputs for properties of Places and Characters, they will be null anyway, and since you are not accessing the other properties there is no mass assignment vulnerability.
Other alternative are to create the inputs manually so that the properties are correctly mapped, either direct html
<input name="Name" value=#Model.Event.Name />
<input name="Description" value=#Model.Event.Desciption />
or using helpers
var Description = Model.Event.Description;
#Html.TextBoxFor(m => Description)