Dave Wade
Dave Wade

Reputation: 473

ASP.NET MVC BeginForm not able to post with parameter

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>

Upvotes: 0

Views: 2902

Answers (5)

Grizzly
Grizzly

Reputation: 5953

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

Upvotes: 0

trashr0x
trashr0x

Reputation: 6565

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());
    }

}

Upvotes: 1

ScottTx
ScottTx

Reputation: 1483

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

Upvotes: 0

Ahmed
Ahmed

Reputation: 1590

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

Upvotes: 0

RizkiDPrast
RizkiDPrast

Reputation: 1725

I believe you're missing FormMethod.Post in

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

Upvotes: 0

Related Questions