Putting HTML inside Html.ActionLink(), plus No Link Text?
Asked Answered
L

12

177

I have two questions:

  1. I'm wondering how I can display no link text when using Html.ActionLink() in an MVC view (actually, this is Site.Master).

There is not an overloaded version that does not allow link text, and when I try passing in just a blank string, the compiler tells me it needs a non-empty string.

How can I fix this?

  1. I need to put <span> tags within the anchor tag, but it's not working with Html.ActionLink();. I'd like to see the following output:

    Span text

How can I put tags inside of the anchor tag in ASP.NET MVC?

Lomax answered 29/12, 2009 at 14:44 Comment(2)
What would be the purpose/use of having a blank action link?Labiovelar
I'm using an image sprite for a navigation bar, and the <li> you're seeing is a particular navigation button (with size, background pos, etc specified in css stylesheet). But it must link to something, so I don't want to display the text. I want the sprite to do that for me.Lomax
L
348

Instead of using Html.ActionLink you can render a url via Url.Action

<a href="<%= Url.Action("Index", "Home") %>"><span>Text</span></a>
<a href="@Url.Action("Index", "Home")"><span>Text</span></a>

And to do a blank url you could have

<a href="<%= Url.Action("Index", "Home") %>"></a>
<a href="@Url.Action("Index", "Home")"></a>
Labiovelar answered 29/12, 2009 at 14:49 Comment(5)
I had to use <a href="@Url.Action("Index", "Home")"><span>Text</span></a>, my dev isn't around to ask why I had to do this but it may be helpful for anyone tring to use the above answer and finding it didnt work.Fibula
@Url.Action is when using razor template. I've updated the answer so you can see both.Labiovelar
<a [email protected]("Create", "Product")><span>Create</span></a> also works. The quotes are not required.Labiovelar
For static content why not type the html directly? It seems less verbose and simplerWatson
Not a real option in Asp.Net Core 2 anymore if you want to use Ajax.Gaeta
E
16

A custom HtmlHelper extension is another option. Note: ParameterDictionary is my own type. You could substitute a RouteValueDictionary but you'd have to construct it differently.

public static string ActionLinkSpan( this HtmlHelper helper, string linkText, string actionName, string controllerName, object htmlAttributes )
{
    TagBuilder spanBuilder = new TagBuilder( "span" );
    spanBuilder.InnerHtml = linkText;

    return BuildNestedAnchor( spanBuilder.ToString(), string.Format( "/{0}/{1}", controllerName, actionName ), htmlAttributes );
}

private static string BuildNestedAnchor( string innerHtml, string url, object htmlAttributes )
{
    TagBuilder anchorBuilder = new TagBuilder( "a" );
    anchorBuilder.Attributes.Add( "href", url );
    anchorBuilder.MergeAttributes( new ParameterDictionary( htmlAttributes ) );
    anchorBuilder.InnerHtml = innerHtml;

    return anchorBuilder.ToString();
}
Exponible answered 29/12, 2009 at 15:4 Comment(0)
A
14

Here is (low and dirty) workaround in case you need to use ajax or some feature which you cannot use when making link manually (using tag):

<%= Html.ActionLink("LinkTextToken", "ActionName", "ControllerName").ToHtmlString().Replace("LinkTextToken", "Refresh <span class='large sprite refresh'></span>")%>

You can use any text instead of 'LinkTextToken', it is there only to be replaced, it is only important that it does not occur anywhere else inside actionlink.

Annatto answered 6/6, 2012 at 11:38 Comment(5)
+1 Nice idea. In Razor, you'll have to rap all of that in Html.Raw()Mendel
Thanks :) Now that I see what we used, I almost get embarrassed, doing these things on the server is such waste of server resources...Annatto
As I'm using @Ajax.ActionLink and didn't feel like manually setting all the date- attributes, this is the best solution for me. +1Gardas
Thanks, worked like a charm when I was also using Ajax.ActionLink. It didn't seem to slow down anything and I got to keep the same layout and styling.Lennon
Just want to say, this does not work in .Net Core, ToHtmlString() has been removed in v2 not sure about v1Gaeta
E
13

Just use Url.Action instead of Html.ActionLink:

<li id="home_nav"><a href="<%= Url.Action("ActionName") %>"><span>Span text</span></a></li>
Each answered 29/12, 2009 at 14:48 Comment(1)
@CraigStunz why should we use Url.Action instead of Html.ActionLink ?Max
C
7

This has always worked well for me. It's not messy and very clean.

<a href="@Url.Action("Index", "Home")"><span>Text</span></a>

Con answered 7/4, 2015 at 15:23 Comment(2)
Url.Action does not have a constructor that will take htmlAttributes. Not the same as ActionLink.Invent
That's because it creates a URL, not an a tagAmorphism
I
3

I ended up with a custom extension method. Its worth noting, when trying to place HTML inside of an Anchor object, the link text can be either to the left, or to the right of the inner HTML. For this reason, I opted to provide parameters for left and right inner HTML - the link text is in the middle. Both left and right inner HTML are optional.

Extension Method ActionLinkInnerHtml:

    public static MvcHtmlString ActionLinkInnerHtml(this HtmlHelper helper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues = null, IDictionary<string, object> htmlAttributes = null, string leftInnerHtml = null, string rightInnerHtml = null)
    {
        // CONSTRUCT THE URL
        var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
        var url = urlHelper.Action(actionName: actionName, controllerName: controllerName, routeValues: routeValues);

        // CREATE AN ANCHOR TAG BUILDER
        var builder = new TagBuilder("a");
        builder.InnerHtml = string.Format("{0}{1}{2}", leftInnerHtml, linkText, rightInnerHtml);
        builder.MergeAttribute(key: "href", value: url);

        // ADD HTML ATTRIBUTES
        builder.MergeAttributes(htmlAttributes, replaceExisting: true);

        // BUILD THE STRING AND RETURN IT
        var mvcHtmlString = MvcHtmlString.Create(builder.ToString());
        return mvcHtmlString;
    }

Example of Usage:

Here is an example of usage. For this example I only wanted the inner html on the right side of the link text...

@Html.ActionLinkInnerHtml(
    linkText: "Hello World"
        , actionName: "SomethingOtherThanIndex"
        , controllerName: "SomethingOtherThanHome"
        , rightInnerHtml: "<span class=\"caret\" />"
        )

Results:

this results in the following HTML...

<a href="/SomethingOtherThanHome/SomethingOtherThanIndex">Hello World<span class="caret" /></a>
Invent answered 10/8, 2016 at 20:36 Comment(0)
W
1

I thought this might be useful when using bootstrap and some glypicons:

<a class="btn btn-primary" 
    href="<%: Url.Action("Download File", "Download", 
    new { id = msg.Id, distributorId = msg.DistributorId }) %>">
    Download
    <span class="glyphicon glyphicon-paperclip"></span>
</a>

This will show an A tag, with a link to a controller, with a nice paperclip icon on it to represent a download link, and the html output is kept clean

Wira answered 18/4, 2014 at 20:40 Comment(0)
W
1

Here is an uber expansion of @tvanfosson's answer. I was inspired by it and decide to make it more generic.

    public static MvcHtmlString NestedActionLink(this HtmlHelper htmlHelper, string linkText, string actionName,
        string controllerName, object routeValues = null, object htmlAttributes = null,
        RouteValueDictionary childElements = null)
    {
        var htmlAttributesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

        if (childElements != null)
        {
            var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);

            var anchorTag = new TagBuilder("a");
            anchorTag.MergeAttribute("href",
                routeValues == null
                    ? urlHelper.Action(actionName, controllerName)
                    : urlHelper.Action(actionName, controllerName, routeValues));
            anchorTag.MergeAttributes(htmlAttributesDictionary);
            TagBuilder childTag = null;

            if (childElements != null)
            {
                foreach (var childElement in childElements)
                {
                    childTag = new TagBuilder(childElement.Key.Split('|')[0]);
                    object elementAttributes;
                    childElements.TryGetValue(childElement.Key, out elementAttributes);

                    var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(elementAttributes);

                    foreach (var attribute in attributes)
                    {
                        switch (attribute.Key)
                        {
                            case "@class":
                                childTag.AddCssClass(attribute.Value.ToString());
                                break;
                            case "InnerText":
                                childTag.SetInnerText(attribute.Value.ToString());
                                break;
                            default:
                                childTag.MergeAttribute(attribute.Key, attribute.Value.ToString());
                                break;
                        }
                    }
                    childTag.ToString(TagRenderMode.SelfClosing);
                    if (childTag != null) anchorTag.InnerHtml += childTag.ToString();
                }                    
            }
            return MvcHtmlString.Create(anchorTag.ToString(TagRenderMode.Normal));
        }
        else
        {
            return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributesDictionary);
        }
    }
Wheelhouse answered 5/7, 2014 at 19:27 Comment(2)
It is great that you were inspired and have the ability to do this. However, I look at nested foreach statements and want to run. It simply it not maintainable by most developers. If it is pretty set in stone black box type extension method then possibly ok, but it has a code smell to it. Not easy on the eyes at all for something that is pretty simple. Thanks for your efforts though.Chromate
I very much agree about nested for loops. For the sake of succinctness it is there. From an optimization stand point yes the nested for loop should be in its own private method, and parameters need to be asserted, the code needs to be much more defensive, etc.Wheelhouse
E
0

It's very simple.

If you want to have something like a glyphicon icon and then "Wish List",

<span class="glyphicon-heart"></span> @Html.ActionLink("Wish List (0)", "Index", "Home")
Expertize answered 21/5, 2014 at 11:23 Comment(0)
F
0

Please try below Code that may help you.

 @Html.ActionLink(" SignIn", "Login", "Account", routeValues: null, htmlAttributes: new {  id = "loginLink" ,**@class="glyphicon glyphicon-log-in"** }) 
Freshet answered 14/3, 2015 at 21:42 Comment(1)
just simple add ,@class and then actual css class help me to solve my problemFreshet
T
0

My solution using bootstrap components:

<a class="btn btn-primary" href="@Url.Action("resetpassword", "Account")">
    <span class="glyphicon glyphicon-user"></span> Reset Password
</a>
Tarrant answered 26/8, 2015 at 16:59 Comment(0)
H
0

please try this code:

@Html.Raw(@Html.ActionLink("text", "ActionName", "ControllerName", null, new { area = "Home" }).ToHtmlString().Replace("text", "<i class='fa fa-car sub-icon-mg' aria-hidden='true'></i> <span class='mini-sub-pro'>Link Text</span>"))
Hereof answered 13/1, 2022 at 11:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.