Metoda HTML.ActionLink

249

Powiedzmy, że mam klasę

public class ItemController:Controller
{
    public ActionResult Login(int id)
    {
        return View("Hi", id);
    }
}

Na stronie, która nie znajduje się w folderze Element, w którym się ItemControllerznajduje, chcę utworzyć link do Loginmetody. Więc której Html.ActionLinkmetody powinienem użyć i jakie parametry powinienem przekazać?

W szczególności szukam zastąpienia metody

Html.ActionLink(article.Title,
    new { controller = "Articles", action = "Details",
          id = article.ArticleID })

który został wycofany w ostatnim wcieleniu ASP.NET MVC.

Grawiton
źródło
17
Dokumentacja dla każdego, kto jej szuka: msdn.microsoft.com/en-us/library/…
BlueRaja - Danny Pflughoeft
@Danny Dzięki, szukałem go w Google, kiedy tu trafiłem.
Rei Miyasaka

Odpowiedzi:

491

Myślę, że chcesz tego:

ASP.NET MVC1

Html.ActionLink(article.Title, 
                "Login",  // <-- Controller Name.
                "Item",   // <-- ActionMethod
                new { id = article.ArticleID }, // <-- Route arguments.
                null  // <-- htmlArguments .. which are none. You need this value
                      //     otherwise you call the WRONG method ...
                      //     (refer to comments, below).
                )

Wykorzystuje następującą metodę sygnatury ActionLink:

public static string ActionLink(this HtmlHelper htmlHelper, 
                                string linkText,
                                string controllerName,
                                string actionName,
                                object values, 
                                object htmlAttributes)

ASP.NET MVC2

zamieniono dwa argumenty

Html.ActionLink(article.Title, 
                "Item",   // <-- ActionMethod
                "Login",  // <-- Controller Name.
                new { id = article.ArticleID }, // <-- Route arguments.
                null  // <-- htmlArguments .. which are none. You need this value
                      //     otherwise you call the WRONG method ...
                      //     (refer to comments, below).
                )

Wykorzystuje następującą metodę sygnatury ActionLink:

public static string ActionLink(this HtmlHelper htmlHelper, 
                                string linkText,
                                string actionName,
                                string controllerName,
                                object values, 
                                object htmlAttributes)

ASP.NET MVC3 +

argumenty są w tej samej kolejności co MVC2, jednak wartość id nie jest już wymagana:

Html.ActionLink(article.Title, 
                "Item",   // <-- ActionMethod
                "Login",  // <-- Controller Name.
                new { article.ArticleID }, // <-- Route arguments.
                null  // <-- htmlArguments .. which are none. You need this value
                      //     otherwise you call the WRONG method ...
                      //     (refer to comments, below).
                )

Pozwala to uniknąć zakodowania na stałe logiki routingu w łączu.

 <a href="/Item/Login/5">Title</a> 

Otrzymasz następujące dane wyjściowe HTML, przy założeniu:

  1. article.Title = "Title"
  2. article.ArticleID = 5
  3. nadal masz zdefiniowaną następującą trasę

. .

routes.MapRoute(
    "Default",     // Route name
    "{controller}/{action}/{id}",                           // URL with parameters
    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
);
Joseph Kingry
źródło
7
Ale czy to nie podaje adresu URL takiego jak / Item / Login? Id = 5?
Adhip Gupta
21
Co dziwne, jeśli przegapisz ostatni parametr, który do mnie dołącza? Długość = 8 do bieżącej akcji
Chris S
32
@Chris S - Wiem, że to stary post, ale powodem? Długość = 8 jest to, że musisz mieć , nullparametr PO swoim new { ... }... ponieważ jeśli sprawdzisz przeciążenia tej metody, to myślisz, że twoje parametry są htmlArguments ... nie przekierowuj argumentów. Aby użyć poprawnej metody, musisz użyć metody, która ma routeArguments, htmlArguments.. więc po prostu podaj wartość null dla tego ostatniego htmlArgument. Pierwszy fragment kodu w tej odpowiedzi ma to. Zaktualizowałem ten post, abyś mógł go łatwo zobaczyć (tzn. Nie przewija się).
Pure.Krome
7
Czy ktoś próbował tego z MVC 3? Wygląda na to, że linie ControllerName i ActionMethod w powyższej próbce są odwrócone. Czy ktoś jeszcze to widział?
Steve Duitsman
8
W MVC3 nie znaleziono właściwości id ... zamiast tego należy użyć:@Html.ActionLink("Text","Action","Controller", new { item.ID }, null)
Gavin Coates
30

Chciałem dodać do odpowiedzi Josepha Kingry'ego . Dostarczył rozwiązanie, ale na początku nie mogłem go uruchomić i uzyskałem wynik taki jak Adhip Gupta. I wtedy zdałem sobie sprawę, że trasa musi przede wszystkim istnieć, a parametry muszą dokładnie pasować do trasy. Miałem więc identyfikator, a następnie parametr tekstowy dla mojej trasy, który również musiał zostać uwzględniony.

Html.ActionLink(article.Title, "Login", "Item", new { id = article.ArticleID, title = article.Title }, null)
Jeff Widmer
źródło
4
Właśnie tego potrzebowałem - zapomniałem dodać ostatni argument zerowy . Dzięki.
Ian Oxley
1
Dzięki za pokazanie odwzorowania również z nazwy parametru trasy (np. Nowy {id = ..., bar = ...}.
William Rose
17

Możesz przyjrzeć się RouteLink()metodzie, która pozwala określić wszystko (oprócz tekstu linku i nazwy trasy) za pomocą słownika.

Haacked
źródło
4
Byłoby wspaniale zobaczyć przykład, jak to rozwiązuje problem; strona MSDN ma wiele przeciążeń, a wiedza o tym, czego szukać, może być myląca
Simon Martin
14

Myślę, że Joseph zmienił kontroler i akcję. Najpierw jest akcja, potem kontroler. To trochę dziwne, ale sposób, w jaki wygląda podpis.

Żeby to wyjaśnić, działa ta wersja (adaptacja przykładu Józefa):

Html.ActionLink(article.Title, 
    "Login",  // <-- ActionMethod
    "Item",   // <-- Controller Name
    new { id = article.ArticleID }, // <-- Route arguments.
    null  // <-- htmlArguments .. which are none
    )
agez
źródło
11

a co z tym

<%=Html.ActionLink("Get Involved", 
                   "Show", 
                   "Home", 
                   new 
                       { 
                           id = "GetInvolved" 
                       }, 
                   new { 
                           @class = "menuitem", 
                           id = "menu_getinvolved" 
                       }
                   )%>
Hasan
źródło
10
Html.ActionLink(article.Title, "Login/" + article.ArticleID, 'Item") 
Adhip Gupta
źródło
To naprawdę powinno być oznaczone jako odpowiedź, ponieważ robi dokładnie to, czego szukała osoba zadająca pytanie ... jednak zauważę, że zaznaczona odpowiedź była bardzo szczegółowa dla użytkownika w prawidłowym konfigurowaniu tras w różnych wersjach MVC.
Indy-Jones,
9

Jeśli chcesz nosić wszystkie fantazyjne spodnie, oto jak możesz je rozszerzyć, aby móc to zrobić:

@(Html.ActionLink<ArticlesController>(x => x.Details(), article.Title, new { id = article.ArticleID }))

Będziesz musiał umieścić to w System.Web.Mvcprzestrzeni nazw:

public static class MyProjectExtensions
{
    public static MvcHtmlString ActionLink<TController>(this HtmlHelper htmlHelper, Expression<Action<TController>> expression, string linkText)
    {
        var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection);

        var link = new TagBuilder("a");

        string actionName = ExpressionHelper.GetExpressionText(expression);
        string controllerName = typeof(TController).Name.Replace("Controller", "");

        link.MergeAttribute("href", urlHelper.Action(actionName, controllerName));
        link.SetInnerText(linkText);

        return new MvcHtmlString(link.ToString());
    }

    public static MvcHtmlString ActionLink<TController, TAction>(this HtmlHelper htmlHelper, Expression<Action<TController, TAction>> expression, string linkText, object routeValues)
    {
        var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection);

        var link = new TagBuilder("a");

        string actionName = ExpressionHelper.GetExpressionText(expression);
        string controllerName = typeof(TController).Name.Replace("Controller", "");

        link.MergeAttribute("href", urlHelper.Action(actionName, controllerName, routeValues));
        link.SetInnerText(linkText);

        return new MvcHtmlString(link.ToString());
    }

    public static MvcHtmlString ActionLink<TController>(this HtmlHelper htmlHelper, Expression<Action<TController>> expression, string linkText, object routeValues, object htmlAttributes) where TController : Controller
    {
        var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection);

        var attributes = AnonymousObjectToKeyValue(htmlAttributes);

        var link = new TagBuilder("a");

        string actionName = ExpressionHelper.GetExpressionText(expression);
        string controllerName = typeof(TController).Name.Replace("Controller", "");

        link.MergeAttribute("href", urlHelper.Action(actionName, controllerName, routeValues));
        link.MergeAttributes(attributes, true);
        link.SetInnerText(linkText);

        return new MvcHtmlString(link.ToString());
    }

    private static Dictionary<string, object> AnonymousObjectToKeyValue(object anonymousObject)
    {
        var dictionary = new Dictionary<string, object>();

        if (anonymousObject == null) return dictionary;

        foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(anonymousObject))
        {
            dictionary.Add(propertyDescriptor.Name, propertyDescriptor.GetValue(anonymousObject));
        }

        return dictionary;
    }
}

Obejmuje to dwa przesłonięcia, Route Valuesa HTML Attributestakże wszystkie Twoje widoki musiałyby zostać dodane: @using YourProject.Controllerslub możesz dodać to do swojegoweb.config <pages><namespaces>

Serj Sagan
źródło
1
Dziwi mnie, że bardziej nie używam tego podejścia. Używanie literałów łańcuchowych w swoich widokach do reprezentowania kontrolera / akcji wydaje się bardzo niebezpieczne.
Johnathon Sullinger
Szukałem tego przez całe życie
Worthy7
Próbowałem tego, nie działało. Dał mi w końcu pusty ciąg - zakładam, bo mam parametry w swoich funkcjach.
Worthy7
Czy możesz opublikować github lub inne miejsce z tym kodem, abym mógł sprawdzić, dlaczego to nie działa?
Serj Sagan,
2
Miłe użycie słowa fancypants. Nie widzimy tego wystarczająco.
gdbj
7

Użyj nazwanych parametrów, aby zwiększyć czytelność i uniknąć nieporozumień.

@Html.ActionLink(
            linkText: "Click Here",
            actionName: "Action",
            controllerName: "Home",
            routeValues: new { Identity = 2577 },
            htmlAttributes: null)
guneysus
źródło
1

Z MVC5 zrobiłem to w ten sposób i jest to w 100% działający kod ....

@Html.ActionLink(department.Name, "Index", "Employee", new { 
                            departmentId = department.DepartmentID }, null)

Możecie sobie wyobrazić ...

Sohail Malik
źródło
0

Użyj tego typu:

@ Html.ActionLink („Strona główna”, „Indeks”, „Strona główna”)

Strona główna: Nazwa tekstu Indeks: Widok akcji Strona główna: HomeController

Baza Użyj ActionLink

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>_Layout</title>
    <link href="@Url.Content("~/Content/bootsrap.min.css")" rel="stylesheet" type="text/css" />
</head>
<body>
    <div class="container">
        <div class="col-md-12">
            <button class="btn btn-default" type="submit">@Html.ActionLink("AnaSayfa","Index","Home")</button>
            <button class="btn btn-default" type="submit">@Html.ActionLink("Hakkımızda", "Hakkimizda", "Home")</button>
            <button class="btn btn-default" type="submit">@Html.ActionLink("Iletişim", "Iletisim", "Home")</button>
        </div> 
        @RenderBody()
        <div class="col-md-12" style="height:200px;background-image:url(/img/footer.jpg)">

        </div>
    </div>
</body>
</html>

Serdin çelik
źródło