Variation on Adding HTML Attributes to Html.BeginForm()

I need a form on my ASP.NET MVC Razor page. My preference would be to use the following syntax:

@using (Html.BeginForm())
{
}

However, I need several attributes added to the form. So I ended up with something like the following:

@using (Html.BeginForm(null, null, FormMethod.Post, new { name = "value" }))
{
}

However, this has an undesired side effect. If there are query arguments in this page's request, the first form passes them along when the form is submitted. However, the second version does not.

I really don't know why BeginForm() doesn't support attributes, but is there a straight-forward way to add attributes to BeginForm() and still pass along any query arguments when the for is submitted?

EDIT:

After looking into this, it would seem the best solution is something like this:

<form action="@Request.RawUrl" method="post" name="value">
</form>

However, when using this syntax, client-side validation is disabled. It seems there is no good solution to this situation without more complicated and potentially unreliable constructs.

Answers


That's indeed true, but I would go with a custom helper in order to preserve the form context inside which is used for client side validation:

public static class FormExtensions
{
    private static object _lastFormNumKey = new object();

    public static IDisposable BeginForm(this HtmlHelper htmlHelper, object htmlAttributes)
    {
        string rawUrl = htmlHelper.ViewContext.HttpContext.Request.RawUrl;
        return htmlHelper.FormHelper(rawUrl, FormMethod.Post, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    private static int IncrementFormCount(IDictionary items)
    {
        object obj2 = items[_lastFormNumKey];
        int num = (obj2 != null) ? (((int)obj2) + 1) : 0;
        items[_lastFormNumKey] = num;
        return num;
    }

    private static string DefaultFormIdGenerator(this HtmlHelper htmlhelper)
    {
        int num = IncrementFormCount(htmlhelper.ViewContext.HttpContext.Items);
        return string.Format(CultureInfo.InvariantCulture, "form{0}", new object[] { num });
    }

    private static IDisposable FormHelper(this HtmlHelper htmlHelper, string formAction, FormMethod method, IDictionary<string, object> htmlAttributes)
    {
        var builder = new TagBuilder("form");
        builder.MergeAttributes<string, object>(htmlAttributes);
        builder.MergeAttribute("action", formAction);
        builder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
        bool flag = htmlHelper.ViewContext.ClientValidationEnabled && !htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled;
        if (flag)
        {
            builder.GenerateId(htmlHelper.DefaultFormIdGenerator());
        }
        htmlHelper.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag));
        var form = new MvcForm(htmlHelper.ViewContext);
        if (flag)
        {
            htmlHelper.ViewContext.FormContext.FormId = builder.Attributes["id"];
        }
        return form;
    }
}

which could be used like this:

@using (Html.BeginForm(htmlAttributes: new { name = "value" }))
{
    ...
}

I had a similar problem and here is quick solution (it works with MVC4).

Declare the extension method:

public static MvcForm BeginForm(this HtmlHelper helper, object htmlAttributes)
{
        return helper.BeginForm(helper.ViewContext.RouteData.Values["Action"].ToString(),
                                helper.ViewContext.RouteData.Values["Controller"].ToString(), 
                                FormMethod.Post, htmlAttributes);
}

and use it in your page:

@using (Html.BeginForm(htmlAttributes: new {@class="form-horizontal"})) 
{
...
}

Small modification to source code:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/Html/FormExtensions.cs

public static MvcForm BeginForm(this HtmlHelper htmlHelper, object htmlAttributes)
{
   // generates <form action="{current url}" method="post">...</form>
   string formAction = htmlHelper.ViewContext.HttpContext.Request.RawUrl;
   return FormHelper(htmlHelper, formAction, FormMethod.Post, new RouteValueDictionary(htmlAttributes));
}

private static MvcForm FormHelper(this HtmlHelper htmlHelper, string formAction, FormMethod method, IDictionary<string, object> htmlAttributes)
{
   TagBuilder tagBuilder = new TagBuilder("form");
   tagBuilder.MergeAttributes(htmlAttributes);
   // action is implicitly generated, so htmlAttributes take precedence.
   tagBuilder.MergeAttribute("action", formAction);
   // method is an explicit parameter, so it takes precedence over the htmlAttributes.
   tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);

   bool traditionalJavascriptEnabled = htmlHelper.ViewContext.ClientValidationEnabled
                                       && !htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled;

   if (traditionalJavascriptEnabled)
   {
       // forms must have an ID for client validation
       tagBuilder.GenerateId(htmlHelper.ViewContext.FormIdGenerator());
   }

   htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
   MvcForm theForm = new MvcForm(htmlHelper.ViewContext);

   if (traditionalJavascriptEnabled)
   {
       htmlHelper.ViewContext.FormContext.FormId = tagBuilder.Attributes["id"];
   }

   return theForm;
}

Need Your Help

External JS files won't recognize elements in html

javascript jquery html

This is probably the weirdest thing I've encountered while programming in JavaScript, and I can't find a solution anywhere online.

Creating a script for a Telnet session?

scripting telnet

Does anyone know of an easy way to create a script that can connect to a telnet server, do some usual telnet stuff, and then log off? I am dealing with users who are not familiar with telnet and the