Call an Action using Ajax.ActionLink does not work - asp.net-mvc

I have a really weirdo issue, I write :
#Ajax.RawActionLink(
"<i class=\"fa fa-print\"></i>",
"CustomerOrder",
"PrintSheet",
new AjaxOptions()
{
UpdateTargetId = "bodyContent",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
},
new
{
#class = "btn btn-success",
data_toggle = "tooltip",
data_placement = "top",
title = "Imprimer"
})
but I get :
<a class="btn btn-success"
data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-update="#bodyContent"
data-placement="top"
data-toggle="tooltip"
href="/Sales/CustomerOrder?Length=10"
title=""
data-original-title="Imprimer">
<i class="fa fa-print"></i>
</a>
in the rendered Html.
I'm calling the print CustomerOrder action from another controller but I get always the current controller in the path, any idea ?
Ps: I'm using an extension of Ajax ActionLink
public static MvcHtmlString RawActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, AjaxOptions ajaxOptions, object htmlAttributes)
{
var repID = Guid.NewGuid().ToString();
var lnk = ajaxHelper.ActionLink(repID, actionName, controllerName, ajaxOptions, htmlAttributes);
return MvcHtmlString.Create(lnk.ToString().Replace(repID, linkText));
}

Assuming that RawActionLink is wrapped around ActionLink it seems that you are targeting wrong overloaded method. Try:
#Ajax.RawActionLink(
"<i class=\"fa fa-print\"></i>", //content
"CustomerOrder", //action
"PrintSheet", //controller
new {}, //routing data <---- ADDED
new AjaxOptions() //ajax options
{
UpdateTargetId = "bodyContent",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
},
new //html attributes
{
#class = "btn btn-success",
data_toggle = "tooltip",
data_placement = "top",
title = "Imprimer"
})
https://msdn.microsoft.com/en-us/library/system.web.mvc.ajax.ajaxextensions.actionlink(v=vs.118).aspx#M:System.Web.Mvc.Ajax.AjaxExtensions.ActionLink%28System.Web.Mvc.AjaxHelper,System.String,System.String,System.String,System.Object,System.Web.Mvc.Ajax.AjaxOptions,System.Object%29

Related

Automatic Callbacks on Radio Button Changes in Ajax.Beginform ASP.net MVC

I have the following block of code in one of my Partial Pages.
Would like to know if the Ajax request can be made on change of the Radio Butto selection.
Thanks in advance.
#using (Ajax.BeginForm("_QueriesPartial", "Bug",
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
OnFailure = "searchFailed",
LoadingElementId = "ajax-loader",
UpdateTargetId = "Result",
}))
{
foreach (Query query in #Model.queries) {
<div class="radio">
#Html.RadioButton("UserQuery", query.id, new { onclick = "submitform()" })
#Html.Label(query.name, query.name)
</div>
}
}
The following works for me:
#using (Ajax.BeginForm("Action", "Controller", null,
new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "JavascriptUpdateMethod(data, target)",
Url = Url.Action("ActionAjax", "Controller")
}, new { id = AjaxFormId }))
{
#Html.Hidden("ActionMethodArg", f.Id)
<div class="radiobutton-inline">
#Html.Label("Default", new { style="width: 45px;"})
#Html.RadioButton("name", f.Id, trueorfalse,
new { onchange = "$('#AjaxFormId').trigger('submit');" })
</div>
}

Embed Input field to Hyperlink in MVC

Is it possible to pass an hidden field value from Razor View to Controller inside or tag? As the field is hidden, it is really a problem to pass its value to the Controller. On the other hand I think the only way to pass this hidden field is using input field inside hyperlink. How to create a code like below?
<a href="/Admin/Delete?ApplicantID=44">
<img class="myclass" src="../../Content/delete.png" title="Delete" />
<input type="hidden" value= "Delete" /></a>
Updates
View:
...
grid.Column("Actions", format: (item) =>
new HtmlString(
#Html.ActionImage("../../Content/detail.png", "Detail", "icon-link", "Detail", "Admin", new { applicantId = item.ApplicantID }).ToString() +
#Html.ActionImage("../../Content/edit.png", "Edit", "icon-link", "Edit", "Admin", new { applicantId = item.ApplicantID }).ToString() +
#Html.ActionImage("../../Content/delete.png", "Delete", "icon-link", "Delete", "Admin", new { applicantId = item.ApplicantID }).ToString()
)
)
...
Controller:
[HttpPost]
public ActionResult Delete(int applicantId)
{
Applicant deletedApplicant = repository.DeleteApplicant(applicantId);
if (deletedApplicant != null)
{
TempData["message"] = string.Format("{0} was deleted",
deletedApplicant.Name);
}
return RedirectToAction("Index");
}
}
Helper Method:
public static MvcHtmlString ActionImage(this HtmlHelper html, string imagePath, string alt, string cssClass,
string action, string controllerName, object routeValues)
{
var currentUrl = new UrlHelper(html.ViewContext.RequestContext);
var imgTagBuilder = new TagBuilder("img");
imgTagBuilder.MergeAttribute("src", currentUrl.Content(imagePath));
imgTagBuilder.MergeAttribute("title", alt);
imgTagBuilder.MergeAttribute("class", cssClass);
string imgHtml = imgTagBuilder.ToString(TagRenderMode.SelfClosing);
var anchorTagBuilder = new TagBuilder("a");
anchorTagBuilder.MergeAttribute("href", currentUrl.Action(action, controllerName, routeValues));
anchorTagBuilder.InnerHtml = imgHtml;
string anchorHtml = anchorTagBuilder.ToString(TagRenderMode.Normal);
return MvcHtmlString.Create(anchorHtml);
}
<input>, <textarea>, <button> and <select> element values are only passed during a form submission. Moreover, they are only passed on when assigned a name attribute.
Clicking an anchor will not pass these values (unless you interject with some JavaScript and append it to the URL). The easiest method is to turn your link in to a mini form:
#using (Html.BeginForm("Delete", "Admin", FormMethod.POST,
new { ApplicantID = #Model.ApplicantID }))
{
<!-- make sure to give this field a name -->
<input type="hidden" name="???" value="Delete" />
<input type="image" src="#Url.Content("~/Content/delete.png")" title="Delete" />
}
Otherwise, use javascript and bind to the anchor's click event and inject the hidden input's value before proceeding.
Based on discussion below, try this.
#Ajax.ActionLink("Delete",
"Delete", new { applicantid = item.applicantid },
new AjaxOptions
{
Confirm = "Delete?",
HttpMethod = "POST",
}, new { #class = "classname" })
a.classname
{
background: url(~/Content/delete.png) no-repeat top left;
display: block;
width: 150px;
height: 150px;
text-indent: -9999px; /* hides the link text */
}
NOTE: this does not need to be inside a form.

ViewModel not storing values when Ajax.ActionLink calling controller

When I'm clicking ActionLink and setting ViewModel values in controller I can see changes when View being rendered. But same values comes as null into Controller when I'm clicking ActionLink second time.
How do I store the value, so it comes into controller ?
View:
#Ajax.ActionLink("Click me", "AjaxTest", "Controller", new AjaxOptions()
{
UpdateTargetId = "updatePanel",
HttpMethod = "POST",
OnSuccess = "A()"
})
<div id="updatePanel">
#Html.Partial("~/Views/Shared/_UpdatableContent.cshtml", this.Model)
</div>
Controller:
[HttpPost]
public ActionResult AjaxTest(MyViewModel model)
{
model.A = "A"
return PartialView("_UpdatableContent", model);
}
Partial view _UpdatableContent:
#Html.HiddenFor(x => x.A)
#if (Model.A == "A")
{
//Draw
}
Try adding this.Model to your ActionLink following:
#Ajax.ActionLink("Click me", "AjaxTest", "Controller", this.Model, new AjaxOptions() { UpdateTargetId = "updatePanel" })
This method passes the model back into the request, which should allow the update to happen.
Probably my biggest gripe with ASP.NET MVC is the fact that the various "Helper" functions are overloaded to the nth-degree, and not always consistently in terms of the order the arguments appear...
Hope that helps :)
I had this very same problem. Setting HttpMethod = "Post" in the AjaxOptions fixed it for me, thanks Sergejs.
My final, working code is as follows
#{
AjaxOptions ajaxOptions = new AjaxOptions
{
HttpMethod = "Post",
LoadingElementId = "product-adding-" +#Model.Product.Id,
LoadingElementDuration = 100,
OnSuccess = "AddedToCart"
};
}
<div>
#Ajax.ActionLink("Add to cart",
"AddToCart",
"Cart",
new { id = Model.Product.Id, returnUrl = Request.Url.PathAndQuery },
ajaxOptions,
new { #class = "button" })
<img id="product-adding-#Model.Product.Id" src="~/Images/ajax_loader.gif" />
</div>

convert ajax actionlink as a bootstrap button

i have an ajax actionlink like this:
<div style="float:left"> #Ajax.ActionLink("EMPLOYEE", "_PartialEmployeeIndex", "Employee", new AjaxOptions() { UpdateTargetId = "divToUpdate" }) </div>
i usually use bootstrap to style my buttons like this:
<input class="btn btn-info" type="button" value="Input">
or like this
<button class="btn btn-success" type="submit"> </button>
so how can i convert an ajax action link to a bootstrap button?
i dont want to put a class name to the div containing the ajax actionlink because the button is displayed with black color font and with an underline...
i want it to be displayed as an actual button with no underline and with white font
You should be able to use the htmlAttributes parameter to add whatever Bootstrap class you want:
#Ajax.ActionLink("EMPLOYEE", "_PartialEmployeeIndex", "Employee", new AjaxOptions() { UpdateTargetId = "divToUpdate" }, new { #class = "btn" })
If you only want an icon you can do it as:
#Ajax.ActionLink(" ", "Delete", new { id = 1 }, new AjaxOptions
{
Confirm = "Are you sure you wish to delete?",
HttpMethod = "Delete",
InsertionMode = InsertionMode.Replace,
LoadingElementId = "div_loading"
}, new { #class = "glyphicon glyphicon-trash" })
The name ationlink cannot be null or empty, so i recommended an space.
If you want an actual Ajax button element, rather than a styling hack, it is also possible but a little involved. It is a shame that MS has not yet chosen to add an ActionButton to both the Html and Ajax helpers as the differences are actually very minor when you remove the duplication of private support methods (you would only need the ActionButton and GenerateButton methods shown below).
The end result is you can have real buttons that trigger like ajax action links:
e.g.
#Ajax.ActionButton("Delete", "Delete", "document",
new { id = ViewBag.Id },
new AjaxOptions()
{
Confirm="Do you really want to delete this file?",
HttpMethod = "Get",
UpdateTargetId = "documentlist" },
new { id = "RefreshDocuments"
})
1. Create an AjaxHelper extension
The code below is based on a decompile of the AjaxExtensions class as many of the required helper methods are not exposed on HtmlHelper.
public static partial class AjaxExtensions
{
public static MvcHtmlString ActionButton(this AjaxHelper ajaxHelper, string buttonText, string actionName, string controllerName, object routeValuesBlah, AjaxOptions ajaxOptions, object htmlAttributesBlah)
{
// Convert generic objects to specific collections
RouteValueDictionary routeValues = new RouteValueDictionary(routeValuesBlah);
RouteValueDictionary htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributesBlah);
if (string.IsNullOrEmpty(buttonText))
throw new ArgumentException("Button text must be provided");
string targetUrl = UrlHelper.GenerateUrl((string)null, actionName, controllerName, routeValues, ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, true);
return MvcHtmlString.Create(GenerateButton(ajaxHelper, buttonText, targetUrl, AjaxExtensions.GetAjaxOptions(ajaxOptions), htmlAttributes));
}
public static string GenerateButton(AjaxHelper ajaxHelper, string linkText, string targetUrl, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)
{
TagBuilder tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttribute("value", linkText);
tagBuilder.MergeAttributes<string, object>(htmlAttributes);
tagBuilder.MergeAttribute("href", targetUrl);
tagBuilder.MergeAttribute("type", "button");
if (ajaxHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
tagBuilder.MergeAttributes<string, object>(ajaxOptions.ToUnobtrusiveHtmlAttributes());
else
tagBuilder.MergeAttribute("onclick", AjaxExtensions.GenerateAjaxScript(ajaxOptions, "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), {0});"));
return tagBuilder.ToString(TagRenderMode.Normal);
}
private static string GenerateAjaxScript(AjaxOptions ajaxOptions, string scriptFormat)
{
string str = ajaxOptions.ToJavascriptString();
return string.Format((IFormatProvider)CultureInfo.InvariantCulture, scriptFormat, new object[1] { str });
}
private static AjaxOptions GetAjaxOptions(AjaxOptions ajaxOptions)
{
if (ajaxOptions == null)
return new AjaxOptions();
else
return ajaxOptions;
}
public static string ToJavascriptString(this AjaxOptions ajaxOptions)
{
StringBuilder stringBuilder = new StringBuilder("{");
stringBuilder.Append(string.Format((IFormatProvider)CultureInfo.InvariantCulture, " insertionMode: {0},", new object[1]
{
ajaxOptions.InsertionModeString()
}));
stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("confirm", ajaxOptions.Confirm));
stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("httpMethod", ajaxOptions.HttpMethod));
stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("loadingElementId", ajaxOptions.LoadingElementId));
stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("updateTargetId", ajaxOptions.UpdateTargetId));
stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("url", ajaxOptions.Url));
stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onBegin", ajaxOptions.OnBegin));
stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onComplete", ajaxOptions.OnComplete));
stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onFailure", ajaxOptions.OnFailure));
stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onSuccess", ajaxOptions.OnSuccess));
--stringBuilder.Length;
stringBuilder.Append(" }");
return ((object)stringBuilder).ToString();
}
public static string InsertionModeString(this AjaxOptions ajaxOptions)
{
switch (ajaxOptions.InsertionMode)
{
case InsertionMode.Replace:
return "Sys.Mvc.InsertionMode.replace";
case InsertionMode.InsertBefore:
return "Sys.Mvc.InsertionMode.insertBefore";
case InsertionMode.InsertAfter:
return "Sys.Mvc.InsertionMode.insertAfter";
default:
return ((int)ajaxOptions.InsertionMode).ToString((IFormatProvider)CultureInfo.InvariantCulture);
}
}
public static string EventStringIfSpecified(this AjaxOptions ajaxOptions, string propertyName, string handler)
{
if (string.IsNullOrEmpty(handler))
return string.Empty;
return string.Format((IFormatProvider)CultureInfo.InvariantCulture, " {0}: Function.createDelegate(this, {1}),",
new object[2]
{
propertyName,
handler
});
}
public static string PropertyStringIfSpecified(this AjaxOptions ajaxOptions, string propertyName, string propertyValue)
{
if (string.IsNullOrEmpty(propertyValue))
return string.Empty;
string str = propertyValue.Replace("'", "\\'");
return string.Format((IFormatProvider)CultureInfo.InvariantCulture, " {0}: '{1}',",
new object[2]
{
propertyName,
str
});
}
}
2. Modify jquery.unobtrusive-ajax.js
Only a small change is required to the JQuery of jquery.unobtrusive-ajax.js to accept the new button object, as it is very close to begin with. First the selector needs to accept buttons as well as links and then the href needs to come from an attribute so than a non-link can provide it (not strictly browser compliant but works for now).
$(document).on("click", "input[data-ajax=true],a[data-ajax=true]", function (evt) {
evt.preventDefault();
asyncRequest(this, {
url: $(this).attr("href"),
type: "GET",
data: []
});
});
*Note: this is using the latest version of everything as at the date of answering (MVC 5)
if you do not want to worry about assigning proper classes to every Bootstrap element, check out TwitterBootstrapMVC
In the example with your ajax link you'd write something like this:
#Ajax.ActionLink("EMPLOYEE", "_PartialEmployeeIndex", "Employee", ).AjaxOptions(new AjaxOptions() { UpdateTargetId = "divToUpdate" })
Adding to Terry answer, if you want to add html to the button, the best way is to use Javascript to append the html code. The linkText parameter of Ajax.Actionlink automatically encodes any text you provide and there is nothing you can do to avoid that (except writing your own helper).
Something like JQuery append or prepend would work.
<div>
#Ajax.ActionLink("EMPLOYEE", "_PartialEmployeeIndex", "Employee", new AjaxOptions() { UpdateTargetId = "divToUpdate" }, new { #class = "btn btn-default my-custom-class" })
</div>
<script>
$(".my-custom-class").prepend("<span class=\"glyphicon glyphicon-pencil\"></span> ");
</script>
An alternative is to use Ajax.BeginForm, which allows you to enter HTML directly. This assumes that you're not already in a form.
#using (Ajax.BeginForm("EMPLOYEE", "_PartialEmployeeIndex", "Employee", new AjaxOptions() { UpdateTargetId = "divToUpdate" }))
{
<button type="submit" id="EmployeeButton" title="Employee" aria-label="Employee Button">
<span class="glyphicon glyphicon-trash"></span>
</button>
}
#Ajax.ActionLink(" ", "EditUser/" + Model.Id, null, new AjaxOptions {
OnSuccess = "userEditGet",
HttpMethod = "post",
LoadingElementId = "ajaxLoader" }
,new { #class = "btn btn-default glyphicon glyphicon-edit" })

Issue with Ajax.ActionLink incorrectly rendering links when using htmlAttributes

Does anyone know of any issues with rendering incorrect querystrings when using htmlAttributes in an Ajax.ActionLink? It seems that if I put even an empty array in for the htmlAttributes, the link gets rendered incorrectly. Here's my code.
When I do this (note the new { }):
<%= Ajax.ActionLink("Delete", "Delete", "Milestone", new RouteValueDictionary { { "id", Model.Id } }, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "ModalDeleteContainer", OnSuccess = "modalDelete" }, new { })%>
The link renders like this:
Delete
When I do this (null instead of new { }):
<%= Ajax.ActionLink("Delete", "Delete", "Milestone", new RouteValueDictionary { { "id", Model.Id } }, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "ModalDeleteContainer", OnSuccess = "modalDelete" }, null)%>
The link renders like this:
Delete
The only difference between the two is the htmlAttributes argument at the end of the Ajax.ActionLink. Thanks for any insight!
You need to use the correct overload of the method. The one you are using takes an IDictionary and that's why it's rendering the way it is.
If you choose the object RouteValues and object htmlAttributes like this:
<%= Ajax.ActionLink("Delete", "Delete", "Milestone", new { id = Model.Id },
new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "ModalDeleteContainer",
OnSuccess = "modalDelete" }, new { })%>
it will all work!

Resources