I have two buttons but I'm only able to validate one. When the user clicks add and the entire form is not filled out then they get error message but if they click finish instead of giving error messages, it goes to another page but I want to give the error before going to that page. This is what I have so far for one:
#model student.Models.Student
<h2>Student Record</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Issue</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.studentNumber, htmlAttributes: new { #class = "col-md-2" })
#Html.EditorFor(model => model.studentNumber, new { htmlAttributes = new { #readonly = "readonly", #id = "reqnum", #class = "form-control" } })
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "col-md-2" })
#Html.ValidationMessageFor(model => model.name, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.Label("Processed by:", htmlAttributes: new { #class = "col-md-2" })
#Html.DropDownListFor(model => model.processedbyDetails.employeeNum, new SelectList(ViewBag.StoresReps, "Value", "Text"), new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.processedbyDetails.employeeNum, "", new { #class = "text-danger" })
</div>
#* -- MANY OTHER INPUTS -- *#
<div class="form-group">
<div class="col-md-offset-4 col-md-12">
<input type="submit" value="Add" name="Add" class="btn btn-default" width="89" />
<input type="button" value="Finish" name="Issue" margin="50px" onclick="location.href='#Url.Action("ViewIssue", "Issue")' " class="btn btn-default" />
<input type="button" value="Cancel" name="Cancel" margin="50px" onclick="location.href='#Url.Action("Cancel", "Issue")' " class="btn btn-default" />
</div>
</div>
</div>
}
Edit
#{
ViewBag.Title = "Student Item";
}
<!-- JS includes -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/4.0/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
<script type="text/javascript">
function onFinishClick() {
if ($('form').valid()) {
location.href = '#Url.Action("ViewIssue", "Issue")';
}
return false;
}
</script>
<input type="button" value="Finish" name="Issue" margin="50px" onclick="onFinishClick()" class="btn btn-default" />
You're using MVC, so just annotate your ViewModel and submit the form with the built in functionality that the framework supplies for you.
public class Student
{
[StringLength(100)]
[DisplayName("Student Name")]
[Required(ErrorMessage = "Please enter a Student Name")]
public string Name { get; set; }
[StringLength(100)]
[DisplayName("Student Number")]
[Required(ErrorMessage = "Please enter a Student Number")]
public string StudentNumber { get; set; }
// etc...
}
Your form should also have the name of the Action you're trying to POST to...
#using (Html.BeginForm("AddOrFinish", "HomeController", FormMethod.Post, new { role = "form" }))
You can't validate against the data annotations on your viewmodel in MVC from a button that's not of type submit (well, maybe you can, but probably more work).
You should then mark both buttons as type submit, then interrogate the name of the button that was clicked once it is sent over.
<div class="form-group">
<div class="col-md-offset-4 col-md-12">
<input type="submit" value="Add" name="add" class="btn btn-default" width="89" />
<input type="submit" value="Finish" name="issue" margin="50px" class="btn btn-default" />
<input type="button" value="Cancel" name="Cancel" margin="50px" onclick="location.href='#Url.Action("Cancel", "Issue")' " class="btn btn-default" />
</div>
</div>
Then in your controller, create a method with this signature. Since you have two submit buttons, the values of the button will be sent along in the request and you can interrogate it. It will look something like this...
[HttpPost]
public ActionResult AddOrFinish(Student model, string add, string issue)
{
if (!ModelState.IsValid)
{
return RedirectToAction("PageImOnNow", model);
}
if (!string.IsNullOrEmpty(add))
{
// do your add logic
// redirect to some page when user clicks "Add"
return RedirectToAction("WhateverPageYouWant");
}
else
{
// do your finish logic
// redirect to ViewIssue page when user clicks "Finish"
return RedirectToAction("ViewIssue");
}
}
More information here -
Handling multiple submit buttons on a form in MVC
Best Practices for ViewModel validation in MVC
Related
I am having an issue capturing the model when it's passed from the view to the controller.
Basically, every Question has a set of QuestionResponse (think of it as the list of offered responses for a certain question in a dropdown). ClinicalSummaryAnswer is the structure capturing one response per question, for a given user. So a user ends up with a collection of ClinicalSummaryAnswer, where every element is an answer to the respone.
When I submit the form below, all the elements in the model ClinicalSummaryView (ClinicalSummaryAnswer, Questions, QuestionResponse) are null from the controller side. Any ideas why this could happening? I suspect it may be due to the foreach? I am not sure. Please help!!
My View
#model TissueBankApp.Models.View.ClinicalSummaryView
<div>
#using (Html.BeginForm("Create", "ClinicalSummary", FormMethod.Post, new { #class = "form-horizontal" }))
{
<div class="form-group">
<input type="hidden" asp-for="Person.PersonId" value="Person.PersonId" />
<input type="submit" value="Create" class="btn btn-outline-primary btn-sm" />
<a asp-action="Details" asp-controller="Person" asp-route-id="Person.PersonId" class="btn btn-outline-primary btn-sm">Cancel</a>
</div>
<div class="col-md-12">
<div class="form-row">
<b> #Html.DisplayFor(model => model.ClinicalSummary.Description) </b>
</div>
#foreach (var question in Model.Questions) //For every question, generate question, and generate responses
{
<div class="form-row">
<label class="control-label col-md-4">#question.QuestionText</label>
#if (question.QuestionResponse.Count() != 0) //If count ViewData[QID] != 0, generate select dropdown, otherwise, input.
{
#Html.DropDownListFor(n => n.ClinicalSummaryAnswer.FirstOrDefault(m => m.QuestionId == question.QuestionId).AnswerId, new SelectList(question.QuestionResponse, "ResponseId", "ResponseText"), "", new { #class = "form-control col-md-4" })
}
else
{
#Html.TextBoxFor(n => n.ClinicalSummaryAnswer.FirstOrDefault(m => m.QuestionId == question.QuestionId).OtherText, new { placeholder = "", #class = "form-control col-md-4", #type = "date" });
}
</div>
}
</div>
}
</div>
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ClinicalSummaryView viewModel, string[] AnswerId, string[] OtherText)
{
//STUFF
//Ignore the OtherText, and AnswerId. I was testing to see if these are passed from view to controller and they are indeed. I just have no way of identifying which answer belongs to which question.
}
Firsly,you need know that for each property of the complex type, model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.Your backend wants to receive ClinicalSummaryView.ClinicalSummaryAnswer which is a list model. so what you pass should be ClinicalSummaryAnswer[index].PropertyName.
Change like below:
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { #class = "form-horizontal" }))
{
<div class="form-group">
<input type="hidden" asp-for="Person.PersonId" value="#Model.Person.PersonId"/>
<input type="submit" value="Create" class="btn btn-outline-primary btn-sm" />
<a asp-action="Details" asp-controller="Person" asp-route-id="#Model.Person.PersonId" class="btn btn-outline-primary btn-sm">Cancel</a>
</div>
<div class="col-md-12">
<div class="form-row">
<b> #Html.DisplayFor(model => model.ClinicalSummary.Description) </b>
</div>
#{ var i = 0;} //add here...
#foreach (var question in Model.Questions)
{
<div class="form-row">
<label class="control-label col-md-4">#question.QuestionText</label>
#if (question.QuestionResponse.Count() != 0)
{
//change here....
#Html.DropDownList("ClinicalSummaryAnswer["+i+ "].AnswerId", new SelectList(question.QuestionResponse, "ResponseId", "ResponseText"), new { #class = "form-control col-md-4" })
}
else
{
//change here....
#Html.TextBox("ClinicalSummaryAnswer["+i+"].OtherText",null, new { placeholder = "", #class = "form-control col-md-4", #type = "date" });
}
</div>
i++; //add here...
}
</div>
}
Besides, the following code should be modify:
//change the value
<input type="hidden" asp-for="Person.PersonId" value="#Model.Person.PersonId"/>
<input type="submit" value="Create" class="btn btn-outline-primary btn-sm" />
//change asp-route-id here..
<a asp-action="Details" asp-controller="Person" asp-route-id="#Model.Person.PersonId" class="btn btn-outline-primary btn-sm">Cancel</a>
I've built a page, where master and details(pictures, aso) are shown on one page. However, if the submitt button for the second form is clicked, the form is not submitted, if validations in the first form fail. If I correct the values, the HttpPostedFileBase uploadFile is null.
The page looks like this:
#model app1.Models.MasterModel
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm(new { #class = "form-inline col-lg-12" }))
{
#Html.AntiForgeryToken()
<div>
<h4>MasterModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="row">
#*Master properties*#
<div class="col-md-4 col-lg-4">
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-8">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
#* aso... *#
</div>
</div>
}
#* Master Details *#
<div class="col-md-4 col-lg-4">
#using (Html.BeginForm("NewPic", "Master", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input name="uploadFile" type="file" />
<input type="submit" value="Upload File" /> <!-- First Button, does not work -->
<div class="container-fluid">
#foreach (app1.Models.PicModel b in Model.Pics)
{
var base64 = Convert.ToBase64String(b.DbPic);
var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
<img src="#imgSrc" width="200" height="200" />
}
</div>
#Html.ActionLink("Upload", "NewPic", new { id = Model.Id }) <!-- Second Button, does not work either -->
<label class="control-label col-md-4 col-lg-4" for="Title">Picer</label>
}
</div>
</div>
<div>
<div class="form-group">
<div class="col-md-offset-2 col-md-12 col-lg-12">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The controller looks like this:
public ActionResult NewPic(int id, HttpPostedFileBase uploadFile)
{
// uploadFile is null
}
You forgotten to put [HttpPost] before NewPic method. So NewPic method will be considered as [HttpGet] so it will not work.
[HttpPost]
public ActionResult NewPic(int id, HttpPostedFileBase uploadFile)
{
// uploadFile is null
}
And also give proper Id to both form as follow so it would be easy to work with this both while client side validation.
Form 1
#using (Html.BeginForm(new {id = "Form1", #class = "form-inline col-lg-12" }))
Form 2
#using (Html.BeginForm("NewPic", "Master", FormMethod.Post, new { id = "Form2", enctype = "multipart/form-data" }))
For more information visit here
I'm trying to make a partial view, which will add dynamically as much fields as I input. I input number of fields, then I want to choose days of week in formed fields and do some work with this data in controller, but I have a problem with passing amount of days in partial view.
Controller :
public class CoursesController : Controller
{
private SchoolContext db = new SchoolContext();
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "CourseId,Name,Language,LanguageProficiency,StartDate,EndDate,TeacherId,NumberOfLessonsPerWeek")] Course course)
{
if (ModelState.IsValid)
{
db.Courses.Add(course);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(course);
}
public ActionResult ShowDaysOfweek(int? countDays)
{
//countDays = 2;
ViewBag.CountDays = countDays;
var days = new List<DayOfWeek> { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday };
ViewBag.DaysOfWeek = new SelectList(days);
return PartialView("ShowDaysOfweek");
}
}
In this View I can add patial view by clicking button in script:
#model SchoolManagementSystem.Models.Course
#using (Ajax.BeginForm(new AjaxOptions { }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" }
<div class="form-group">
#Html.Label("Number Of Lessons Per Week", htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-5">
#Html.TextBox("NumberOfLessonsPerWeek", null, htmlAttributes: new { #class = "form-control", #type = "number", #id = "numberOfLessonsPerWeek" })
</div>
<div>
<input type="button" value="Add days" id="Show" class="btn btn-default" />
</div>
</div>
<div id="ShowResults">
<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>
}
<script type="text/javascript">
var url = '#Url.Action("ShowDaysOfweek", "Courses")';
$('#Show').on("click", function () {
var countDays = $('#numberOfLessonsPerWeek').val();
url += '/?countDays=' + countDays;
$('#ShowResults').load(url);
})
</script>
Partial view:
#for (var i = 0; i < ViewBag.CountDays; i++)
{
<div class="form-group">
#Html.Label("Day of week", htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-5">
#Html.DropDownList("DaysOfWeek", (IEnumerable<SelectListItem>)ViewBag.DaysOfWeek, htmlAttributes: new { #class = "form-control", style = "height: 35px" })
</div>
</div>
}
Is it possible to do something? Thanks!
First create the hidden field with partial view url
<input type="hidden" value="#Url.Action("ShowDaysOfweek", "Courses", new { countDays= "numberOfLessonsPerWeek" })" id="hdnURLShowDaysOfweek" />
in javascript read the url and replace the parameter
<script type="text/javascript">
var url = $('#hdnURLShowDaysOfweek').val();
$('#Show').on("click", function () {
url = url.replace("numberOfLessonsPerWeek",$('#numberOfLessonsPerWeek').val());
$('#ShowResults').load(url);
})
</script>
I spent some time on this problem. I'm passing a ViewModel back from my View to the Controller via a form HttpPost. However, only SelectedItemId has value. Users and Rights are null.
View model UserRightViewModel
public class UserRightViewModel
{
public string SelectedItemId { get; set; }
public SelectList Users;
public List<RightViewModel> Rights { get; set; }
}
View model RightsViewModel
public class RightsViewModel
{
public int ID { get; set; }
public string Name{ get; set; }
public bool isSelected { get; set; }
}
Controller Post
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(UserRightsViewModel _viewModel)
{
UserRightsViewModel viewModel = _viewModel;
/*value _viewModel = null for Users and Rights
/* code stuff */
return View(viewModel);
}
View
#model Web.ViewModels.UserRightsViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>
#ViewBag.Title
</h1>
#*<ol class="breadcrumb">
<li><i class="fa fa-dashboard"></i> Home</li>
<li>Examples</li>
<li class="active">Blank page</li>
</ol>*#
</section>
<!-- Main content -->
<section class="content">
<!-- Default box -->
<div class="box">
<div class="box-header with-border">
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse" data-toggle="tooltip" title="Collapse">
<i class="fa fa-minus"></i>
</button>
<button type="button" class="btn btn-box-tool" data-widget="remove" data-toggle="tooltip" title="Remove">
<i class="fa fa-times"></i>
</button>
</div>
</div>
<br />
<div class="form-group">
#Html.LabelFor(model => model.SelectedItemId, "User", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.SelectedItemId, Model.Users, new { onchange = "redirect(this.value)", #class = "form-control" })
#Html.ValidationMessageFor(model => model.SelectedItemId)
</div>
</div>
<br />
<div class="box-body">
#for (int i = 0; i < Model.Rights.Count(); i += 2)
{
<div class="row form-group">
<div class="col-md-6">
<div class="row">
<div class="col-md-4">
<div class="col-md-6">
</div>
<div class="col-md-6">
#Html.CheckBoxFor(model => model.Rights.ElementAt(i).isSelected, new { #class = "checkbox checkbox-center" })
#Html.ValidationMessageFor(model => model.Rights.ElementAt(i).isSelected, "", new { #class = "text-danger" })
</div>
</div>
<div class="col-md-8">
#Model.Rights.ElementAt(i).Denumire
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-4">
<div class="col-md-6">
</div>
<div class="col-md-6">
#Html.CheckBoxFor(model => model.Rights.ElementAt(i + 1).isSelected, new { #class = "checkbox checkbox-center" })
#Html.ValidationMessageFor(model => model.Rights.ElementAt(i + 1).isSelected, "", new { #class = "text-danger" })
</div>
</div>
<div class="col-md-8">
#Model.Rights.ElementAt(i + 1).Name
</div>
</div>
</div>
</div>
}
<br />
<br />
<div class="form-group">
<div class="row">
<div class="col-md-6">
<div class="col-md-9">
</div>
<div class="col-md-3">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
<div class="col-md-6">
#Html.ActionLink("Cancel", "Index", null, new { #class = "btn btn-danger" })
</div>
</div>
</div>
</div>
</div>
</section>
</div>
}
I have no idea why some values are null. Why some of values are sent and some not?
Users is null because your not (and nor should you) creating and input for each property of each SelectListItem in the SelectList (if you need to return the view you re-populate the value in the POST method). And in any case, its a field, not a property (has no { get; set; }) so the DefaultModelBinder cannot set it).
Rights is null because you cannot use .ElementAt() to generate form controls for a collection (inspect the html your generating and you will see that the name attributes have no relationship to your model). The name attributes need to be
<input name="Rights[0].ID" ... />
<input name="Rights[0].Name" ... />
<input name="Rights[0].isSelected" ... />
<input name="Rights[1].ID" ... />
<input name="Rights[1].Name" ... />
<input name="Rights[1].isSelected" ... />
....
Note that your only generating a for control for the isSelected property which on its own would be meaningless in the POST method and you will an least want to include a hidden input for the ID property.
It appears from your code that you want to generate a 2 column layout, in which case your code should be (simplified)
<div class="row form-group">
#for(int i = 0; i < Model.Rights.Count; i++)
{
<div class="col-md-6">
#Html.HiddenFor(m => m.Rights[0].ID)
#Html.CheckBoxFor(m => m.Rights[0].isSelected)
</div>
// End current 'row' and start a new one every 2nd item
if ((i + 1) % 2 == 0)
{
#:</div><div class="row form-group">
}
}
</div>
Hi here is answer for your doubt, some values are posting and some values are not.
Actually you are getting the correct result and everything works perfectly
I said this because, if you bind selectedid and selectlistitem to the dropdownlistfor helper, then you will get only selectedid which was assigned as a name for the dropdownlist control. so when post, browser has sent name value pair.
This is same reason for checkbox also , which property has binded to the name that only you will get it in the post.
Hope above information was helpful
Thanks
Karthik
I can see you put submit button outside form. so form not submitting property.
I'm new in ASP.NET MVC. I created a login form and created two text boxes, then I used requirements attributes for them that if the were empty or wrong make errors.
Also I created a button link by #Html.ActionLink("", "") helper, so when I click it, without checking the validation of text boxes goes to next page. Would you please help me how can I fix this problem?
view code
controller code
#using (Html.BeginForm("CheckLogin", "Login", FormMethod.Post))
{
#Html.ValidationSummary(true)
#Html.ValidationMessageFor(model => model.username, null, new { #class = "required", #style = "color : red" })
<div class="form-group has-feedback">
#Html.TextBoxFor(model => model.username, null, new { #class = "form-control", placeholder = "Enter User Name" })
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
#Html.ValidationMessageFor(model => model.userpassword, null, new { #class = "required", #style = "color : red" })
<div class="form-group has-feedback">
#Html.PasswordFor(model=>model.userpassword, new { #class = "form-control", placeholder = "Enter Password" })
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">Sign In</button>
</div>
<!-- /.col -->
</div>
}
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
i think you simply copy and paste it
i think you should use
#Html.ActionLink("linkname", "Action name", "controller name")
and link is created .
In case if you are using Asp.Net Core you can use built-in tag builders:
<a class="btn btn-primary" asp-controller="<Controller name>" asp-action="<action name>" asp-route-<parameterName>="<parameter value>">Click Me!</a>
btn btn-primary is a bookstrap class.
You need to create a form in order to get correct validation
#model yourmodel
#using (Html.BeginForm("..", "...", FormMethod.Post))
{
#Html.ValidationSummary(true)
...
// your fields here now like
#Html.EditorFor(m=>m.prop)
#Html.ValidationFor(m=>m.prop)
//your submit button here
<input type=submit>
}
and at the end of the page don't forget to include Jquery validation scripts