I have the following view,, which create 10 ajax.beginform ,, But the problem that i am facing is that incase an error occurs during the creation of the object then the ModelState.AddModelError will not be shown on the view although i have set the #Html.ValidationSummary(true)
The view looks as follow
#model Medical.Models.VisitLabResult
#for (int item = 0; item < 10; item++)
{
<tr id = #item>
#using (Ajax.BeginForm("CreateAll", "VisitLabResult", new AjaxOptions
{
HttpMethod = "Post",
UpdateTargetId = item.ToString() + "td",
InsertionMode = InsertionMode.Replace,
LoadingElementId = "progress2",
OnSuccess = string.Format(
"disableform({0})",
Json.Encode(item)),
}))
{
#Html.ValidationSummary(true)
#Html.AntiForgeryToken()
<td>
#Html.DropDownList("LabTestID", String.Empty)
#Html.ValidationMessageFor(model => model.LabTestID)
</td>
<td>
#Html.EditorFor(model => model.Result)
#Html.ValidationMessageFor(model => model.Result)
</td>
<td>
#Html.EditorFor(model => model.DateTaken)
#Html.ValidationMessageFor(model => model.DateTaken)
</td>
<td>
#Html.EditorFor(model => model.Comment)
#Html.ValidationMessageFor(model => model.Comment)
</td>
<td>
<input type="submit" value="Create" />
</td>
<td id = #(item.ToString() + "td")>
</td>
}
</tr>
}
</table>
And my action method which defines the ModelState.AddModelError looks as follow:-
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateAll(VisitLabResult vlr, int visitid = 28)
{
try
{
if (ModelState.IsValid)
{
var v = repository.GetVisit(visitid);
if (!(v.EligableToStart(User.Identity.Name))){
return View("NotFound");
}
vlr.VisitID = visitid;
repository.AddVisitLabResult(vlr);
repository.Save();
return Content("Addedd Succsfully");
}
}
catch (DbUpdateException)
{
JsonRequestBehavior.AllowGet);
ModelState.AddModelError(string.Empty, "The Same test Type might have been already created,, go back to the Visit page to see the avilalbe Lab Tests");
}
}
So how i can show the ModelState.AddModelError on my view.
I would urge you to change your try{ } catch(){ }
And first check if there exists a visit for the given id
and if so simply returns the model with the added model error
if (visitExists)
{
ModelState.AddModelError("CustomError", "The Same test Type might have been already created,, go back to the Visit page to see the avilalbe Lab Tests");
return View(vlr);
}
//Other code here
Change your AddModelError To
ModelState.AddModelError("CustomError", "The Same test Type might have been already created,, go back to the Visit page to see the avilalbe Lab Tests");
And in your view simply add a
#Html.ValidationMessage("CustomError")
Then when you return your model the error will be shown where you have placed the #Html.ValidationMessage ...
#Html.ValidationSummary(true)
shows only the error message about model's propertys, if you want to show also the added message, added with
ModelState.AddModelError(
"CustomError",
"The Same test Type might have been already created, go back to the Visit page to see the avilalbe Lab Tests");
you need to set
#Html.ValidationSummary(false)
If you need to display the validation's message near your input fields you need to set #Html.ValidationSummary(true) and to follow the steps suggested by Syneryx
You can use from ViewData dictionary in View to access ModelState data.
For example:
in Action:
ModelState.AddModelError("CustomError", "Error 1");
ModelState.AddModelError("CustomError", "Error 2");
and to get "Error 1" message:
ViewData.ModelState["CustomError"].Errors[0].ErrorMessage
Related
I have a comment section set up on one of my pages. The parent view has a partial view which shows the comments for that ID and gives the option to display another partial view to post a comment. When someone post a comment I want the first partial view within the parent to refresh displaying the new comment.
Currently when you click Post Comment, the AddComment method is called and added to the database. I get an error saying that I am passing the wrong type of model to the view. It seems to be trying to pass the return value to my AddComment partial view instead of injecting it into Partent View Div.
Parent View
#model QIEducationWebApp.Models.Course
#{
ViewBag.Title = "Course Details";
}
<h1 class="page-header">#ViewBag.Title</h1>
Javascript is here
.
.
.
<table class="table">
DETAILS HERE
</table>
<ul id="view-options">
<li>#Html.ActionLink("Back to Courses", "Index", "Course")</li>
</ul>
<input type="button" id="View" class="ShowComment" value="Show Comments"/>
<div id="CommentSection"/>
Partial View to view comments
Javascript is here
.
.
.
<div class="CommentSection">
#foreach (var item in Model)
{
<div class="Comment">
<div class="CommentText">
#Html.DisplayFor(modelItem => item.CommentText)
</div>
<div class="CommentSep">
<span class="Commenter">#Html.DisplayFor(modelItem => item.UserName)</span> - <span class="CommentDate">#Html.DisplayFor(modelItem => item.CommentDate)</span>
</div>
</div>
}
<input type="button" id="Post" class="AddComment" value="Add a Comment"/>
<br />
<br />
</div>
<div id="AddComment" />
<br />
<br />
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("ViewComments",
new { courseID = #ViewBag.courseID, page }),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(
new PagedListRenderOptions { MaximumPageNumbersToDisplay = 5, DisplayLinkToFirstPage = PagedListDisplayMode.IfNeeded,
DisplayLinkToLastPage = PagedListDisplayMode.IfNeeded },
new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "CommentSection" }))
Method behind the is partial view
public PartialViewResult ViewComments(int courseID, int? page = 1)
{
ViewBag.courseID = courseID;
var coursecomments = db.CourseComments.Where(cc => cc.CourseID == courseID);
int pageSize = 10;
int pageNumber = (page ?? 1);
return PartialView(coursecomments.OrderByDescending(cc => cc.CommentDate).ToPagedList(pageNumber, pageSize));
}
Partial to Post Comment
Javascript is here
.
.
.
#using (Ajax.BeginForm("AddComment", "CourseComment", new { courseID = #ViewBag.courseID, userName = #User.Identity.Name },
new AjaxOptions { UpdateTargetId = "CommentSection" }))
{
#Html.ValidationSummary(true)
<div class="NewComment">
<div class="editor-field">
#Html.TextAreaFor(model => model.CommentText, new { maxLength = 500 })
#Html.ValidationMessageFor(model => model.CommentText)
</div>
<input type="submit" class="PostComment" value="Post Comment" />
<div id="Counter" class="CommentCounter"/>
</div>
}
Controller method linked to the Post Comment Ajax.BeginForm()
public PartialViewResult AddComment(CourseComment coursecomment, int courseID, String userName)
{
coursecomment.CommentDate = System.DateTime.Now;
coursecomment.CourseID = courseID;
coursecomment.UserName = userName;
if (ModelState.IsValid)
{
db.CourseComments.AddObject(coursecomment);
db.SaveChanges();
}
ViewBag.courseID = courseID;
return ViewComments(courseID);
}
Adding pictures
Details
After selecting View Comments button
After selecting Add Comment
After Posting the the comment I want the list of Comments to refresh displaying the newly added Comment. Like So
For now I have it changed. I wanted to the comments section to be hidden until the show comments was clicked. Then after posting a comment on the comments section was refreshed, but I couldn't get that to work. So just reloading the whole page will refresh the comments section, but make it hidden at that time. I made it so that the comments section shows by default without the option to hide it. So unless anyone can figure out a way to get it to work how I wanted, this works for now.
my problem it's kinda strange. So in a Index.cshtml i populate with data and in some place i use
#Html.Action("_Create")
To create a partialView where i populate with some data from the Server and with some empty fields that need to be completed for submit.
[HttpGet]
public PartialViewResult _Create()
{
var userOrder = _orderRepository.CurrentUser(User.Identity.GetUserName());
var something = new CurrentListOrderViewModel()
{
PersonName = userOrder.Select(p=>p.PersonName).FirstOrDefault(),
Funds = userOrder.Select(p=>p.Funds).FirstOrDefault(),
Order = "",
OrderCosts = 0,
Restaurant = "",
TodayOrderDate = DateTime.Now,
WindowsName = User.Identity.GetUserName()
};
return PartialView(something);
}
[HttpPost]
[ValidateAntiForgeryToken]
public PartialViewResult _Create([Bind(Include = "PersonName,Funds,Order,OrderCosts,WindowsName,TodayOrderDate")]CurrentListOrderViewModel model)
{
if(ModelState.IsValid)
{
return PartialView();
}
return PartialView(model);
}
So the problem is when the page loads with HTTPGET everything goes fine, the table get's populated from the database and if i change in the _Create.cshtml the #Html.EditorFor into something else so i can disable it, i lose the data. What could be the cause? I don't really understand it.
#model FoodOrderQubizInterfaces.ViewModel.Order.CurrentListOrderViewModel
#using(Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<tr>
<td>
#Html.EditorFor(model => model.PersonName)
#Html.ValidationMessageFor(model => model.PersonName)
</td>
<td>
#Html.EditorFor(model => model.Funds, new { htmlAttributes = new { disabled = "disabled" }, })
#Html.ValidationMessageFor(model => model.Funds)
</td>
<td>
#Html.EditorFor(model => model.Order)
#Html.ValidationMessageFor(model => model.Order)
</td>
<td>
#*#Html.DropDownListFor(model => model.Restaurant,*#
<select>
<option value="1">Bigys</option>
<option value="2">Zulu</option>
</select>
</td>
<td>
#Html.EditorFor(model => model.OrderCosts)
#Html.ValidationMessageFor(model => model.OrderCosts)
</td>
<td>
#Html.EditorFor(model => model.WindowsName)
#Html.ValidationMessageFor(model => model.WindowsName)
<input type="submit" value="Save/Modify" class="btn btn-default" />
</td>
<td>
#Html.EditorFor(model => model.TodayOrderDate)
#Html.ValidationMessageFor(model => model.TodayOrderDate)
</td>
<td>
</td>
</tr>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Hope you guys can help me with some advice cause i'm out of ideas
I found the problem, after i search the web for some time. The cause was when you submit and you have the disabled attribute added it will not add to the model cause he thinks the model is disabled so the user couldn't interact with it.
So to fix it i changed from
<td>
#Html.EditorFor(model => model.Funds, new { htmlAttributes = new { disabled = "disabled" }, })
#Html.ValidationMessageFor(model => model.Funds)
</td>
To
#Html.TextBoxFor(model => model.Funds, new { #readonly = true })
And in that was it keeps the data. I hope it will help someone else with my problem
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am new in Asp.net MVC and i researched about Ajax.BeginForm but when i apply codes it did not work. Can you share very simple example with Ajax.Beginform with View, Controller, Model?
Thanks.
Simple example: Form with textbox and Search button.
If you write "name" into the textbox and submit form, it will brings you patients with "name" in table.
View:
#using (Ajax.BeginForm("GetPatients", "Patient", new AjaxOptions {//GetPatients is name of method in PatientController
InsertionMode = InsertionMode.Replace, //target element(#patientList) will be replaced
UpdateTargetId = "patientList",
LoadingElementId = "loader" // div with .gif loader - that is shown when data are loading
}))
{
string patient_Name = "";
#Html.EditorFor(x=>patient_Name) //text box with name and id, that it will pass to controller
<input type="submit" value="Search" />
}
#* ... *#
<div id="loader" class=" aletr" style="display:none">
Loading...<img src="~/Images/ajax-loader.gif" />
</div>
#Html.Partial("_patientList") #* this is view with patient table. Same view you will return from controller *#
_patientList.cshtml:
#model IEnumerable<YourApp.Models.Patient>
<table id="patientList" >
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Number)
</th>
</tr>
#foreach (var patient in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => patient.Name)
</td>
<td>
#Html.DisplayFor(modelItem => patient.Number)
</td>
</tr>
}
</table>
Patient.cs
public class Patient
{
public string Name { get; set; }
public int Number{ get; set; }
}
PatientController.cs
public PartialViewResult GetPatients(string patient_Name="")
{
var patients = yourDBcontext.Patients.Where(x=>x.Name.Contains(patient_Name))
return PartialView("_patientList", patients);
}
And also as TSmith said in comments, donĀ“t forget to install jQuery Unobtrusive Ajax library through NuGet.
All This Work :)
Model
public partial class ClientMessage
{
public int IdCon { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Controller
public class TestAjaxBeginFormController : Controller{
projectNameEntities db = new projectNameEntities();
public ActionResult Index(){
return View();
}
[HttpPost]
public ActionResult GetClientMessages(ClientMessage Vm) {
var model = db.ClientMessages.Where(x => x.Name.Contains(Vm.Name));
return PartialView("_PartialView", model);
}
}
View index.cshtml
#model projectName.Models.ClientMessage
#{
Layout = null;
}
<script src="~/Scripts/jquery-1.9.1.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script>
//\\\\\\\ JS retrun message SucccessPost or FailPost
function SuccessMessage() {
alert("Succcess Post");
}
function FailMessage() {
alert("Fail Post");
}
</script>
<h1>Page Index</h1>
#using (Ajax.BeginForm("GetClientMessages", "TestAjaxBeginForm", null , new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "SuccessMessage",
OnFailure = "FailMessage" ,
UpdateTargetId = "resultTarget"
}, new { id = "MyNewNameId" })) // set new Id name for Form
{
#Html.AntiForgeryToken()
#Html.EditorFor(x => x.Name)
<input type="submit" value="Search" />
}
<div id="resultTarget"> </div>
View _PartialView.cshtml
#model IEnumerable<projectName.Models.ClientMessage >
<table>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.IdCon)</td>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>#Html.DisplayFor(modelItem => item.Email)</td>
</tr>
}
</table>
Besides the previous post instructions, I had to install the package Microsoft.jQuery.Unobtrusive.Ajax and add to the view the following line
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
I have a Controller and View which allow a user to 'checkout' the contents of a shopping cart.
All was working fine when my CheckoutController was returning an Order model to the Checkout view. However, I now want to also display the contents of the shopping cart on the Checkout view so have modified the Controller and View to use a viewmodel containing the Order and CartItems.
I have created a CheckoutViewModel and modified the GET: and POST: ActionResults to use this viewmodel.
Problem is the CheckOutViewModel is not being properly populated during the POST:
I am getting an 'Object reference not set to an instance of an object' error on this line 'checkoutViewModel.Order.Username = User.Identity.Name;' in the POST: ActionResult but I am not sure exactly what is not being instansiated.
If need be, I can post the original working coded that used the Oreder model instead of the CheckoutViewModel.
I'm sure I have some simple sturctural or syntax problem, I just can't tell what it is because I'm still trying to get my head around the best way to do things in MVC.
GET: ActionResult
//GET: /Checkout/AddressAndPayment
public ActionResult AddressAndPayment()
{
//To pre-populate the form, create a new Order object and get the ShoppingCart, populate the ViewModel and pass it to the view
var order = new Order();
order.Username = User.Identity.Name;
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
storeDB.SaveChanges();
var cart = ShoppingCart.GetCart(this.HttpContext);
// Set up our ViewModel
var viewModel = new CheckoutViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal(),
Order = order
};
// Return the view
return View(viewModel);
}
POST: ActionResult
//POST: /Checkout/AddressAndPayment
[HttpPost]
public ActionResult AddressAndPayment(FormCollection values)
{
var checkoutViewModel = new CheckoutViewModel();
TryValidateModel(checkoutViewModel);
try
{
checkoutViewModel.Order.Username = User.Identity.Name;
checkoutViewModel.Order.OrderDate = DateTime.Now;
//Save Order
storeDB.Orders.Add(checkoutViewModel.Order);
storeDB.SaveChanges();
//Process the order
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(checkoutViewModel.Order);
storeDB.SaveChanges();
//Send 'Order Confirmation' email
ViewData["order"] = checkoutViewModel.Order;
UserMailer.OrderConfirmation(checkoutViewModel.Order).SendAsync();
return RedirectToAction("Complete", new { id = checkoutViewModel.Order.OrderID });
}
catch
{
//Invalid - redisplay with errors
return View(checkoutViewModel);
}
}
View
#model OrderUp.ViewModels.CheckoutViewModel
#{
ViewBag.Title = "AddressAndPayment";
}
<script src="../../Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<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)
<h2>Pick Up Details</h2>
<fieldset>
<legend>When are you gonna be hungry?</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Order.Phone)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Order.Phone)
#Html.ValidationMessageFor(model => model.Order.Phone)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Order.PickUpDateTime)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Order.PickUpDateTime)
#Html.ValidationMessageFor(model => model.Order.PickUpDateTime)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Order.Notes)
</div>
<div class="editor-multiline-field">
#Html.EditorFor(model => model.Order.Notes)
#Html.ValidationMessageFor(model => model.Order.Notes)
</div>
</fieldset>
<input type="submit" value="Submit Order" />
}
<div style="height:30px;"></div>
<h3>
<em>Review</em> your cart:
</h3>
<table>
<tr>
<th>
Menu Item
</th>
<th>
Price (each)
</th>
<th>
Notes
</th>
<th>
Quantity
</th>
<th></th>
</tr>
#foreach (var item in Model.CartItems)
{
<tr id="row-#item.RecordID">
<td>
#Html.ActionLink(item.MenuItem.Name, "Details", "Store", new { id = item.MenuItemID }, null)
</td>
<td>
#Html.DisplayFor(modelItem => item.MenuItem.Price)
</td>
<td>
#Html.DisplayFor(modelItem => item.Notes )
</td>
<td id="item-count-#item.RecordID">
#item.Count
</td>
<td>
</td>
</tr>
}
<tr>
<td >
Total
</td>
<td id="cart-total">
#String.Format("${0:F2}", Model.CartTotal)
</td>
<td>
</td>
<td>
</td>
<td>
</td>
</tr>
</table>
You should use CheckoutViewModel as an input parameter to your HttpPost method and then try debug and see if you are still not getting that model populated after the form post.
[HttpPost]
public ActionResult AddressAndPayment(CheckOutViewModel checkoutViewModel)
{
TryValidateModel(checkoutViewModel);
try
{
checkoutViewModel.Order.Username = User.Identity.Name;
checkoutViewModel.Order.OrderDate = DateTime.Now;
//Save Order
storeDB.Orders.Add(checkoutViewModel.Order);
storeDB.SaveChanges();
//Process the order
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(checkoutViewModel.Order);
storeDB.SaveChanges();
//Send 'Order Confirmation' email
ViewData["order"] = checkoutViewModel.Order;
UserMailer.OrderConfirmation(checkoutViewModel.Order).SendAsync();
return RedirectToAction("Complete", new { id = checkoutViewModel.Order.OrderID });
}
catch
{
//Invalid - redisplay with errors
return View(checkoutViewModel);
}
}
I have a login widget that is a partial view that I want updated with model validation errors when there is a problem otherwise the page should be reloaded.
I have the following
LogOn.cshtml
#{
var ajaxOpts = new AjaxOptions {OnSuccess = "success"};
}
<div id="login">
#Html.ValidationSummary(excludePropertyErrors: true)
<h2>Start by Logging in</h2>
#using (Ajax.BeginForm("LogOn", "Account", ajaxOpts))
{
#Html.Hidden("returnUrl", Request.Url.PathAndQuery)
<table width="100%" border="0" cellspacing="0" cellpadding="5">
<tr>
<td>
<span class="bluey">Username:</span><br />
#Html.TextBoxFor(m => m.UserName, new {tabindex = "1", Class = "field"})
#Html.ValidationMessageFor(m => m.UserName, "*")
</td>
<td>
<span class="bluey">Password:</span><br />
#Html.TextBoxFor(m => m.Password, new {tabindex = "2", Class = "field"})
#Html.ValidationMessageFor(m => m.Password, "*")
</td>
</tr>
<tr>
<td>
<input name="login" type="submit" value="Submit" class="input_btn" tabindex="3" />
</td>
<td>#Html.CheckBoxFor(m => m.RememberMe) #Html.LabelFor(m => m.RememberMe) <span class="bluey"> | </span> #Html.ActionLink("Forgot Password?", "Password", "User")</td>
</tr>
</table>
}
</div>
<script>
function success(context) {
//var returnUrl = context.get_data().returnUrl;
//if (returnUrl) {
// window.location.href = returnUrl;
//} else {
$("#login").replaceWith(context);
//}
}
</script>
and the action that gets called is
[HttpPost]
public ActionResult LogOn(LogOnModel userDetails, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(userDetails.UserName, userDetails.Password))
{
FormsAuthentication.SetAuthCookie(userDetails.UserName, userDetails.RememberMe);
return Redirect(returnUrl);
}
ModelState.AddModelError("", "The username or password provided was incorrect");
}
return PartialView(userDetails);
}
I have it working so that when data is incorrect the partial view is redisplayed with errors however this part only works if I comment out the other lines of javascript in success(context) that are there for when the user logs in successfully and the page should be redirected.
Is there a better way to do this?
I did try returning anonymous objects with Json() that had a status property and either a returnUrl or Html however I could not figure out how to get the html that would have been generated by PartialView(userDetails) into the Json() call, and it seems like that might be the wrong way to go about things anyway.
You cannot redirect in an AJAX call. You could return a JSON object from the server pointing to the url to redirect to and then perform the actual redirect in your success callback.
function success(result) {
if (result.returnUrl) {
// the controller action returned JSON
window.location.href = returnUrl;
} else {
// the controller action returned a partial
$('#login').html(result);
}
}
Now in your controller action in case of success simply return the return url instead of redirecting:
return Json(new { returnUrl = returnUrl });
By the way you already seem to have the return url in the view: Request.Url.PathAndQuery. I don't see the point of having it transit to the server and back. You could simply return some JSON boolean indicating the success and perform the redirect in the success callback to the url.