MVC: How to call Javascript function in action? - asp.net-mvc

In my view I has a function called testalert,and in my controller I has a action called Index,I use javascriptmodel can solve my problem,but I find that if my action do not return a view(),for example:just return Json(model),the javascriptmodel will not work.How to call js function when I return json?Why do the javascriptmodel only be designed to work well
in return view?
function testalert(para) {
alert(para);
}
public ActionResult Index()
{
//work well and alert "abc"
this.AddJavaScriptFunction("testalert", PageLoadEvent.Ready, null, "abc");
return View();
}
public ActionResult GetData()
{
var restult="data";
// not work
this.AddJavaScriptFunction("testalert", PageLoadEvent.Ready, null, "abc");
return Json(restult);
}

I've done f.e AsyncHelper.Call(url, params) which return as a result promise, and on clientside wait for promise.done and to my stuff.
shorter version:
var AsyncAction = (function () {
return {
//options: passed to $.ajax
Call: function (options, helperOptions) {
return $.ajax(options)
.done(function (result) {
helperOptions.onSucceed(result.model);
});
}
};
})();

Related

Having trouble calling next Action once a users login is validated. ERROR redirected you too many times

I need to route to a new view once the login is validated. This is currently not working. My authenticateUser method is working correctly as i can see when debugging, but not sure how to get the redirect to work.
CONTROLLER
public ActionResult Login()
{
if (PCSSession.Current.IsAuthenticated)
{
return RedirectToAction("Landing", "Start");
}
else
{
return View();
}
}
[HttpPost]
public JsonResult AttemptToLogin(string username, string password)
{
try
{
return new JsonResult
{
Data = PCSSession.Current.AuthenticateUser(username, password)
};
}
catch (Exception ex)
{
return new JsonResult
{
Data = "Error occured while trying to login"
};
}
}
AJAX IN VIEW
<script type="text/javascript">
$("#btnLogin").click(function () {
$.post("/Login/AttemptToLogin",
{
username: $('#Username').val(),
password: $('#Password').val(),
},
function (data) {
if (data == "true") {
$("#loginForm").submit();
} else {
}
});
});
This is because of [HttpPost] attribute. There are two things you could do here
You can pass your url to client and the call navigate(), or window.location = myurl
OR
return Redirect(redirectUrl);
hope this helps.
In order fix my error of too many redirects, i had to change the redirecttoaction to just return view.
if (PCSSession.Current.IsAuthenticated)
{
return View("../Start/Landing");
}
else
{
return View("Login");
}

Return view use Ajax

I want to call a view using Ajax script:
In main view:
<script>
var onCommand = function (column, command, record, recordIndex, cellIndex) {
Ext.Msg.alert('record = ' + record.data.ID);
Ext.Ajax.request({
url: '/Details/',
method: 'GET',
params: {
id: record.data.ID
},
success: function (response) {
var result = (response.responseText);
if (result != "") {
modelName = result;
CreateLookUp(combo, id, false, true);
} else {
CreateLookUp(combo, id, true, false);
}
}
});
}
</script>
Controller:
// GET: Bob/Details/5
public ActionResult Details(String ID)
{
int id = Convert.ToInt32(ID);
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
BobRepository bobRepository = new BobRepository();
Bob bob = bobRepository.GetBob(id);
if (bob == null)
{
return HttpNotFound();
}
return View(bob);
}
The function call of the controller is called, the turget view is not returned. What is the reason?
I am not sure what you are trying to do, I am assuming you are filling that view in some pop up modal, first you need to return partial view instead of view, i suggest checking if the request is ajax then return partial view else return view,
something like this
if(Request.IsAjaxRequest())
{return PartialView(bob);}
else
return View(bob);
in your js, parse the text to html, you can use jquery.htmlparse
success: function (response) {
var result = $.parseHTML(response);
//do what you want with your html

How to call async Task from ajax aspnet core

I am using asp.net core and I am calling async task ActionResult method from ajax. Its is running fine on local host but after hosting on IIS it throw 500 status code error.
But it is not calling this method is ajax code
This is ajax method:
$('.Savebtn').click(function () {
$.ajax({
url: '#Url.Action("Testing", "Home")',
data: "Test data",
type: 'POST', //POST if you want to save, GET if you want to fetch data from server
success: function (obj) {
// here comes your response after calling the server
alert('Suceeded');
},
error: function (obj) {
alert('Something happened');
}
});
});
This is Controller method:
[HttpPost]
public async Task<IActionResult> Testing()
{
if (ModelState.IsValid)
{
try
{
return NotFound();
}
catch (Exception ex)
{
return NotFound();
}
}
return View();
}
Error Screen Shot
In Startup.cs file add service like this:
services.AddAntiforgery(options => options.HeaderName = "RequestVerificationToken");
In your cshtml file add:
#Html.AntiForgeryToken()
$.ajax({
type: 'GET',
url: '/home/Demo1',
beforeSend: function (xhr) {
xhr.setRequestHeader("RequestVerificationToken",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
success: function (result) {
alert(result);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(thrownError);
}
});
And your method in Controller looks like this:
[HttpGet]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Demo1()
{
//your code
return new JsonResult(null);
}
If you don't want [ValidateAntiForgeryToken] remove it and it will work. If you want it, then you have to pass the auto generated cookie value to validate as mentioned below, check this.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Testing()
{
if (ModelState.IsValid)
{
try
{
await Task.Delay(100);
return Ok();
}
catch (Exception ex)
{
return NotFound();
}
}
return View();
}
View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "__AjaxAntiForgeryForm" }))
{
#Html.AntiForgeryToken()
}
<button class="Savebtn btn btn-success">Save</button>
Script:
$(document).ready(function () {
$('.Savebtn').click(function () {
var form = $('#__AjaxAntiForgeryForm');
var token = $('input[name="__RequestVerificationToken"]', form).val();
$.ajax({
url: '#Url.Action("Testing", "Home")',
data: {
__RequestVerificationToken: token,
data: "Test data"
},
type: 'POST', //POST if you want to save, GET if you want to fetch data from server
success: function (obj) {
// here comes your response after calling the server
alert('Suceeded');
},
error: function (obj) {
alert('Something happened');
}
});
});
})
</script>
Reference
First change the URL to like 'Testing/Home' and Make sure you're passing data because if you don't it might throw 500 status code error.
In my case I wasn't passing any data I mean I was sending an empty form that was why. I thought it might help someone.

Get one database record, display it, update it, and save it back to the database

SOLVED! It was a Knockout issue (wrong binding). But maybe someone likes to argue or comment about the code in general (dataservice, viewmodel, etc).
I tried to build a Breeze sample, where I get one database record (with fetchEntityByKey), display it for updating, then with a save button, write the changes back to the database. I could not figure out how to get it to work.
I was trying to have a dataservice ('class') and a viewmodel ('class'), binding the viewmodel with Knockout to the view.
I very much appreciated if someone could provide a sample or provide some hints.
Thankx, Harry
var dataservice = (function () {
var serviceName = "/api/amms/";
breeze.NamingConvention.camelCase.setAsDefault();
var entityManager = new breeze.EntityManager(serviceName);
var dataservice = {
serviceName: serviceName,
entityManager: entityManager,
init: init,
saveChanges: saveChanges,
getLocation: getLocation
};
return dataservice;
function init() {
return getMetadataStore();
}
function getMetadataStore() {
return entityManager.fetchMetadata()
.then(function (result) { return dataservice; })
.fail(function () { window.alert("fetchMetadata:fail"); })
.fin(function () { });
}
function saveChanges() {
return entityManager.saveChanges()
.then(function (result) { return result; })
.fail(function () { window.alert("fetchEntityByKey:fail"); })
.fin(function () { });
}
function getLocation() {
return entityManager.fetchEntityByKey("LgtLocation", 1001, false)
.then(function (result) { return result.entity; })
.fail(function () { window.alert("fetchEntityByKey:fail"); })
.fin(function () { });
}
})();
var viewmodel = (function () {
var viewmodel = {
location: null,
error: ko.observable(""),
init: init,
saveChanges: null
};
return viewmodel;
function init() {
return dataservice.init().then(function () {
viewmodel.saveChanges = dataservice.saveChanges;
return getLocation();
})
}
function getLocation() {
return dataservice.getLocation().then(function (result) {
return viewmodel.location = result;
})
}
})();
viewmodel.init().then(function () {
ko.applyBindings(viewmodel);
});
Glad you solved it. Can't help noticing that you added a great number of do-nothing callbacks. I can't think of a reason to do that. You also asked for metadata explicitly. But your call to fetchEntityByKey will do that implicitly for you because, as you called it, it will always go to the server.
Also, it is a good idea to re-throw the error in the fail callback within a dataservice so that a caller (e.g., the ViewModel) can add its own fail handler. Without re-throw, the caller's fail callback would not hear it (Q promise machinery acts as if the first fail handler "solved" the problem).
Therefore, your dataservice could be reduced to:
var dataservice = (function () {
breeze.NamingConvention.camelCase.setAsDefault();
var serviceName = "/api/amms/";
var entityManager = new breeze.EntityManager(serviceName);
var dataservice = {
serviceName: serviceName, // why are you exporting this?
entityManager: entityManager,
saveChanges: saveChanges,
getLocation: getLocation
};
return dataservice;
function saveChanges() {
return entityManager.saveChanges()
.fail(function () {
window.alert("saveChanges failed: " + error.message);
throw error; // re-throw so caller can hear it
})
}
function getLocation() {
return entityManager.fetchEntityByKey("LgtLocation", 1001, false)
.then(function (result) { return result.entity; })
.fail(function () {
window.alert("fetchEntityByKey failed: " + error.message);
throw error; // re-throw so caller can hear it
})
}
})();
I don't want to make too much of this. Maybe you're giving us the stripped down version of something more substantial. But, in case you (or a reader) think those methods are always necessary, I wanted to make clear that they are not.

ASP.NET MVC Ajax Error handling

How do I handle exceptions thrown in a controller when jquery ajax calls an action?
For example, I would like a global javascript code that gets executed on any kind of server exception during an ajax call which displays the exception message if in debug mode or just a normal error message.
On the client side, I will call a function on the ajax error.
On the server side, Do I need to write a custom actionfilter?
If the server sends some status code different than 200, the error callback is executed:
$.ajax({
url: '/foo',
success: function(result) {
alert('yeap');
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert('oops, something bad happened');
}
});
and to register a global error handler you could use the $.ajaxSetup() method:
$.ajaxSetup({
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert('oops, something bad happened');
}
});
Another way is to use JSON. So you could write a custom action filter on the server which catches exception and transforms them into JSON response:
public class MyErrorHandlerAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
filterContext.Result = new JsonResult
{
Data = new { success = false, error = filterContext.Exception.ToString() },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
and then decorate your controller action with this attribute:
[MyErrorHandler]
public ActionResult Foo(string id)
{
if (string.IsNullOrEmpty(id))
{
throw new Exception("oh no");
}
return Json(new { success = true });
}
and finally invoke it:
$.getJSON('/home/foo', { id: null }, function (result) {
if (!result.success) {
alert(result.error);
} else {
// handle the success
}
});
After googling I write a simple Exception handing based on MVC Action Filter:
public class HandleExceptionAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
filterContext.Exception.Message,
filterContext.Exception.StackTrace
}
};
filterContext.ExceptionHandled = true;
}
else
{
base.OnException(filterContext);
}
}
}
and write in global.ascx:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleExceptionAttribute());
}
and then write this script on the layout or Master page:
<script type="text/javascript">
$(document).ajaxError(function (e, jqxhr, settings, exception) {
e.stopPropagation();
if (jqxhr != null)
alert(jqxhr.responseText);
});
</script>
Finally you should turn on custom error.
and then enjoy it :)
Unfortunately, neither of answers are good for me. Surprisingly the solution is much simpler. Return from controller:
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Response.ReasonPhrase);
And handle it as standard HTTP error on client as you like.
I did a quick solution because I was short of time and it worked ok. Although I think the better option is use an Exception Filter, maybe my solution can help in the case that a simple solution is needed.
I did the following. In the controller method I returned a JsonResult with a property "Success" inside the Data:
[HttpPut]
public JsonResult UpdateEmployeeConfig(EmployeConfig employeToSave)
{
if (!ModelState.IsValid)
{
return new JsonResult
{
Data = new { ErrorMessage = "Model is not valid", Success = false },
ContentEncoding = System.Text.Encoding.UTF8,
JsonRequestBehavior = JsonRequestBehavior.DenyGet
};
}
try
{
MyDbContext db = new MyDbContext();
db.Entry(employeToSave).State = EntityState.Modified;
db.SaveChanges();
DTO.EmployeConfig user = (DTO.EmployeConfig)Session["EmployeLoggin"];
if (employeToSave.Id == user.Id)
{
user.Company = employeToSave.Company;
user.Language = employeToSave.Language;
user.Money = employeToSave.Money;
user.CostCenter = employeToSave.CostCenter;
Session["EmployeLoggin"] = user;
}
}
catch (Exception ex)
{
return new JsonResult
{
Data = new { ErrorMessage = ex.Message, Success = false },
ContentEncoding = System.Text.Encoding.UTF8,
JsonRequestBehavior = JsonRequestBehavior.DenyGet
};
}
return new JsonResult() { Data = new { Success = true }, };
}
Later in the ajax call I just asked for this property to know if I had an exception:
$.ajax({
url: 'UpdateEmployeeConfig',
type: 'PUT',
data: JSON.stringify(EmployeConfig),
contentType: "application/json;charset=utf-8",
success: function (data) {
if (data.Success) {
//This is for the example. Please do something prettier for the user, :)
alert('All was really ok');
}
else {
alert('Oups.. we had errors: ' + data.ErrorMessage);
}
},
error: function (request, status, error) {
alert('oh, errors here. The call to the server is not working.')
}
});
Hope this helps. Happy code! :P
In agreement with aleho's response here's a complete example. It works like a charm and is super simple.
Controller code
[HttpGet]
public async Task<ActionResult> ChildItems()
{
var client = TranslationDataHttpClient.GetClient();
HttpResponseMessage response = await client.GetAsync("childItems);
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
List<WorkflowItem> parameters = JsonConvert.DeserializeObject<List<WorkflowItem>>(content);
return Json(content, JsonRequestBehavior.AllowGet);
}
else
{
return new HttpStatusCodeResult(response.StatusCode, response.ReasonPhrase);
}
}
}
Javascript code in the view
var url = '#Html.Raw(#Url.Action("ChildItems", "WorkflowItemModal")';
$.ajax({
type: "GET",
dataType: "json",
url: url,
contentType: "application/json; charset=utf-8",
success: function (data) {
// Do something with the returned data
},
error: function (xhr, status, error) {
// Handle the error.
}
});
Hope this helps someone else!
For handling errors from ajax calls on the client side, you assign a function to the error option of the ajax call.
To set a default globally, you can use the function described here:
http://api.jquery.com/jQuery.ajaxSetup.

Resources