Problem using #Ajax.ActionLink in MVC when passing the htmlAttributes - asp.net-mvc

When I call #Ajax.ActionLink in my .vbhtml view using the htmlAttributes:
#Ajax.ActionLink("LinkText",
"Action",
routeValues:=baseController.PathParams(New With {.id = Model.Icodciud}),
ajaxOptions:=New AjaxOptions() With {.HttpMethod = "POST", .UpdateTargetId = "myPanel", .InsertionMode = InsertionMode.InsertBefore},
htmlAttributes:=New With {.class = "btn btn-primary"})
The result in the page is this one:
<a class="btn btn-primary" data-ajax="true" data-ajax-method="POST" data-ajax-mode="before" data-ajax-update="#myPanel"
href="/Controller/Action?Count=2&Keys=System.Collections.Generic.Dictionary%602%2BKeyCollection%5BSystem.String%2CSystem.Object%5D&Values=System.Collections.Generic.Dictionary%602%2BValueCollection%5BSystem.String%2CSystem.Object%5D">LinkText</a>
But if I remove the htmlAttributes parameter:
#Ajax.ActionLink("LinkText",
"Action",
routeValues:=baseController.PathParams(New With {.id = Model.Id}),
ajaxOptions:=New AjaxOptions() With {.HttpMethod = "POST", .UpdateTargetId = "myPanel", .InsertionMode = InsertionMode.InsertBefore})
Then the result is this one (it works good):
<a data-ajax="true" data-ajax-method="POST" data-ajax-mode="before" data-ajax-update="#myPanel"
href="/Controller/Action?Id=1&Other=Params">LinkText</a>
The baseController.PathParams method always returns a RouteValueDictionary:
Public Function PathParams(Optional params As Object = Nothing) As RouteValueDictionary
Dim dictionary As IDictionary(Of String, Object) = New Dictionary(Of String, Object)()
... some code ...
Return New RouteValueDictionary(dictionary)
End Function
So it can't be the problem. The problem is when I use htmlAttributes or not.
Any ideas?

I found the problem, If you want yo use the ActionLink method with routeValues as RouteValueDictionary then htmlAttributes should be an IDictionary:
Public Shared Function ActionLink(ajaxHelper As AjaxHelper, linkText As String, actionName As String, routeValues As Object, ajaxOptions As AjaxOptions, htmlAttributes As Object) As MvcHtmlString
Public Shared Function ActionLink(ajaxHelper As AjaxHelper, linkText As String, actionName As String, routeValues As RouteValueDictionary, ajaxOptions As AjaxOptions, htmlAttributes As IDictionary(Of String, Object)) As MvcHtmlString

Related

Adding HTML class to Ajax.RouteForm

I have an Ajax.Routeform whose systax is given as:`
#using (Ajax.BeginRouteForm("Contact", new AjaxOptions { HttpMethod = "POST", OnSuccess = "emailSuccess", OnFailure = "emailFailure" }))
{
}
I need to add a html class "form-horizontal" to it. How can I achieve this?
The method overload containg html attributes is
Ajax.Routeform( string routeName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes )
`
The method overload for htmlAttributes is
Ajax.Routeform( string routeName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes )
Since routeValues is not required I can pass null in it and the syntax is quite stright forward
#using (Ajax.BeginRouteForm("Contact", null, new AjaxOptions { ... }, new { #class = "form-horizontal" }))

Call an Action using Ajax.ActionLink does not work

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

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" })

Passing parameter to controller action from a Html.ActionLink

Is there anything wrong with this html? I want to have a link in the masterpage to navigate to "CreateParts" view. I have action 'CreateParts' which have a parameter parentPartId in the controller 'PartList'.
<li id="taskAdminPartCreate" runat="server">
<%= Html.ActionLink("Create New Part", "CreateParts", "PartList", new { parentPartId = 0 })%></li>
My controller action is like
public ActionResult CreateParts(int parentPartId)
{
HSPartList objHSPart = new HSPartList();
objHSPart.Id = parentPartId;
return View(objHSPart);
}
When I click on 'Create New Part' in the menu in SiteMaster, I get exception. Please help me out of this.
You are using incorrect overload. You should use this overload
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
Object routeValues,
Object htmlAttributes
)
And the correct code would be
<%= Html.ActionLink("Create New Part", "CreateParts", "PartList", new { parentPartId = 0 }, null)%>
Note that extra parameter at the end.
For the other overloads, visit LinkExtensions.ActionLink Method. As you can see there is no string, string, string, object overload that you are trying to use.
You are using the incorrect overload of ActionLink. Try this
<%= Html.ActionLink("Create New Part", "CreateParts", "PartList", new { parentPartId = 0 }, null)%>
Addition to the accepted answer:
if you are going to use
#Html.ActionLink("LinkName", "ActionName", "ControllerName", new { #id = idValue, #secondParam= = 2 },null)
this will create actionlink where you can't create new custom attribute or style for the link.
However, the 4th parameter in ActionLink extension will solve that problem. Use the 4th parameter for customization in your way.
#Html.ActionLink("LinkName", "ActionName", "ControllerName", new { #id = idValue, #secondParam= = 2 }, new { #class = "btn btn-info", #target = "_blank" })

Why does Html.ActionLink render "?Length=4"

I'm VERY confused as to why this code
Html.ActionLink("About", "About", "Home", new { hidefocus = "hidefocus" })
results in this link:
<a hidefocus="hidefocus" href="/Home/About?Length=4">About</a>
The hidefocus part is what I was aiming to achieve, but where does the ?Length=4 come from?
The Length=4 is coming from an attempt to serialize a string object. Your code is running this ActionLink method:
public static string ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes)
This takes a string object "Home" for routeValues, which the MVC plumbing searches for public properties turning them into route values. In the case of a string object, the only public property is Length, and since there will be no routes defined with a Length parameter it appends the property name and value as a query string parameter. You'll probably find if you run this from a page not on HomeController it will throw an error about a missing About action method. Try using the following:
Html.ActionLink("About", "About", new { controller = "Home" }, new { hidefocus = "hidefocus" })
The way I solved this is was adding a null to the fourth parameter before the anonymous declaration (new {}) so that it uses the following method overload: (linkText, actionName, controllerName, routeValues, htmlAttributes):
Html.ActionLink("About", "About", "Home", null, new { hidefocus = "hidefocus" })
You forgot to add the HTMLAttributes parm.
This will work without any changes:
Html.ActionLink("About", "About", "Home", new { hidefocus = "hidefocus" },null)
The parameters to ActionLink are not correct, it's attempting to use the "Home" value as a route value, instead of the anonymous type.
I believe you just need to add new { } or null as the last parameter.
EDIT: Just re-read the post and realized you'll likely want to specify null as the second last parameter, not the last.
Html.ActionLink("About", "About", "Home", new { hidefocus = "hidefocus" }, new { })
This will take the overload:
string linkText, string actionName, string controllerName, Object routeValues, Object htmlAttributes
Just remove "Home" (name of the controller) so that the code would be:
Html.ActionLink("About", "About", new { hidefocus = "hidefocus" })
Kindly use right overloaded method with five (5) parameters. Example:
#using (#Ajax.BeginForm("Register", "Account", null,
new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "OnSuccess",
OnFailure = "OnFailure",
OnBegin = "OnBegin",
OnComplete = "OnComplete"
}, new { #class = "form-login" }))
This worked fine
#Html.ActionLink("Informationen", "About", "Home", new { area = "" }, new { #class = "nav-link" })
added new { area = "" }.
As Jonathon Watney pointed out in a comment, this also goes for
Html.BeginForm()
methods. In my case, I was in a Create.cshtml targeting the post request of the corresponding controller + Create action and had
using (Html.BeginForm("Create")) {
#Html.AntiForgeryToken()
...
}
which was adding the querystring "?Length=6" to the form action when rendered. Hinted by roryf's approved answer and realizing the string length of "Create" is 6, I finally solved this by removing the explicit action specification:
using (Html.BeginForm()) {
#Html.AntiForgeryToken()
...
}
With attribute names:
#Html.ActionLink(linkText: "SomeText", actionName: "SomeAction", controllerName: "SomeControllerName", routeValues: new { parameterName = parameterValue}, htmlAttributes: null)
Perhaps others had the same issue and need to supply a class value via HTMLAttributes parm.
Here's my solution:
#Html.ActionLink("About", "About", new { controller = "Home", area = "" }, new { hidefocus = "hidefocus", #class = "nav-item nav-link" })
Search for an answer to my question landed me here, basically it's the selection of correct overload of #Html.ActionLink
which matters.
I was selecting an overload which didn't exist, (without the last null), and MVC had no such overload, resulting in a false URL something like the OP mentioned.
A personal note: you can use anonymous types doesn't mean you can use any of the overloads- which do not exist? - make certain: it has to be defined!
- Came here in times of MVC 5.2

Resources