0

I have a form with a searchbox on it. When someone types something into the search box and hits the search button I am trying ot do a post to capture the search filter and then fire off a view.

Here is the controller code

public class SpotsController : Controller
{
    [HttpPost]
    [AllowAnonymous]
    public ActionResult SearchSpots(string searchfilter)
    {
        //your code here....
        return Index(searchfilter);
    }

Here is the code from my view up until the part that is tryign to do the submit

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - Haunt Spotter</title>
</head>
<form id="__AjaxAntiForgeryForm" action="#" method="post"><@Html.AntiForgeryToken()></form>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
        </div>
        <div class="navbar-collapse collapse">
            @using (Html.BeginForm("SearchSpots", "Spots"))
            {
                <input id="searchfilter" type="text" class="form-control" autocomplete="off" placeholder="Search" name="searchfilter">
                <button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
            }
        </div>
    </div>
</div>

If I take the parameter off of the controller function it works fine. If not it seems to crash and try to re display a get which fails because I only have a post function for this. I am guessing I have something screwed up with the parameter but I can't figure out what it is. Any help woudl be greatly appreciated.

UPDATE

Based on feedback I have changed my post to a get

    [HttpGet]
    [AllowAnonymous]
    public ActionResult SearchSpots(string searchfilter)
    {
        //your code here....
        return Index(searchfilter);
    }

and my view code to this

@using (Html.BeginForm("SearchSpots", "Spots", FormMethod.Get, null))
{
    <input id="searchfilter" type="text" class="form-control" autocomplete="off" placeholder="Search" name="searchfilter">
    <button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
}

Unfortunately I still have the original issue. If I remove the searchfileter parameter from my controller call then it goes into the call with no problems but when I am expecting the modelbinder to give me a searchfilter it crashes out.

Here is the call I am redirecting to in my search function

private ApplicationDbContext db = new ApplicationDbContext();

    // GET: Spots
    public ActionResult Index(string filter = "")
    {
        ViewBag.initialFilter = filter;
        if (User.IsInRole("SiteAdmin"))
        {
            return View(db.Spots.ToList());
        }
        else
        {
            return View(db.Spots.Where(x => x.Approved).ToList());
        }

    }

and the view that is displayed

@model IEnumerable<HauntSpots.Models.Spot>

@{
    ViewBag.Title = "Index";
}

<h2 class="align-right">Haunt Spots</h2>

@if (Context.User.IsInRole("SiteAdmin"))
{ 
    <p style="padding-top:20px">

    <a href="@Url.Action("Create")" title="Add New Spot" class="btn btn-primary"><i class="icon-plus-sign"></i> Add New</a>
    </p>
}

<table id="dt-spots" class="table table-striped">
    <thead>
        <tr>
        <th></th>
        <th></th>
        <th></th>
        @if (Context.User.IsInRole("SiteAdmin"))
        {
            <th></th>
        }
    </tr>
</thead>
<tbody>

    @foreach (var item in Model)
{

        <tr>
            <td>
                @if (Context.User.IsInRole("SiteAdmin"))
                {
                    @Html.Hidden(Url.Action("Edit", "Spots", new { id = item.Id }))
                    <a style="color: Red; vertical-align: middle; font-size: 2em" href="@Url.Action("Delete", "Spots", new { id = item.Id })" title="Delete Spot" class="btn"><i class="icon-remove-sign"></i></a>
                }
                else
                {
                    @Html.Hidden(Url.Action("Details", "Spots", new { id = item.Id }))
                }
            </td>
            <td>

                @if (item.Image == null)
                {
                    <img width="100" height="100"
                         src="~/Content/Images/NoPhoto.jpg" class="img-rounded" />
                }
                else
                {
                    <img width="100" height="100"
                         src="@item.Image" class="img-rounded"/>
                }
            </td>
            <td >
                <div class="form-group pull-left col-md-2">
                    <h4>@item.Title </h4>
                    <h5 style="clear: left">
                        @if (item.Address != null)
                        {
                            <span>@item.Address</span>
                            <br/>
                        }

                        @if (item.State == null)
                        {
                            <span>@item.City</span><br/>
                            <span>@item.Country</span>
                        }
                        else
                        {
                            if (item.State == "")
                            {
                                <span>@item.City</span>
                                <br/>
                                <span>@item.Country</span>
                            }
                            else
                            {
                                <span>@item.City, @item.State</span>
                                <br/>
                                <span>@item.Country</span>
                            }
                        }
                    </h5>
                </div>
                <div class="form-group pull-left col-md-8">
                    <h6>@item.Summary</h6>
                </div>
            </td>
            @if (Context.User.IsInRole("SiteAdmin"))
            {
                <td>
                    @if (@item.Approved)
                    {
                        <span style="color: green">Approved</span>
                }
                else
                {
                        <span style="color: red">Not Approved</span>
                }
                </td>
            }
        </tr>
    }
    </tbody>
</table>

<script type="text/javascript">
    $(document).ready(function () {
        //Initalize and configure DataTables
        $('#dt-spots').dataTable({
            "oSearch": { "sSearch": "@ViewBag.initialFilter" }
        });


        $("tbody").on("click", "tr", function () {
            window.location = $(this).find('input').attr('name');
        });
    });
</script>
Dave Wade
  • 473
  • 1
  • 5
  • 18
  • Why would you need a `POST`? You're not doing any inserts or updates, just fetching results based on a parameter (`searchfilter`)—use `GET`. – trashr0x Nov 18 '16 at 14:55
  • Based on some thing I had seen online since the searchfilter is not there when the page was rendered I needed to do a post to get the value to use. Even if I was able to use a get though this still seems liek an issue with me trying to pass a parameter since the post fires correctly when none are specified – Dave Wade Nov 18 '16 at 15:00
  • As an aside you should not normally return a view from a Post action. Google Post Redirect Get – ste-fu Nov 18 '16 at 15:07
  • @DaveWade change `` to ` />`.. I believe this has something to do with ` – Grizzly Nov 18 '16 at 15:22
  • I did attempt this but got the same error – Dave Wade Nov 18 '16 at 15:41
  • @DaveWade Okay, let's start from the beginning.. have you debugged and is the method getting hit? – Grizzly Nov 18 '16 at 15:49
  • I have tested this personally, and it is working for me. I am starting to believe that this is a routing issue. Can you post your Route.Config file? – Grizzly Nov 18 '16 at 16:20
  • @DaveWade based on your updated question, your solution seems superfluous - have a look at my updated answer. – trashr0x Nov 18 '16 at 16:44

5 Answers5

1

Do a GET instead of a POST—you're not doing any inserts or updates, just fetching results based on a parameter (searchfilter). With a GET, the values of the input elements in your form will be appended as parameters to the query string of the target URL, which would produce something like mywebsite.com/spots/spotsearch?searchfilter=whateverValueInTheInputBox (depending on how you have your routing configured).

Razor:

@using (Html.BeginForm("SearchSpots", "Spots", FormMethod.Get, null))
{
    <input id="searchfilter" type="text" class="form-control" autocomplete="off" placeholder="Search" name="searchfilter">
    <button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>       
}

Controller:

public class SpotsController : Controller
{
    [HttpGet]
    [AllowAnonymous]
    public ActionResult SearchSpots(string searchfilter)
    {
        // ...        
    }
}

Edit: As per @BviLLe_Kid, you can try replacing <button> with <input>.

Edit 2: Can't help but wonder why you are proxying the call to Index via SearchSpots, causing an unnecessary redirect. If all that SearchSpots does is redirect to Index, why not submit the form directly to Index?

Razor:

@using (Html.BeginForm("Index", "Spots", FormMethod.Get, null))
{
    <!-- remember to rename to name="filter" below -->
    <input id="filter" type="text" class="form-control" autocomplete="off" placeholder="Search" name="filter">
    <input class="btn btn-default" type="submit" <i class="glyphicon glyphicon-search"</i>/>
}

Controller:

// GET: Spots
public ActionResult Index(string filter = "")
{
    ViewBag.initialFilter = filter;
    if (User.IsInRole("SiteAdmin"))
    {
        return View(db.Spots.ToList());
    }
    else
    {
        return View(db.Spots.Where(x => x.Approved).ToList());
    }

}
trashr0x
  • 6,457
  • 2
  • 29
  • 39
  • No luck with the get either although I think you are right and I should be doing a GET instead of a POST. After I made the changes and it crashed I removed the parameter from the controller action and it goes in just fine so it seems to have something to do with the parameter I am expecting the modelbinder to provide for me. – Dave Wade Nov 18 '16 at 15:15
  • why `return Index(searchfilter);`? If you're returning a view for that particular action, just `return View(yourModelHere);` and then in your view add `@model modelType` (if `yourModelHere` is a string for example, you'd write `@model string`). – trashr0x Nov 18 '16 at 15:21
  • Basically index is the view I want it normally opens up and displays an unfiltered list that you can filter with a text box. I am trying to call this view up which already has a model of the data I am displaying and I just want an initial filter put on the data. I will post the call and view above – Dave Wade Nov 18 '16 at 15:25
  • you have an extra `>` after `"submit"` – Grizzly Nov 18 '16 at 15:47
0

I believe you're missing FormMethod.Post in

@using (Html.BeginForm("SearchSpots", "Spots", FormMethod.Post)) {...

RizkiDPrast
  • 1,695
  • 1
  • 14
  • 21
  • I did try adding this but with no luck. I still get the same error which seems to be it choking on the parameter I am expecting in the controller function – Dave Wade Nov 18 '16 at 15:05
0

you don't need two forms in your HTML and this code is working and post search text

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - Haunt Spotter</title>
</head>
  <body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            </div>
            <div class="navbar-collapse collapse">
                @using (Html.BeginForm("SearchSpots", "Spots", FormMethod.Post))
                {
                    @Html.AntiForgeryToken()
                    <input id="searchfilter" type="text" class="form-control" autocomplete="off" placeholder="Search" name="searchfilter">
                    <button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
                }
            </div>
        </div>
    </div>

And you can add [ValidateAntiForgeryToken] in your action

Ahmed
  • 1,542
  • 2
  • 13
  • 21
0

In agreement with most of the other answers stating that this needs to be an HttpGet request rather than an HttpPost request I believe that this can be solved by changing your HTML.

HTML:

@using (Html.BeginForm("SearchSpots", "Spots", FormMethod.Get, null))
{
    <input id="searchfilter" type="text" class="form-control" autocomplete="off" placeholder="Search" name="searchfilter">
    <input class="btn btn-default" type="submit" <i class="glyphicon glyphicon-search"</i>/>       // part that needs changing
}

Controller:

[HttpGet]
public ActionResult SearchSpots(string searchfilter)
{
    // logic       
}

I believe your issue can be related to this. <button> is exactly what it is.. a button.. it basically does nothing, and is mainly used for JS purposes. However, the <input type="submit" /> actually submits the surrounding form.

I hope this helps!

UPDATE

I did need the input to pass the parameter. I still had the same error even after it was being passed and I had to make this final tweak to get it running

[HttpGet]
    [AllowAnonymous]
    public ActionResult SearchSpots(string searchfilter)
    {
        return RedirectToAction("Index", new { filter = searchfilter}); 
    }

I needed to redirect instead of trying to return a view

Community
  • 1
  • 1
Grizzly
  • 5,873
  • 8
  • 56
  • 109
  • I missed the element type in my answer (shameless copy-pasting), will factor that in. – trashr0x Nov 18 '16 at 15:40
  • @trashr0x so you meant to change ` – Grizzly Nov 18 '16 at 15:43
  • Nope, I'm saying I've added your suggestion to my answer (though I'm not sure it's the problem). – trashr0x Nov 18 '16 at 15:45
  • @trashr0x ahh i see. I haven't heard anything from the OP, so idk. – Grizzly Nov 18 '16 at 15:46
  • This solution was the closest to what I needed. The input did get me the parameter passed. I still had the issue and had to change my controller function to do a redirect instead of returning a view. Thanks for the help – Dave Wade Nov 18 '16 at 16:23
  • @DaveWade ahh silly of me.. I didn't even recognize that you were trying to return to the Index view.. If I knew that I could've been a lot more help haha – Grizzly Nov 18 '16 at 16:25
  • @DaveWade Now looking at your code.. do you mean `return RedirectToAction("Index", new { filter= searchfilter}); `? Your Index action doesn't have a parameter with the name, `searchfilter`.. just `filter` – Grizzly Nov 18 '16 at 16:30
  • lol yes I just figured that out and fixed it 2 minutes ago. Nice catch. – Dave Wade Nov 18 '16 at 16:46
0

This is a routing issue. You don't have a route that will hit SearchSpots with a parameter. So either change your route, or change the BeginForm to include the parameter.

ASP.NET MVC - passing parameters to the controller

Pass multiple parameters in Html.BeginForm MVC

Community
  • 1
  • 1
ScottTx
  • 1,483
  • 8
  • 12