views:

1621

answers:

2

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!

+6  A: 

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

James S
You could also make that more reusable by making the ProductSearch key check a param. You could also linq-ify it, eg QueryString.AllKeys.Where(x=>!x.StartsWith("ProductSearch"))
Dan Atkinson
True. And you could get rid of the whole foreach() loop... HttpContext.Current.Request.QueryString.AllKeys.Where(k => !k.StartsWith(except)).ToList().ForEach(k => sb.Append(helper.Hidden(k, HttpContext.Current.Request.QueryString[k]))); :)
James S
A: 

Use one of the overloads of BeginForm that takes a routeValues object or dictionary.

Additional properties not in the route will be added as query parameters.

Richard
James S