ASP.NET MVC 2: prevent ajax action link from replacing the updateTarget - ajax

I use an ajax action link on a view, then bind a js function onto its onCompleted property.
In this function, i get the response object, do some funny stuff, then write the message property to the updatetarget element.
The problem is, when it finishes its work on the oncompleted event, it writes the raw json response onto the updatetarget element, replacing the text i already written. I want to prevent it to write the raw response to the updatetarget. I'm aware of the InsertionMode property, but its useless to me because it appends text to the element one way or another.
The scripts i mentioned are below;
The code of the action link on view:
<%: Ajax.ActionLink("Delete", "Delete",
new { id = Model.Id, secretKey = Model.SecretKey },
new AjaxOptions { OnComplete = "WriteJsonResultToElement", UpdateTargetId="commandResult" })
%>
The WriteJsonResultToElement function
function WriteJsonResultToElement(resultObject) {
updateTarget = resultObject.get_updateTarget();
obj = resultObject.get_object();
$(updateTarget).text(obj.message); // here i set the text of update target
if (obj.result > 0)
$('*:contains("' + obj.id + '")').last().parent().remove();
}
My JsonResult Delete method returns this data after action:
{"message":"Deleted","result":1,"id":132}
Thanks.

If you don't want the raw JSON response appended to the DOM don't specify an UpdateTargetId:
<%: Ajax.ActionLink(
"Delete",
"Delete",
new { id = Model.Id, secretKey = Model.SecretKey },
new AjaxOptions { OnComplete = "success" })
%>
and handle it in the success callback:
function success(result) {
var obj = result.get_object();
alert(obj.message);
// TODO: do something with the object
}

Related

MiniProfiler Throwing Unexpected Token with Ajax.BeginForm

I'm using MiniProfiler on an MVC 4 app. We have a view being rendered in a modal (using the Colorbox jquery plugin). That view then has a partial view in it with an ajax form that looks like this:
#using(Ajax.BeginForm("<action name>", "<controller name>", new {area="<area name>"}, new AjaxOptions
{
UpdateTargetId = "modal-body",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST"
}))
{
<html for form here>
}
When we submit the form it returns the same partial view to overwrite this whole section on the view. When it's posted MiniProfiler throws an error: SyntaxError: Unexpected token ,
This happens in this function:
var jQueryAjaxComplete = function (e, xhr, settings) {
if (xhr) {
// should be an array of strings, e.g. ["008c4813-9bd7-443d-9376-9441ec4d6a8c","16ff377b-8b9c-4c20-a7b5-97cd9fa7eea7"]
var stringIds = xhr.getResponseHeader('X-MiniProfiler-Ids');
if (stringIds) {
var ids = typeof JSON != 'undefined' ? JSON.parse(stringIds) : eval(stringIds);
fetchResults(ids);
}
}
};
It's expecting to a json array of guids, but instead it's getting the array twice, like this:
"["6de0e02c-e694-4d8a-ac22-ea6a847efe0e","970f6640-fe5b-45d9-bf59-c916b665458d"], ["6de0e02c-e694-4d8a-ac22-ea6a847efe0e","970f6640-fe5b-45d9-bf59-c916b665458d"]"
This causes it to puke when it tries to parse the array. I'm not sure why the array is getting duplicated. Any help would be greatly appreciated. Thanks!
This a miniprofiler bug https://github.com/MiniProfiler/ui/pull/5
Wait for next update.

Calling multiple action methods (using ajax) and showing the result of last in a new tab

I have a form in which I need to call two action methods, one after the other. This is how the flow goes.
First I check if the prerequisite data is entered by the user. If not then I show a message that user needs to enter the data first.
If all the prerequisite data is entered, I call an action method which return data. If there is no data returned then I show a message "No data found" on the same page.
If data is returned then I call another action method present in a different controller, which returns a view with all the data, in a new tab.
The View:
#using (Ajax.BeginForm("Index", "OrderListItems", null, new AjaxOptions { OnBegin = "verifyRequiredData"}, new { #id = "formCreateOrderListReport", #target = "_blank" }))
{
//Contains controls and a button
}
The Script in this View:
function verifyRequiredData() {
if ($("#dtScheduledDate").val() == "") {
$('#dvValidationSummary').html("");
var errorMessage = "";
errorMessage = "<span>Please correct the following errors:</span><ul>";
errorMessage += "<li>Please enter Scheduled date</li>";
$('#dvValidationSummary').append(errorMessage);
$('#dvValidationSummary').removeClass('validation-summary-valid').addClass('validation-summary-errors');
return false;
}
else {
$('#dvValidationSummary').addClass('validation-summary-valid').removeClass('validation-summary-errors');
$('#dvValidationSummary').html("");
$.ajax({
type: "GET",
url: '#Url.Action("GetOrderListReport", "OrderList")',
data: {
ScheduledDate: $("#dtScheduledDate").val(),
Crews: $('#selAddCrewMembers').val(),
Priorities: $('#selPriority').val(),
ServiceTypes: $('#selServiceTypes').val(),
IsMeterInfoRequired: $('#chkPrintMeterInfo').val()
},
cache: false,
success: function (data) {
debugger;
if (data !== "No data found") {
//var newUrl = '#Url.Action("Index", "OrderListItems")';
//window.open(newUrl, '_blank');
return true;
} else {
//Show message "No data found"
return false;
}
}
});
return false;
}
}
The "GetOrderListReport" Action method in "OrderList" Controller:
public ActionResult GetOrderListReport(OrderListModel model)
{
var contract = new OrderReportDrilldownParamDataContract
{
ScheduledDate = model.ScheduledDate
//Setting other properties as well
};
var result = OrderDataModel.GetOrderList(contract);
if (string.IsNullOrWhiteSpace(result) || string.IsNullOrEmpty(result))
{
return Json("No data found", JsonRequestBehavior.AllowGet);
}
var deserializedData = SO.Core.ExtensionMethods.DeserializeObjectFromJson<OrderReportDrilldownDataContract>(result);
// send it to index method for list
TempData["DataContract"] = deserializedData;
return Json(deserializedData, JsonRequestBehavior.AllowGet);
}
The last action method present in OrderListItems Controller, the result of which needs to be shown in a new tab:
public ActionResult Index()
{
var deserializedData = TempData["DataContract"] as OrderReportDrilldownDataContract;
var model = new OrderListItemViewModel(deserializedData);
return View(model);
}
The problem is that I am not seeing this data in a new tab, although I have used #target = "_blank" in the Ajax.BeginForm. I have also tried to use window.open(newUrl, '_blank') as can be seen above. But still the result is not shown in a new tab.
Please assist as to where I am going wrong?
If you are using the Ajax.BeginForm you shouldn't also be doing an ajax post, as the unobtrusive ajax library will automatically perform an ajax post when submitting the form.
Also, if you use a view model with data annotation validations and client unobtrusive validations, then there would be no need for you to manually validate the data in the begin ajax callback as the form won't be submitted if any validation errors are found.
The only javascript code you need to add in this scenario is a piece of code for the ajax success callback. That will look as the one you currently have, but you need to take into account that opening in new tabs depends on the browser and user settings. It may even be considered as a pop-up by the browser and blocked, requiring the user intervention to allow them as in IE8. You can give it a try on this fiddle.
So this would be your model:
public class OrderListModel
{
[Required]
public DateTime ScheduledDate { get; set; }
//the other properties of the OrderListModel
}
The form will be posted using unobtrusive Ajax to the GetOrderListReport of the OrderList controller. On the sucess callback you will check for the response and when it is different from "No data found", you will then manually open the OrderListItems page on a new tab.
This would be your view:
#model someNamespace.OrderListModel
<script type="text/javascript">
function ViewOrderListItems(data){
debugger;
if (data !== "No data found") {
var newUrl = '#Url.Action("Index", "OrderListItems")';
//this will work or not depending on browser and user settings.
//passing _newtab may work in Firefox too.
window.open(newUrl, '_blank');
} else {
//Show message "No data found" somewhere in the current page
}
}
</script>
#using (Ajax.BeginForm("GetOrderListReport", "OrderList", null,
new AjaxOptions { OnSucces= "ViewOrderListItems"},
new { #id = "formCreateOrderListReport" }))
{
#Html.ValidationSummary(false)
//input and submit buttons
//for inputs, make sure to use the helpers like #Html.TextBoxFor(), #Html.CheckBoxFor(), etc
//so the unobtrusive validation attributes are added to your input elements.
//You may consider using #Html.ValidationMessageFor() so error messages are displayed next to the inputs instead in the validation summary
//Example:
<div>
#Html.LabelFor(m => m.ScheduledDate)
</div>
<div>
#Html.TextBoxFor(m => m.ScheduledDate, new {id = "dtScheduledDate"})
#Html.ValidationMessageFor(m => m.ScheduledDate)
</div>
<input type="submit" value="Get Report" />
}
With this in place, you should be able to post the data in the initial page using ajax. Then based on the response received you will open another window\tab (as mentioned, depending on browser and user settings this may be opened in a new window or even be blocked) with the second page content (OrderListItems).
Here's a skeleton of what I think you are trying to do. Note that window.open is a popup though and most user will have popups blocked.
<form id="formCreateOrderListReport">
<input type="text" vaule="testing" name="id" id="id"/>
<input type="submit" value="submit" />
</form>
<script type="text/javascript">
$('#formCreateOrderListReport').on('submit', function (event) {
$.ajax({
type: "POST",
url: '/home/test',
data: { id: $('#id').val()},
cache: false
}).done(function () {
debugger;
alert("success");
var newUrl = '/home/contact';
window.open(newUrl, '_blank');
}).fail(function () {
debugger;
alert("error");
});
return false;
});
</script>
Scale down the app to get the UI flow that you want then work with data.

Ajax.BeginForm with OnComplete always updates page

I have simple ajax form in MVC. In AjaxOptions there is OnComplete set to simple javascript function which does one thing - returns false.
#using (Ajax.BeginForm("Action", "Controller", new AjaxOptions { UpdateTargetId = "DivFormId", HttpMethod = "Post", OnComplete = "preventUpdate" }))
function preventUpdate(xhr) {
return false;
}
The problem is, that page is already updated. E.g. in one case controller returns partial view after postback, in other case it returns some Json object. I want it to update page when partial view is returned, and to show dialog window when json is returned. Unfortunately when json is returned, it clears the page (update it with json) even when OnComplete function returns false as MSDN says: To cancel the page update, return false from the JavaScript function.
How to prevent page update depending on received response?
Thank you!
----- UPDATE -------
So far I found following solution. When I don't specify UpdateTargetId, I can do manually with the response what I want. But it is still not documented behaviour with return false.
Use OnSuccess and get rid of UpdateTargetId. Like this:
#using (Ajax.BeginForm("Action", "Controller", new AjaxOptions { HttpMethod = "Post", OnSuccess = "foo" }))
{
...
}
and then:
function foo(result) {
if (result.SomePropertyThatExistsInYourJsonObject) {
// the server returned a JSON object => show the dialog window here
} else {
// the server returned a partial view => update the DOM:
$('#DivFormId').html(result);
}
}

call to controller to populate text box based on dropdownlistfor selection using Ajax

I have a dropdown and when I select an item from it, I want to pass on the selected value to a function in a controller, query the db and auto load a text box with query results.
How do I use Ajax to make that call to the controller when there is onclick() event on the dropdown?
My dropdown and textbox in my aspx page is:
<%: Html.DropDownListFor(model => model.ApplicationSegmentGuid, Model.ApplicationSegment)%>
<%: Html.TextAreaFor(model => model.EmailsSentTo, false, new { style = "width:500px; height:50px;" })%>
My function in controller is
public ActionResult AsyncFocalPoint(Nullable<Guid> ApplicationSegmentGuid)
{
string tempEmail = UnityHelper.Resolve<IUserDirectory>().EmailOf();
tempEmail = "subbulakshmi.kailasam#lyondellbasell.com" + tempEmail;
IList<string> EmailAddresses = new List<String>();
using (TSADRequestEntities context = UnityHelper.Resolve<TSADRequestEntities>())
{
EmailAddresses = context.FOCALPOINTs.Where(T => T.APPLICATIONSEGMENT.ItemGuid == ApplicationSegmentGuid && T.FlagActive)
.Select(T => T.Email).ToList();
}
foreach (string emailAddress in EmailAddresses)
tempEmail = tempEmail + ";" + emailAddress;
return Json(tempEmail, JsonRequestBehavior.AllowGet);
}
You could give your dropdown an id and url:
<%= Html.DropDownListFor(
model => model.ApplicationSegmentGuid,
Model.ApplicationSegment,
new { id = "myddl", data_url = Url.Action("AsyncFocalPoint") }
) %>
and then subscribe to the .change() event of the dropdown list unobtrusively and trigger the AJAX request:
$(function() {
$('#myddl').change(function() {
// get the selected value of the ddl
var value = $(this).val();
// get the url that the data-url attribute of the ddl
// is pointing to and which will be used to send the AJAX request to
var url = $(this).data('url');
$.ajax({
url: url,
type: 'POST',
data: { applicationSegmentGuid: value },
success: function(result) {
// TODO: do something with the result returned by the server here
// for example if you wanted to show the results in your textarea
// you could do this (it might be a good idea to override the id
// of the textarea as well the same way we did with the ddl):
$('#EmailsSentTo').val(result);
}
});
});
});

Getting JSonResult from ASP's Ajax.ActionLink

How do I actually get the JSON from a controller method using Ajax.ActionLink? I tried searching the website, but the closest thing I got was ASP.NET MVC controller actions that return JSON or partial html
And the "best answer" doesn't actually tell you how to get JSON from the SomeActionMethod in the ajax.actionlink.
Personally I don't like the Ajax.* helpers. In ASP.NET MVC < 3 they pollute my HTML with javascript and in ASP.NET MVC 3 they pollute my HTML with HTML 5 data-* attributes which are totally redundant (such as the url of an anchor). Also they don't automatically parse the JSON objects in the success callbacks which is what your question is about.
I use normal Html.* helpers, like this:
#Html.ActionLink(
"click me", // linkText
"SomeAction", // action
"SomeController", // controller
null, // routeValues
new { id = "mylink" } // htmlAttributes
)
which obviously generate normal HTML:
click me
and which I unobtrusively AJAXify in separate javascript files:
$(function() {
$('#mylink').click(function() {
$.post(this.href, function(json) {
// TODO: Do something with the JSON object
// returned the your controller action
alert(json.someProperty);
});
return false;
});
});
Assuming the following controller action:
[HttpPost]
public ActionResult SomeAction()
{
return Json(new { someProperty = "Hello World" });
}
UPDATE:
As requested in the comments section here's how to do it using the Ajax.* helpers (I repeat once again, that's just an illustration of how this could be achieved and absolutely not something I recommend, see my initial answer for my recommended solution):
#Ajax.ActionLink(
"click me",
"SomeAction",
"SomeController",
new AjaxOptions {
HttpMethod = "POST",
OnSuccess = "success"
}
)
and inside the success callback:
function success(data) {
var json = $.parseJSON(data.responseText);
alert(json.someProperty);
}

Resources