Redirecting to ajax edit form on model binding error in asp.net mvc - ajax

I have a simple model called JobStatus, and I am enabling edit functionality within the Index view with an Ajax Actionlink.
My problem is that if there is a model error when editing an item, I don't know how to bring the item back to the model.
My Index action in my controller:
public ActionResult Index()
{
return View(db.JobStatuses.OrderBy(x => x.JobStatusID).ToList());
}
Here is my Index view:
#model IEnumerable<Project.Models.JobStatus>
#foreach (var item in Model)
{
<div id='#string.Format("div_{0}", item.JobStatusID)'>
#item.JobStatusID
#item.Name
#item.Description
#Ajax.ActionLink("Edit", "Edit", new { id = item.JobStatusID }, new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = string.Format("div_{0}", item.JobStatusID) })
</div>
}
And here is my edit GET request:
public ActionResult Edit(string id = null)
{
JobStatus jobstatus = db.JobStatuses.Find(id);
if (jobstatus == null)
{
return HttpNotFound();
}
return PartialView(jobstatus);
}
My edit.cshtml:
#model Project.Models.JobStatus
#using Microsoft.Ajax;
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.JobStatusID)
#Html.TextBoxFor(model => model.Name, null, new { #class = "form-control" })
#Html.TextBoxFor(model => model.Description, null, new { #class = "form-control" })
<input type="submit" value="Save" />
}
And finally my edit POST method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(JobStatus jobstatus, string action)
{
if (ModelState.IsValid)
{
db.Entry(jobstatus).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
// Not sure what to do here, how to I repopulate my index form?
return RedirectToAction("Index");
}
At the moment, on a modelstate failure the user is just redirected to the index page - what I'd like is to simply redisplay the index form, with the appropriate edit form enabled and populated, and any validation errors shown.
I've tried redirecting to my Edit action, but this just shows my form on a new page, without my index form (because currently my edit action is an ajax action)
Of course, please let me know if there is a better way to achieve this!

You can't do the RedirectToAction with Ajax Post.
Check out asp.net mvc ajax post - redirecttoaction not working
But you can load in the following way
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Edit(JobStatus jobstatus, string action)
{
if (ModelState.IsValid)
{
db.Entry(jobstatus).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return Json(new{id=jobStatus, success=true})
}
Once you get the JSON result from server you can load the same using the id returned
Like
$.post('/jobs/edit',{data:$(form).serialize()},function(data){
if(data.success){
$.get('/jobs/index/'+data.id,{},function(data){
$('#container').html(data);
});
}
});

Related

Adding 'Edit' Ajax.ActionResult to render on same page in MVC

My first ever Ajax request is failing, and I'm not quite sure as to why.
I've used the MVC scaffolding in order to create a table (which uses a default #Html.Actionlink). However, I'm looking to include an 'edit' section on the same page via ajax requests.
So my table now has:
<td>
#Ajax.ActionLink("Edit", "Edit", new { id=item.OID}, new AjaxOptions {
UpdateTargetId = "editblock",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET" }
) |
As suggested here.
Within the same view i have a div defined as:
<div id="editblock">
Edit Section Here
</div>
And My controller is defined as:
public PartialViewResult Edit(int? id)
{
if (id == null)
{
return PartialView(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
}
TableModel tablevar = db.TableModel.Find(id);
if (tablevar == null)
{
return PartialView(HttpNotFound());
}
return PartialView("Edit", tablevar );
}
[HttpPost]
[ValidateAntiForgeryToken]
public PartialViewResult Edit( TableModel tablevar )
{
if (ModelState.IsValid)
{
db.Entry(tablevar ).State = EntityState.Modified;
db.SaveChanges();
}
return PartialView("Edit",tablevar );
}
My "Edit.cshtml" looks like:
#model Project.Models.TableModel
<body>
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
Could anyone suggest as to why this is failing, and what I should be doing instead to render this partial view onto the page (as currently it keeps redirecting to new page and not showing on 'index' screen)?
Place those scripts at the bottom of your view. By the time they execute your form isn't present (and therefore the auto-wireup fails). In general, you want <script> tags as close to the </body> tag as possible to your content is there before the script executes.
Other than that, you look fine.

MVC 4 Ajax.BeginForm and ModelState.AddModelError

I'm trying to get errors to show up after an ajax submit has returned an error. I'm not sure what I'm missing, but I can't get it to work. This question is basically the same thing - ModelState.AddModelError is not being displayed inside my view but I'm still not having any luck. My experience with Ajax and MVC (any version) is still a bit limited. Here is a very simple example, most of which I took from the previous link.
View: test.cshtml
#model TestProject.VisitLabResult
#Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
#Scripts.Render("~/Scripts/ckeditor/ckeditor.js")
#{
AjaxOptions ajaxOpts = new AjaxOptions
{
Url = Url.Action("test"),
HttpMethod = "Post",
LoadingElementId = "loading",
LoadingElementDuration = 500,
OnSuccess = "processData"
};
}
#Html.ValidationMessage("CustomError")
<div id="loading" class="load" style="display:none">
<p>Saving...</p>
</div>
<table>
#for (int item = 0; item < 10; item++)
{
<tr id = #item>
#using (Ajax.BeginForm(ajaxOpts))
{
#Html.ValidationSummary(true)
#Html.AntiForgeryToken()
<td>
<input type="submit" value="Create" />
</td>
<td id = #(item.ToString() + "td")>
</td>
}
</tr>
}
</table>
Controller: HomeController.cs
public ActionResult test()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult test(VisitLabResult vlr, int visitid = 28)
{
try
{
if (ModelState.IsValid)
{
if (Request.IsAjaxRequest())
{
throw new Exception("error");
}
else
return View(vlr);
}
else
return View(vlr);
}
catch (Exception ex)
{
ModelState.AddModelError("CustomError", "The Same test Type might have been already created, go back to the Visit page to see the available Lab Tests");
return View(vlr);
}
}
Model
public class VisitLabResult
{
public int visitid { get; set; }
}
If it is an Ajax request I throw an error and it's caught and an error is added to ModelState. That error never shows up on the page though. Am I approaching this the right way at all? Or do I need to take a different route? I appreciate any help.
Just to clarify the solution for other people hitting this question. The ajax helper fires OnSuccess vs OnFailure based on the returned HTTP Code per the AjaxOptions docs:
OnSuccess: This function is called if the response status is in the 200 range.
OnFailure: This function is called if the response status is not in the 200 range.
In other words, you have to manually specify that there was a failure when returning your ActionResult by changing the Response.StatusCode and then returning whatever values you're expecting in your OnFailure js method. You can drive that based on any business logic you want (i.e. catch Exception ex) or !ModelState.IsValid ...)
[HttpPost]
public ActionResult Search(Person model)
{
if (ModelState.IsValid) {
// if valid, return a HTML view inserted by AJAX helper
var results = PersonRepository.Get(model)
return PartialView("Resulsts", vm);
} else {
// if invalid, return a JSON object and handle with OnFailure method
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { errors = ModelState.Values.SelectMany(v => v.Errors) });
}
}
Further Reading:
MVC 4 Ajax.BeginForm and ModelState.AddModelError
ASP.NET MVC “Ajax.BeginForm” executes OnSuccess even though model is not valid

Result not showing with unobtrusive ajax in mvc 3

I'm trying to implement a little ajax using unobtrusive ajax of mvc 3. Here is the code snippet:
Controller:
[HttpGet]
public ActionResult ViewEmployee()
{
return View();
}
[HttpPost]
public ActionResult ViewEmployee(EMPLOYEE model)
{
var obj = new EmployeeService();
var result=obj.FindEmployee(model);
return View("ViewEmployee", result);
}
View:
#{AjaxOptions AjaxOpts = new AjaxOptions { UpdateTargetId = "ajax", HttpMethod = "Post" };}
#using (Ajax.BeginForm("ViewEmployee", "Home", AjaxOpts))
{
#Html.LabelFor(x => x.EmployeeID)
#Html.TextBoxFor(x => x.EmployeeID)
<input type="submit" name="Find Name" value="Find Name" />
}
<div id="ajax">
#{
if (Model != null)
{
foreach (var x in Model.EmployeeName)
{
#x
}
}
else
{
#Html.Label("No Employee is selected!")
}
}
</div>
I debugged the code, its sending the employee id to the ViewEmployee method, finding the name but not being able to display the name back into the view.
I've activated the unobtrusive ajax property in web.config & imported the scripts into the view.
Whats going wrong with this? Please help.
This is simple but effective article, ask me if you have any more question! I resolved the problem! By the way, whats wrong with stackoverflow, i'm not getting no response literally!
http://www.c-sharpcorner.com/UploadFile/specialhost/using-unobtrusive-ajax-forms-in-Asp-Net-mvc3/

return json result in view

I have a newsletter text box that renders in a PartialView. This is the get action:
[ChildActionOnly]
public PartialViewResult NewsLetterSidebar()
{
return PartialView("_NewsLetterSidebar");
}
And this is the Razor view:
model Blog.Web.UI.ViewModels.NewsLetterViewModel
#{
ViewBag.Title = "_NewsLetterSidebar";
}
#using (Html.BeginForm("NewsLetter", "Home", FormMethod.Post))
{
<h3>News Letter</h3>
<div>
#Html.TextBoxFor(news => news.EmailAddress)
#Html.ValidationMessageFor(news => news.EmailAddress)
</div>
<div>
<input type="submit" value="Verify">
</div>
}
I want the success message to appear under the verify button in case of valid input. This is my post action:
[HttpPost]
public JsonResult NewsLetter(NewsLetterViewModel newsLetter)
{
var newsLetterViewModel = newsLetter.ConvertToNewsLetterModel();
if (ModelState.IsValid)
{
_newsLetterRepository.Add(newsLetterViewModel);
_newsLetterRepository.Save();
}
return Json("Done!");
}
How can I show the JSON message under the View?
Not tested but will help you to complete what you want.
[HTTPGET]
public JsonResult NewsLetter(NewsLetterViewModel newsLetter)
{
var newsLetterViewModel = newsLetter.ConvertToNewsLetterModel();
if (ModelState.IsValid)
{
_newsLetterRepository.Add(newsLetterViewModel);
_newsLetterRepository.Save();
}
return Json("Done!", JsonRequestBehavior.AllowGet);
}
replace this
#using (Html.BeginForm("NewsLetter", "Home", FormMethod.Post))
{}
with
#using (Ajax.BeginForm("NewsLetter", "Home", new AjaxOptions{ onsuccess:"ShowMessage"}))
{}
JS
function ShowMessage(data){
alert(data);
}
If you would like to use the first approach from my comment above, you need slightly modify your code. First of all, add Message property to your NewsLetterViewModel, then change the partial view:
#using (Ajax.BeginForm("NewsLetter", new AjaxOptions{UpdateTargetId = "newsletter-container"}))
{
<h3>News Letter</h3>
<span>#Model.Message</span>
<div>
#Html.TextBoxFor(news => news.EmailAddress)
#Html.ValidationMessageFor(news => news.EmailAddress)
</div>
<div>
<input type="submit" value="Verify">
</div>
}
Please noеe that your partial view should be wrapped into a html element with id="newsletter-container" on your page e.g:
<div id="newsletter-container">
#{Html.RenderPartial("_NewsLetterSidebar", new NewsLetterModel());}
</div>
Now, a small change in the controller:
[HttpPost]
public ActionResult NewsLetter(NewsLetterViewModel newsLetter)
{
var newsLetterViewModel = newsLetter.ConvertToNewsLetterModel();
if (ModelState.IsValid)
{
_newsLetterRepository.Add(newsLetterViewModel);
_newsLetterRepository.Save();
model.Message = "Done!";
}
return PartialView("_NewsLetterSidebar", model);
}
You also need to add jquery.unobtrusive-ajax.js to make it work.

Return PartialView to specific div from Action

I am playing about with jQuery UI and PartialViews and have run into a problem I can't quiet get my head around.
This bit works as I expect:
<div>
#Ajax.ActionLink("Test Me!", "dialogtest", new { id = Model.Id }, new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "dialogtest-view" })</td>
</div>
<div id="dialogtest-view">
</div>
this GETs to this action method
[HttpGet]
public PartialViewResult DialogTest(int id)
{
//pretend to get something from DB here
var vm = new DialogUITestVM();
return PartialView("uidialog_partial", vm);
}
And returns me a PartialView which displays in the targeted div. jQuery + jQueryUI is used to pop this div up as a modal dialog. Part 1 of test done!
OK so now let's say the PartialView returned is just a basic form with a textbox, something along the lines of:
#using (Html.BeginForm("DialogTest", "pages", FormMethod.Post))
{
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x => x.Name)
<button type="submit">Test Me!</button>
}
This is POSTd back to the controller fine -
[HttpPost]
public ActionResult DialogTest(DialogUITestVM vm)
{
//arbitrary validation so I can test pass and fail)
if (vm.Name.Equals("Rob"))
{
//error!
vm.ErrorMessage = "There was an error you numpty. Sort it out.";
return PartialView(vm);
}
//hooray it passed - go back to index
return RedirectToAction("index");
}
However - if I make the action fail the validation, rather than targeting the PartialView to the div again, it redraws the whole page (which obviously loses the jQuery UI dialog).
What I want is: if validation fails, just update the div that contained the form.
Where am I going wrong?
You could use an Ajax form in your partial instead of a normal form and use a OnSuccess callback in your AjaxOptions:
#using (Ajax.BeginForm("DialogTest", "pages", new AjaxOptions { UpdateTargetId = "dialogtest-view", OnSuccess = "success" }))
{
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x => x.Name)
<button type="submit">Test Me!</button>
}
and then modify your controller action respectively:
[HttpPost]
public ActionResult DialogTest(DialogUITestVM vm)
{
//arbitrary validation so I can test pass and fail)
if (vm.Name.Equals("Rob"))
{
//error!
vm.ErrorMessage = "There was an error you numpty. Sort it out.";
return PartialView(vm);
}
//hooray it passed - go back to index
return Json(new { redirectUrl = Url.Action("Index") });
}
and of course define the corresponding success callback in your javascript files:
function success(result) {
if (result.redirectUrl) {
window.location.href = result.redirectUrl;
}
}

Resources