How to Use Ajax.BeginForm OnSuccess and OnFailure Methods? - asp.net

I using this Ajax.BeginForm
<% using( Ajax.BeginForm( "Create","Mandate",
new AjaxOptions( ) {
OnSuccess = "GoToMandates",
OnFailure = "ShowPopUpError"
} ) ) {%>
<% } %>
What do I need to write in the controler to catch this OnSucces and OnFailure.
Because OnSuccess I need to show Success message
OnFailure I need to show othere message.
In my Controller
Public ActionResult GetSomething(FromCollection collection)
{
if(exists == null)
{
//OnSuccess
}
else
{
//OnFailure
}
}
Can anydboy help me out.. how to catch this?
Thanks

The OnSuccess and OnFailure looks like they are expecting javascript callback functions.
<script type="text/javascript">
function handleError(ajaxContext) {
var response = ajaxContext.get_response();
var statusCode = response.get_statusCode();
alert("Sorry, the request failed with status code " + statusCode);
}
</script>
<%= Ajax.ActionLink("Click me", "MyAction",
new AjaxOptions { UpdateTargetId = "myElement", OnFailure = "handleError"}) %>
Example from Pro ASP.NET Framework page 425
ASP.NET AjaxOptions Class
Added Controller Example
The simpliest way to do this would be what I've got here but I recommend looking into strongly typed mvc views using some kind of ViewModel and maybe look into using jQuery for your ajax. With that said this should hopefully work for you.
if (exists)
{
ViewData["msg"] = "Some Success Message";
}
else
{
ViewData["msg"] = "Some Error Message";
}
return View();
In your view
<div id="myResults" style="border: 2px dotted red; padding: .5em;">
<%: ViewData["msg"]%>
</div>

I was also searching for the same answer, but looks like Ajax.BeginForm() .. 's event's are not well documented or need more self experiments to find out when these onSuccess and onFailure events are called. But I got a very easy and straight forward alternative for not to bother with setting onSuccess and onFailure properties of the AjaxOptions. Rather, in your Controller's action method, simply call the onSuccess(), onFailure() javascript method by sending ActionResult as JavaScriptResult. For example,
Public ActionResult Create(FromCollection collection)
{
if(exists == null)
{
//OnSuccess
return JavaScript("OnSuccess();");
}
else
{
//OnFailure
return JavaScript("OnFailure();");
}
}
And the Ajax.BeginForm tag should look like
<%
using( Ajax.BeginForm( "Create","Mandate", new AjaxOptions())) // see, no OnSuccess and OnFailure here.
{%>
<% } %>
Now, you need to define the OnSuccess() and OnFailure() javascript methods in your page and thats it.
EDIT:
I was thinking, perhaps, OnSuccess() will be called by default if no Exception is thrown from the Server. OnFailure() will be called if any Exception is thrown from the server. I did not test this concept yet. If that is true, then, it wont be a good idea to practice sending JavaScript("OnSuccess();") and JavaScript("OnFailure();"); from the server, because that will not be a good pattern to follow.

Related

MVC - Update model in view after ajax form submit

I have a question concerning MVC4. I have a form that I submit through ajax to my controller. In the controller, I create a new entry in a database, using the data of the form.
In case of success, the form fields need to go empty and a message should appear that the record has been added. So that the user is ready to add the next item through the form. I can clear the fields in the "ResetView" method through javascript (that's called through "OnSuccess"), that's not a problem
In case of error, the form fields need to remain filled in and a message should appear that it failed. In my case, it calls also the "OnFailure" method "ShowError" (which just shows the div "CustomerMessage").
The issue I am having when an item fails, is that the "CustomMessage" from my model is empty, while I did explicitly set it in my controller. So in my view, the #Model.CustomMessage is always empty!
I read that I should call the "ModelState.Clear()" function in my controller but that doesn't seem to do anything.
Can someone check what could be wrong in my code?
Any help is greatly appreciated.
View:
<div id="createform">
#using (Ajax.BeginForm("Create", "Payment", new { username = User.Identity.Name }, new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "createform",
//OnBegin = "SubmitForm",
OnSuccess = "ResetView",
OnFailure = "ShowError"
}))
{
<div id="CustomMessage">#Model.CustomMessage</div>
... bunch of #Html.TextBoxFor stuff (like below) ...
#Html.TextBoxFor(model => model.SenderName, new { id = "SenderName"})
}
</div>
Controller:
[HttpPost]
public ActionResult Create(PaymentViewModel payment, string username)
{
if (ModelState.IsValid)
{
try
{
paymentRepository.AddPayment(payment.PaymentLine, username);
paymentRepository.SaveChanges();
payment.CustomMessage = "success";
}
catch (Exception ex)
{
payment.CustomMessage = "error";
}
}
else
{
payment.CustomMessage = "error";
}
ModelState.Clear();
return PartialView(payment);
}
I found the problem. It seemed that I was missing a reference to the jquery unobtrusive ajax script. After adding that, everything worked correctly
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

ASP.NET MVC Remove query string in action method

I have an action method that looks like this:
public ActionResult Index(string message)
{
if (message != null)
{
ViewBag.Message = message;
}
return View();
}
What happens is that the url of a request to this will look like:
www.mysite.com/controller/?message=Hello%20world
But I want it to look just
www.mysite.com/controller/
Is there a way to remove the query string inside the actionmethod?
No, unless you use a POST method, the information has to get passed somehow. An alternative may be to use an in-between class.
// this would work if you went to controller/SetMessage?message=hello%20world
public ActionResult SetMessage(string message)
{
ViewBag.Message = message ?? "";
return RedirectToAction("Index");
}
public ActionResult Index()
{
ViewBag.Message = TempData["message"] != null ? TempData["message"] : "";
return View();
}
Or. if you simply used a POST
//your view:
#using(Html.BeginForm())
{
#Html.TextBox("message")
<input type="submit" value="submit" />
}
[HttpGet]
public ActionResult Index()
{ return View(); }
[HttpPost]
public ActionResult Index(FormCollection form)
{
ViewBag.Message = form["message"];
return View();
}
You can remove the query string by adding some JavaScript in the razor view.
#section scripts{
<script>
if (location.href.includes('?')) {
history.pushState({}, null, location.href.split('?')[0]);
}
</script>
}
If you navigate to page
www.mysite.com/controller/?message=Hello%20world
Then it'll show
www.mysite.com/controller/
in the browser.
Most modern browsers support this (browser support).
Look into routes. They define how a url with parameters will be written.
If you create a new MVC application, and look at the Global.asax.cs file under `RegisterRoutes(). you should see one entry.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "home", action = "index", id = UrlParameter.Optional } // Parameter defaults
);
Look at each part:
"Default" is the name. This just has to be unique for each route you create.
"{controller}/{action}/{id}" is the pattern you want to use. example.org/home/index?id=2 will be written example.org/home/index/2 instead
new { controller = "home", action = "index", id = UrlParameter.Optional } is defining the defaults if nothing is specified.
So, that route make it so if you go to example.org it will assume you mean example.org/home/index{id is optional}.
Working from that, you can start to see how to create your own routes.
Now, addressing your question the short answer is yes you could make the URL look like that, but not really. You would have to define a route with a default message, and it would only look like that if someone didn't specify a message. You have to tell the controller what the message is. I'm sorry, but the best you can do is define a route that gives you
/message/Hello%20World and using string.replace make that look even nicer `'/message/hello_world'
I'm not sure you're really thinking that through. If you remove the query string... then you remove the query string.. ie, your page won't have the query string value to do whatever it needs to do.
There's a bunch of different hacks you could do.. all of them are not ideal. You could use javascript to strip out the query string. You could redirect to a querystring-less page after setting a session variable.. it's all pretty ugly.
Remember that what the user sees in the address bar is on the client. The client controls that. You can fiddle with it via javascript, but doing so is generally a bad idea. Since hiding things from the user can be considered malware-like behavior.
I recommend using a slug. Check out this post: SOF Slug post
In previous applications, I took this approach to remove querystrings from the URL.

Partial ASP.NET MVC View submit

I'm new in ASP.NET MVC so the question could appear 'stupid', sorry.
I have a Partial View inside my Home view.
The Partial View submit a form calling an Action Method inside the HomeController.
It works fine with server validation, the problem is that after the post only the Partial View is rendered.
How can I render the entire Home view after post?
About the code:
Inside PartialView I have a form:
<% using (Html.BeginForm("Request", "Home")) { %>
Request is a ActionResult defined inside my HomeController.
[HttpPost]
public ActionResult Request(RequestModel model)
{
if (ModelState.IsValid)
{
// Saving data .....
}
else
{
// Show Server Validation Errors
return View();
}
}
At this time, after the post, the ascx shows the server validation erros but only the PartialView ascx code is rendered.
The Url looks like this after the post:
http://xxxxxxxxxxx/Home/Request
What I want is showing the entire Home view with the ascx inside showing server validation errors.
Try to do a partial submit using jQuery:
<script type="text/javascript">
$(document).ready(function () {
$("input[type=submit]").live("click", function () {
var f = $("input[type=submit]").parents("form");
var action = f.attr("action");
var serializedForm = f.serialize();
$.ajax({
type: 'POST',
url: action,
data: serializedForm,
success: function (data, textStatus, request) {
if (!data == "") {
// redisplay partial view
$("#formDiv").html(data);
}
else {
// do whatever on sucess
}
}
});
return false;
});
});
</script>
Assuming your view/ascx/HTML is something like this:
<div id="formDiv">
<% Html.RenderAction("Request"); %>
</div>
Change return type also:
[HttpPost]
public PartialViewResult Request(RequestModel model)
{
if (ModelState.IsValid)
{
// Saving data .....
}
else
{
// Show Server Validation Errors
return PartialView();
}
}
I was facing same issue in code, so I just made a small modification in my code and it worked.
Instead of returning the same view, I used
return Redirect(Request.Referrer)
Earlier:
return View();

Message Control in Masterpage with ASP.NET MVC

Hey everyone. Got a custom on how to do this as im new to MVC and trying to get a couple of small things implemented. This is the way I did it in WebForms but want to transition it to MVC.
I have a user control that contains CSS which will render a message. This control is located in the MasterPage and called from a ASPX page like this:
Pseudo code:
try{
Msg.MessageMode = WebPageMessageMode.OK;
Msg.ShowOK("Report deleted.");
}
catch
{
Msg.MessageMode = WebPageMessageMode.ErrorMessage;
Msg.ShowError("There was a problem deleting the report.");
}
Masterpage.aspx
<cc1:WebPageMessage runat="server" ID="msg" />
I currently have the control in the MasterPage and now im a bit confused about proceeding from here.
Should I put the 'Msg' object above from the pseudo code to a View from the MasterPage?
What is the proper way to do something like?
I dont think there is a one-solution-fits-all here.
Anyway this is my solution that uses jQuery:
1) Create a MyResultModel class to handle a message to the user
public enum MyResultType { Info, Error }
public class MyResultModel
{
public MyResultModel( MyResultType type, string message ) {
switch ( type ) {
case MyResultType.Info: Title = "OK"; break;
case MyResultType.Error: Title = "Error!!!"; break;
}
Message = message;
}
public String Title { get; set; }
public String Message { get; set; }
}
2) Create a Partial View named MyResult in the Shared Folder to handle the model
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyResultModel>" %>
<div id="resultTitle"><%: Model.Title %></div>
<div id="resultMessage"><%: Model.Message %></div>
3) Create and use a BaseController for your controllers and add the following method to it. The method simply add a custom Http Header to the response
protected PartialViewResult PartialView( string viewName, object model, string resultHeader ) {
Response.AppendHeader( "MyHttpCustomHeader", resultHeader );
return base.PartialView( viewName, model );
}
4) In your action return a MyResultView when you've done
[HttpPost]
public virtual ActionResult DoSomething() {
try {
//Do Something
return PartialView( "MyResult",
new MyResultModel( MyResultType.Info, "Operation Completed" ),
"HttpResultInfo" );
}
catch ( Exception ex ) {
return PartialView( "MyResult",
new MyResultModel( MyResultType.Error, ex.Message ),
"HttpResultError" );
}
}
5) Finally, Submit the form using jquery and handle the results.
$.ajax({
type: "post",
dataType: "html",
url: "your/url/here",
data: $("#myform").serialize(),
success: function (response, status, xml) {
var resultType = xml.getResponseHeader("MyHttpCustomHeader");
if (resultType == null) {
//No message do whatever you need
}
else {
//response contain your HTML partial view here. Choose your
//desidered way to display it
}
}
});
With a scenario like this you dont need to place a control on the master page. You can:
Show the view as it comes from the action without any modification
Use some fancy message display technique as StackOverflow does with the orange sliding message (in this case simply extract the title and the message from the returned html)
Use some fancy jquery plugin as jGrowl to show your message
If you want to check wether it is an Info/Error message simply check the custom header with jQuery in the else branch
var title = $(response).filter("#resultTitle").text();
var message = $(response).filter("#resultMessage").text();
if (resultType == "HttpResultInfo") {
showInfoMessage(title, message);
}
else if (resultType == "HttpResultError") {
showErrorMessage(title, message);
}
Hope it helps!
In a controller, as part of the action, you can set a message like this:
public ActionResult MyAction()
{
// Do some stuff
TempData["message"] = "This is a message.";
return View("MyView");
}
In your master page or in your view:
<%
string text = TempData["Message"] as string;
Response.Write(text);
%>

ASP.NET MVC Controller FileContent ActionResult called via AJAX

The setup:
The controller contains a method public ActionResult SaveFile() which returns a FileContentResult.
What works:
The view contains a form, which submits to this action. The result is this dialog:
What doesn't work:
The view contains some javascript to do an AJAX call to the same controller action where the form would post. Rather than triggering the aforementioned dialog, or even the AJAX success function, the response triggers the AJAX error function, and the XMLHttpRequest.responseText contains the file response.
What I need to do:
Make the request for the file using AJAX, and end up with the same result as when submitting a form. How can I make the AJAX request provide the dialog that submitting a form shows?
Here's a quick example I made up. This is the concept LukLed was talking about with calling SaveFile but don't return file contents via ajax and instead redirect to the download.
Here's the view code:
<script src="../../Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
// hide form code here
// upload to server
$('#btnUpload').click(function() {
$.ajax({
type: 'POST',
dataType: 'json',
url: '<%= Url.Action("SaveFile", "Home") %>',
success: function(fileId) {
window.location = '<%= Url.Action("DownloadFile", "Home") %>?fileId=' + fileId;
},
error: function() {
alert('An error occurred uploading data.');
}
});
});
});
</script>
<% using (Html.BeginForm()) { %>
<div>Field 1: <%= Html.TextBox("field1") %></div>
<div>Field 2: <%= Html.TextBox("field2") %></div>
<div>Field 3: <%= Html.TextBox("field3") %></div>
<button id="btnUpload" type="button">Upload</button>
<% } %>
Here's the controller code:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public JsonResult SaveFile(string field1, string field2, string field3)
{
// save the data to the database or where ever
int savedFileId = 1;
// return the saved file id to the browser
return Json(savedFileId);
}
public FileContentResult DownloadFile(int fileId)
{
// load file content from db or file system
string fileContents = "field1,field2,field3";
// convert to byte array
// use a different encoding if needed
var encoding = new System.Text.ASCIIEncoding();
byte[] returnContent = encoding.GetBytes(fileContents);
return File(returnContent, "application/CSV", "test.csv");
}
public ActionResult About()
{
return View();
}
}

Resources