Mvc Html.ActionButton
Asked Answered
T

7

11

Has anyone written one? I want it to behave like a link but look like a button. A form with a single button wont do it has I don't want any POST.

Turkey answered 23/5, 2009 at 11:31 Comment(0)
G
18

The easiest way to do it is to have a small form tag with method="get", in which you place a submit button:

<form method="get" action="/myController/myAction/">
    <input type="submit" value="button text goes here" />
</form>

You can of course write a very simple extension method that takes the button text and a RouteValueDictionary (or an anonymous type with the routevalues) and builds the form so you won't have to re-do it everywhere.

EDIT: In response to cdmckay's answer, here's an alternative code that uses the TagBuilder class instead of a regular StringBuilder to build the form, mostly for clarity:

using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;

namespace MvcApplication1
{
    public static class HtmlExtensions
    {
        public static string ActionButton(this HtmlHelper helper, string value, 
                              string action, string controller, object routeValues)
        {
            var a = (new UrlHelper(helper.ViewContext.RequestContext))
                        .Action(action, controller, routeValues);

            var form = new TagBuilder("form");
            form.Attributes.Add("method", "get");
            form.Attributes.Add("action", a);

            var input = new TagBuilder("input");
            input.Attributes.Add("type", "submit");
            input.Attributes.Add("value", value);

            form.InnerHtml = input.ToString(TagRenderMode.SelfClosing);

            return form.ToString(TagRenderMode.Normal);
        }
    }
}

Also, as opposed to cdmckay's code, this one will actually compile ;) I am aware that there might be quite a lot of overhead in this code, but I am expecting that you won't need to run it a lot of times on each page. In case you do, there is probably a bunch of optimizations that you could do.

Govea answered 23/5, 2009 at 11:42 Comment(5)
Other than not having the using statements, what is stopping my example from compiling? (I haven't tested it, I'm just curious)Accepter
Well, for one thing, the Url.Action() method isn't available except for in the view, because Url is a property of the ViewPage/ViewUserControl, that maps to an instance of a UrlHelper. Instead of just referencing a property that doesn't exist, i create an instance based on the current RequestContext. I see now that you have changed your code to "var action = Html.ActionLink(...)", which would compile if you used "var action = helper.ActionLink(...)", but it would give you the entire <a> tag, which you don't want in the action property of the form...Govea
This does not work well if your "ActionButton" happens to be within a form already!Freespoken
For any ASP.NET MVC newbies like myself who might struggle with implementing this, try passing it as an argument to MvcHtmlString.Create. Ex: @MvcHtmlString.Create(Html.ActionButton(value, action, controller, routeValues))Baudoin
You can use @Html.ActionButton("Create New", "Create", "ASSETs", null) if you return an MvcHtmlString and just change the return statement to return new MvcHtmlString(form.ToString()); @C1phrAlessandro
S
13

If you want it to behave like a link but "look" like a button just use the ActionLink with a CSS Class.

<%: Html.ActionLink("Back", "Index", null, new { @class = "link-button" })%>

Here is the CSS for a Button that I am using.

.link-button {
    -moz-border-radius:0.333em 0.333em 0.333em 0.333em;
    -moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.4);
    background:-moz-linear-gradient(center top , white, #306AB5 4%, #274976) repeat scroll 0 0 transparent;
    border-color:#306AB5 #2B5892 #274771;
    border-style:solid;
    border-width:1px;
    color:white;
    cursor:pointer;
    display:inline-block;
    font-size:1.167em;
    font-weight:bold;
    line-height:1.429em;
    padding:0.286em 1em 0.357em;
    text-shadow:0 1px 2px rgba(0, 0, 0, 0.4);
}


.link-button {
    color: white;
    border-color: #a1a7ae #909498 #6b7076;
    background: #9fa7b0 url(../images/old-browsers-bg/button-element-grey-bg.png) repeat-x top;
    background: -moz-linear-gradient(
        top,
        white,
        #c5cbce 5%,
        #9fa7b0
    );
    background: -webkit-gradient(
        linear,
        left top, left bottom,
        from(white),
        to(#9fa7b0),
        color-stop(0.05, #c5cbce)
    );
    -moz-text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
    -webkit-text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
    -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
    -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
    }
    .link-button:hover {
        border-color: #a1a7b0 #939798 #6e7275;
        background: #b1b5ba url(../images/old-browsers-bg/button-element-grey-hover-bg.png) repeat-x top;
        background: -moz-linear-gradient(
            top,
            white,
            #d6dadc 4%,
            #b1b5ba
        );
        background: -webkit-gradient(
            linear,
            left top, left bottom,
            from(white),
            to(#b1b5ba),
            color-stop(0.03, #d6dadc)
        );
    }
    .link-button:active {
        border-color: #666666 #ffffff #ffffff #979898;
        background: #dddddd url(../images/old-browsers-bg/button-element-grey-active-bg.png) repeat-x top;
        background: -moz-linear-gradient(
            top,
            #f1f1f1,
            #dddddd
        );
        background: -webkit-gradient(
            linear,
            left top, left bottom,
            from(#f1f1f1),
            to(#dddddd)
        );
    }
Slabber answered 22/10, 2010 at 22:26 Comment(0)
F
6

Two versions to make an extension for...

<button 
    onclick="javascript:window.location=('@Url.Action("Review", "Order", null)')"
    >Review Order</button>

Unobtrusive version:

<button data-action="@Url.Action("Review", "Order", null)">Review Order</button>

$(document).on('click', "[data-action]", 
    function(e) { window.location = $(this).attr('data-action'); }
);

If you have users which do not have javascript turned on, then form tag is the way to go. Although, this make the situation difficult if your link is already within a form. However, you could change the action and method to GET.

Freespoken answered 27/7, 2011 at 11:7 Comment(0)
A
3

Code for Tomas' answer:

public static class HtmlExtensions
{
  public static string ActionButton(this HtmlHelper helper, string value, 
      string action, string controller, object routeValues)
  {
    UrlHelper urlHelper = new UrlHelper(helper.ViewContext);
    var action = urlHelper.Action(action, controller, routeValues);

    var html = new StringBuilder();
    html.AppendFormat("<form method='get' action'{0}'>", action).AppendLine()
        .AppendFormat("    <input type='submit' value='{0}' />", value).AppendLine()
        .AppendFormat("</form>").AppendLine();

    return html.ToString();
  }
}
Accepter answered 28/6, 2009 at 18:32 Comment(3)
In response to your edit: you can't use Html.ActionLink() - use helper.ActionLink if you want that. However, that will produce an entire anchor tag (and currently with an incorrect url, seeing as the first parameter is the link text and the second is the action name...) and that is not what we want here.Govea
Whoops, I had Url.Action() before, then I second guessed myself. Anyway, there ya go.Accepter
Also replace Url with urlHelper.Accepter
C
0

I modified Tomas Lyckens code to return a MvcHtmlString instead of just a string. This makes sure the output is HTML, not escaped as text. I also xml-documented it nicely. Thanks to Tomas who did all the real work.

    /// <summary>
    /// Returns an HTML submit button (enclosed in its own form) that contains the virtual path of the specified action.
    /// </summary>
    /// <param name="helper">The HTML helper instance that this method extends.</param>
    /// <param name="buttonText">The visible text of the button.</param>
    /// <param name="action">The name of the action.</param>
    /// <param name="controller">The name of the controller.</param>
    /// <param name="routeValues">An object that contains the parameters for a route. The parameters are retrieved through reflection by examining the properties of the object. The object is typically created by using object initializer syntax.</param>
    /// <returns></returns>
    public static MvcHtmlString ActionButton(this HtmlHelper helper, string buttonText, string action, string controller, object routeValues)
    {
        string a = (new UrlHelper(helper.ViewContext.RequestContext)).Action(action, controller, routeValues);

        var form = new TagBuilder("form");
        form.Attributes.Add("method", "get");
        form.Attributes.Add("action", a);

        var input = new TagBuilder("input");
        input.Attributes.Add("type", "submit");
        input.Attributes.Add("value", buttonText);

        form.InnerHtml = input.ToString(TagRenderMode.SelfClosing);

        return MvcHtmlString.Create(form.ToString(TagRenderMode.Normal));
    } 
Canella answered 24/6, 2011 at 11:23 Comment(0)
B
0

Here is a VB.NET version with one extra thing, the controller and routes parameters are optional so it can be used without repeating the controller name if it's the same as the current/default controller for the page.

Public Module MvcExtensions

    ''' <summary> 
    ''' Returns an HTML submit button (enclosed in its own form) that contains the virtual path of the specified action. 
    ''' </summary> 
    ''' <param name="helper">The HTML helper instance that this method extends.</param> 
    ''' <param name="text">Text displayed in the button.</param> 
    ''' <param name="action">Action name.</param> 
    ''' <param name="controller">Optional controller name, using current when null.</param> 
    ''' <param name="routeValues">
    ''' An object that contains the parameters for a route. The parameters are retrieved
    ''' through reflection by examining the properties of the object.
    ''' The object is typically created by using object initializer syntax.
    ''' </param> 
    ''' <returns>
    ''' HTML output.
    ''' </returns> 
    <Extension()> _
    Public Function ActionButton(helper As HtmlHelper, text As String, action As String,
                                 Optional controller As String = Nothing, Optional routeValues As Object = Nothing) As MvcHtmlString

        ' Validate parameters
        If String.IsNullOrEmpty(text) Then Throw New ArgumentNullException("text")

        ' Get post back URL for action
        Dim actionUrl As String = New UrlHelper(helper.ViewContext.RequestContext).Action(action, controller, routeValues)

        ' Build form tag with action URL
        Dim form = New TagBuilder("form")
        form.Attributes.Add("method", "get")
        form.Attributes.Add("action", actionUrl)

        ' Add button
        Dim input = New TagBuilder("input")
        input.Attributes.Add("type", "submit")
        input.Attributes.Add("value", text)
        form.InnerHtml = input.ToString(TagRenderMode.SelfClosing)

        ' Return result as HTML
        Return MvcHtmlString.Create(form.ToString(TagRenderMode.Normal))

    End Function

End Module

Then it can be invoked like the other other MVC controls, with minimum code on the page.

This should really have gone into the core MVC framework from the start; it seems such an obvious requirement. I think buttons are much more intuitive for the user when performing actions which create or change things, rather than links. Links should just navigate to related information (not change anything). If i had a grid I would use ActionLink for any data navigation (e.g. click a product name to goto product page) but only ActionButton for real "actions" like edit and delete.

Beware answered 5/12, 2011 at 4:51 Comment(0)
M
0

If you want a button that functions like a link this should work:

<input type="button" value="Button Text"
  onclick="@("location.href='https://mcmap.net/q/957305/-mvc-html-actionbutton/2460971'")" />

If you want the button to use a controller action only a minor change is needed:

<input type="button" value="Button Text"
  onclick="@("location.href='" + Url.Action("ActionName", "ControllerName") + "'")" />
Microwatt answered 13/1, 2015 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.