2

I am implementing a search filter to one of my application's views. I struggle at getting routeValues passed to controller action using @Html.BeginForm() and GET request.

The action accepts the following properties:

public ActionResult Books(int id, string type, string search)
{
    //rest of the code
}

The View's search box looks like this:

@model ILookup<string, CityLibrary.Models.Library.Book>
.... 
@using (Html.BeginForm("Books", "Collections", new { id = Model.First().First().CollectionId, type = ViewBag.BookType }, FormMethod.Get, null))
{
    <div class="input-group col-md-4">
        @Html.TextBox("search", null, new { @class = "form-control form-control-fixed-width", @placeholder = "Filter title..." })
        <span class="input-group-btn">
            <button class="btn btn-default" type="submit">
                <span class="glyphicon glyphicon-search"></span>
            </button>
        </span>
    </div>
}

The problem occurs when I am submitting the search box. The controller action gets the id and search string, but type is always null, even though ViewBag.BookType is not null. Fiddler shows this:

GET /Collections/Books/2?search=searchterm

Which seems to be completely ignoring type parameter in the request.

Source code in browser:

<form action="/Collections/Books/2?type=available" method="get">
    <div class="input-group col-md-4">
        <input class="form-control form-control-fixed-width" id="search" name="search" placeholder="Filter title..." type="text" value="" />
        <span class="input-group-btn">
            <button class="btn btn-default" type="submit">
               <span class="glyphicon glyphicon-search"></span>
            </button>
        </span>
    </div>
</form> 

Does it have something to do with GET method? I would like to avoid POSTing as I would have to write another controller action with basically the same code.

EDIT: It seems that the problem occurs when I try to use GET request. POSTing the form actually passes all the parameters to the controller action. Why is that?

6
  • It suggests that ViewBag.BookType is null. Add a <div>@ViewBag.BookType</div> in your view and confirm it. Commented May 28, 2016 at 11:38
  • It does not. The @ViewBag.BookType is also used in the View at the top (a simple if/else statement to display a correct header). I also tried to hardcode the type property. @using (Html.BeginForm("Books", "Collections", new { id = Model.First().First().CollectionId, type = "available" }, FormMethod.Get, null)) also returns type of null. Commented May 28, 2016 at 11:43
  • Do you actually have a route defined with ..../{id}/{type} (if not then a route value cannot be added) Commented May 28, 2016 at 12:02
  • I do not want to specify a custom route. I would like this all to be passed as querystring, like: /Collections/Books/2?type=available&search=searchterm. Commented May 28, 2016 at 12:20
  • 1
    The add a hidden input for type. And if you have the default route, then you do not even need the new { id = Model.First().First().CollectionId } code Commented May 28, 2016 at 12:24

1 Answer 1

1

This behavior is in accordance with the HTML specifications, In particular for a form with method="get", (my emphasis)

Mutate action URL

Let destination be a new URL that is equal to the action except that its <query> component is replaced by query (adding a U+003F QUESTION MARK character (?) if appropriate).

So the query string value in your form's action attribute is replaced with the query string generated by the name/value pairs of the form controls.

Two options to solve this:

  1. Remove the new { type = ViewBag.BookType } from the BeginForm() method and add a hidden input for the parameter

    <input type="hidden" name="type" value="@ViewBag.BookType" />
    
  2. Create a custom route definition for the method so that type is added as a route parameter, not a query string value (note this must be before the Default route)

    routes.MapRoute(
        name: "Books",
        url: "Collections/Books/{id}/{type}",
        defaults: new { controller = "Collections", action = "Books" }
    );
    

    so that your current BeginForm() code will generate

    <form action="/Collections/Books/2/available" method="get">
    

and the form submit will result in a url of Collections/Books/2/available?search=searchterm

Sign up to request clarification or add additional context in comments.

3 Comments

Also, it's worth mentioning (especially for those not so experienced in MVC like me), that the custom MapRoute has to be placed before the Default one, otherwise it's not working.
@Dandry, I already did - at the end of the second point - enclosed in brackets :)
Pardon me. This is what happens when one is too much focused on source code rather than plain text. Thank you alot!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.