Poner HTML dentro de Html.ActionLink (), más ¿Sin texto de enlace?

169

Tengo dos preguntas:

  1. Me pregunto cómo no puedo mostrar ningún texto de enlace cuando lo uso Html.ActionLink()en una vista MVC (en realidad, esto es Site.Master).

No hay una versión sobrecargada que no permita el texto del enlace, y cuando intento pasar solo un espacio en blanco string, el compilador me dice que necesita una cadena no vacía.

¿Cómo puedo arreglar esto?

  1. Necesito poner <span>etiquetas dentro de la etiqueta de anclaje, pero no funciona Html.ActionLink();. Me gustaría ver el siguiente resultado:

    Texto de extensión

¿Cómo puedo poner etiquetas dentro de la etiqueta de anclaje en ASP.NET MVC?

MegaMatt
fuente
¿Cuál sería el propósito / uso de tener un enlace de acción en blanco?
David
1
Estoy usando un sprite de imagen para una barra de navegación, y lo <li>que está viendo es un botón de navegación particular (con tamaño, posición de fondo, etc., especificado en la hoja de estilo CSS). Pero debe vincularse a algo, por lo que no quiero mostrar el texto. Quiero que el sprite haga eso por mí.
MegaMatt

Respuestas:

322

En lugar de usar Html.ActionLink, puede representar una url a través de Url.Action

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

Y para hacer una url en blanco podrías tener

<a href="<%= Url.Action("Index", "Home") %>"></a>
<a href="@Url.Action("Index", "Home")"></a>
David
fuente
3
Tuve que usar <a href="@Url.Action("Index", "Home")"> <span> Text </span> </a>, mi desarrollador no está cerca para preguntar por qué tuve que hacerlo esto, pero puede ser útil para cualquiera que intente usar la respuesta anterior y descubra que no funcionó.
Dave Haigh
2
@ Url.Action es cuando se usa la plantilla de afeitar. He actualizado la respuesta para que puedas ver ambas.
David
<a [email protected]("Create", "Product")><span>Create</span></a>También funciona. Las cotizaciones no son obligatorias.
David
1
Para contenido estático, ¿por qué no escribir el html directamente? Parece menos detallado y más simple
SkeetJon
Ya no es una opción real en Asp.Net Core 2 si quieres usar Ajax.
Zorkind
17

Una extensión HtmlHelper personalizada es otra opción. Nota : ParameterDictionary es mi propio tipo. Podría sustituir un RouteValueDictionary pero tendría que construirlo de manera diferente.

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();
}
tvanfosson
fuente
14

Aquí hay una solución alternativa (baja y sucia) en caso de que necesite usar ajax o alguna característica que no pueda usar al hacer un enlace manualmente (usando la etiqueta):

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

Puede usar cualquier texto en lugar de 'LinkTextToken', solo está para ser reemplazado, solo es importante que no ocurra en ningún otro lugar dentro de actionlink.

Goran Obradovic
fuente
66
+1 Buena idea. En la maquinilla de afeitar, que tendrá que golpear todo eso enHtml.Raw()
Carrie Kendall
Gracias :) Ahora que veo lo que usamos, casi me da vergüenza, hacer estas cosas en el servidor es una pérdida de recursos del servidor ...
Goran Obradovic
1
Como estoy usando @ Ajax.ActionLink y no tenía ganas de configurar manualmente todos los date-atributos, esta es la mejor solución para mí. +1
asontu
Gracias, funcionó de maravilla cuando también estaba usando Ajax.ActionLink. No pareció ralentizar nada y pude mantener el mismo diseño y estilo.
Blacky Wolf
Solo quiero decir que esto no funciona en .Net Core, ToHtmlString () se ha eliminado en v2 no estoy seguro acerca de v1
Zorkind
12

Solo use en Url.Actionlugar de Html.ActionLink:

<li id="home_nav"><a href="<%= Url.Action("ActionName") %>"><span>Span text</span></a></li>
Craig Stuntz
fuente
@CraigStunz ¿por qué deberíamos usar Url.Action en lugar de Html.ActionLink?
Roxy'Pro
6

Esto siempre me ha funcionado bien. No es desordenado y muy limpio.

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

dbarth
fuente
Url.Action no tiene un constructor que tomará htmlAttributes. No es lo mismo que ActionLink.
barrypicker
2

Terminé con un método de extensión personalizado. Vale la pena señalar que, cuando se trata de colocar HTML dentro de un objeto Anchor, el texto del enlace puede estar a la izquierda o a la derecha del HTML interno. Por esta razón, opté por proporcionar parámetros para el HTML interno izquierdo y derecho: el texto del enlace está en el medio. Tanto el HTML interno izquierdo como el derecho son opcionales.

Método de extensión 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;
    }

Ejemplo de uso:

Aquí hay un ejemplo de uso. Para este ejemplo, solo quería el html interno en el lado derecho del texto del enlace ...

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

Resultados:

esto da como resultado el siguiente HTML ...

<a href="/SomethingOtherThanHome/SomethingOtherThanIndex">Hello World<span class="caret" /></a>
barrypicker
fuente
1

Pensé que esto podría ser útil al usar bootstrap y algunos 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>

Esto mostrará una etiqueta A, con un enlace a un controlador, con un buen ícono de clip para representar un enlace de descarga, y la salida html se mantiene limpia

Terry Kernan
fuente
1

Aquí hay una gran expansión de la respuesta de @ tvanfosson. Me inspiró y decidí hacerlo más genérico.

    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);
        }
    }
william-kid
fuente
1
Es genial que te hayas inspirado y tengas la capacidad de hacerlo. Sin embargo, miro las declaraciones foreach anidadas y quiero ejecutar. Simplemente no es mantenible por la mayoría de los desarrolladores. Si está bastante establecido en el método de extensión de tipo caja de piedra negra, entonces posiblemente esté bien, pero tiene un olor a código. No es nada fácil para los ojos para algo que es bastante simple. Gracias por tus esfuerzos sin embargo.
Tom Stickel
Estoy muy de acuerdo con los bucles anidados. Por el bien de la concisión está ahí. Desde un punto de vista de la optimización sí, el anidarse para el lazo debe estar en su propio método privado, y los parámetros tienen que ser afirmado, las necesidades de código a ser mucho más defensivo, etc.
William-kid
0

Es muy simple.

Si desea tener algo como un icono de glifo y luego "Lista de deseos",

<span class="glyphicon-heart"></span> @Html.ActionLink("Wish List (0)", "Index", "Home")
DanKodi
fuente
0

Mi solución usando componentes de arranque:

<a class="btn btn-primary" href="@Url.Action("resetpassword", "Account")">
    <span class="glyphicon glyphicon-user"></span> Reset Password
</a>
Carlos Toledo
fuente
0

Por favor, intente debajo del Código que puede ayudarlo.

 @Html.ActionLink(" SignIn", "Login", "Account", routeValues: null, htmlAttributes: new {  id = "loginLink" ,**@class="glyphicon glyphicon-log-in"** }) 
Ahsanul
fuente
simplemente agregue, @ class y luego la clase css real me ayudan a resolver mi problema
Ahsanul