Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC - Getting Html.BeginForm() to remember Querystring params when submitting via GET

I have a form rendered via Html.BeginForm(), it exists as a component in the Master page so that it appears on every page in the application. I have done this using Html.RenderAction() from Mvc Futures assembly. It's a simple search form that updates some items in the same component underneigh the search form itself, and performs a GET so that the search term appears in the querystring.

<div class="sideBarContent">
   <h2>Search Products</h2>

   <% using (Html.BeginForm(ViewContext.RouteData.Values["action"].ToString(),
         ViewContext.RouteData.Values["controller"].ToString(), FormMethod.Get)) { %>

      <fieldset>
         <legend>Search Products</legend>

         <div class="formRow">
            <label for="ProductsSearch">Search</label>
            <%= Html.TextBox("ProductsSearch") %>
         </div>

         <input type="submit" value="Search" class="button" />
       </fieldset>

   <% } %>

   <ul>
      // Products will eventually be listed here
   </ul>
</div>

I need this form to do the following:

1) It should perform a GET to whatever current page it is on appending 'ProductsSearch' as a querystring parameter (eg. example.com/?ProductsSearch=test or example.com/books/fiction?ProductsSearch=test)

2) It should remember any exising querystring parameters that are already in the querystring, maintaining them after you click Search button eg. example.com/myOrders?page=2 after Search click it should go to example.com/myOrders?page=2&ProductsSearch=test)

I can get it to do 1) but can't work out 2).

I relise that normally for a from to GET and appending querystring params it needs to have hidden form fields, so I could write a utility function that automatically adds a bunch of hidden form fields for any querystring values, but I wanted to check that there's wasn't an easier approach, or maybe I'm going about it the wrong way.

Cheers!

like image 663
Sunday Ironfoot Avatar asked Jan 23 '23 09:01

Sunday Ironfoot


2 Answers

You'll need to do the hidden form field method.

Even if you could attach the entire querystring to the end of the URL in the action attribute of the <form> tag, browsers don't pay attention to this when doing GET form submissions.

Your method isn't too difficult; you'd want to do something like this:

public static string QueryStringAsHidden(this HtmlHelper helper)
{
    var sb = new StringBuilder();
    foreach (var key in HttpContext.Current.Request.QueryString.AllKeys)
    {
        if (! key.StartsWith("ProductSearch"))
            sb.Append(helper.Hidden(key, HttpContext.Current.Request.QueryString[key]));
    }
        return sb.ToString();
    }

I put the .StartsWith() in there because you don't want to be on a search page and submit the search string twice (and now you can prepend paging and other search-specific variables with ProductSearch.

Edit: PS: To get the form to post to the current page, you don't have to explicitly provide action and controller -- you can also send nulls.

Edit2: Why even bother with a helper method? :)

<% HttpContext.Current.Request.QueryString.AllKeys.Where(k => !k.StartsWith("ProductSearch")).ToList().ForEach(k => Response.Write(Html.Hidden(k, HttpContext.Current.Request.QueryString[k]))); %>

James

like image 179
James S Avatar answered Jan 25 '23 22:01

James S


A direct to call BeginForm() does keep your query string values. Any other overload tends to fail. I love the ease of using BeginForm() from my forms, but needed a way to class all my styled forms a certain way an not lose the query string values in the action.

Here is what I came up with:

public static MvcForm BeginNormalForm<T>(this HtmlHelper<T> htmlHelper)
{
    var dictionary = new Dictionary<string, object> {{"class", "normal"}};
    var rvd = new RouteValueDictionary();
    if (htmlHelper.ViewContext.HttpContext != null && htmlHelper.ViewContext.HttpContext.Request != null)
    {
        foreach (var key in htmlHelper.ViewContext.HttpContext.Request.QueryString.AllKeys)
        {
            rvd[key] = htmlHelper.ViewContext.HttpContext.Request.QueryString[key];
        }
    }
    var form = htmlHelper.BeginForm(null, null, rvd, FormMethod.Post, dictionary);
    return form;
}

Seems to work well and keeps my class attribute.

like image 31
Hal Avatar answered Jan 25 '23 21:01

Hal