Html.BeginForm in a partial view?

I have a block of code that uses the Html.BeginForm to submit a value from textbox to controller. When I put this in a View, it works fine. That is, the controller's action method is called. However, when I place this block of code inside a partial view that will get rendered in the View, the controller's action never gets called.

Not sure if this is the usual behavior or if I am missing something...

@using (Html.BeginForm("TestAction", "Home",  FormMethod.Post, new { id = "formId" }))
{<table>
   <tr>
        <td>Data Date</td>
        <td>@Html.TextBox("date")</td>
    </tr>    
   <tr>
        <td></td>
        <td><input id="btnRun" type="submit"  value="submit" /></td>
   </tr>

}

Controller:

 [HttpPost]
 public ActionResult TestAction(string date)
 {
     [doing something......]
     return View();
 }

Thanks in advance!

Typically @Html.BeginForm() renders the <form> tag's action to point back to the route details that got it here. Thus in partial views, you're actually rendering back to the outer page. (Yeah, mind-bending.) If you want to specifically direct the form post to a particular route, add additional parameters to the @Html.BeginForm(). See http://msdn.microsoft.com/en-us/library/system.web.mvc.html.formextensions.beginform(v=vs.108).aspx

you could have a couple of routes:

public static class webapiconfig
{
    public static void register(httpconfiguration config)
    {
        config.routes.maphttproute(
            name: "apibyid",
            routetemplate: "api/{controller}/{id}",
            defaults: new { id = routeparameter.optional },
            constraints: new { id = @"^[0-9]+$" }
        );

        config.routes.maphttproute(
            name: "apibyname",
            routetemplate: "api/{controller}/{action}/{name}",
            defaults: null,
            constraints: new { name = @"^[a-z]+$" }
        );

        config.routes.maphttproute(
            name: "apibyaction",
            routetemplate: "api/{controller}/{action}",
            defaults: new { action = "get" }
        );
    }
}

i also had this problem, and couldn't find any neat and obvious solution.

the solution i came up with was to implement an html extension method that returns a 'pagedata' class that you define, containing whatever data you need:

    [threadstatic]
    private static controllerbase pagedatacontroller;
    [threadstatic]
    private static pagedata pagedata;

    public static pagedata getpagedata(this htmlhelper html) {
        controllerbase controller = html.viewcontext.controller;
        while (controller.controllercontext.ischildaction) {
            controller = controller.controllercontext.parentactionviewcontext.controller;
        }
        if (pagedatacontroller == controller) {
            return pagedata;
        } else {
            pagedatacontroller = controller;
            pagedata = new pagedata();
            return pagedata;
        }
    }

it finds the top-level controller for the current request, and returns the same pagedata object every time the method is called within the same http request. it creates a new pagedata object the first time it is called in a new http request.

the issue appears to be with the .net 4.0 installation on the vm we were using for that server. after creating two new vms (both windows 2008 64-bit, one standard one enterprise) with clean .net 4.0 installs, the bundling worked fine on both. thanks for all the help.

you haven't actually shown how you are calling the partial view and whether your controller action has actually passed a model to this view.

make sure that your controller has properly initialized the model. so if we suppose that you have the following model:

public class myviewmodel
{
    public string selectedoption { get; set; }
    public ienumerable<selectlistitem> selectoptions { get; set; }
}

and the following controller action:

public actionresult foo()
{
    var model = new myviewmodel();
    model.selectoptions = new[]
    {
        new selectlistitem { value = "1", text = "item 1" },
        new selectlistitem { value = "2", text = "item 2" },
        new selectlistitem { value = "3", text = "item 3" },
    };
    return view(model);
}

ten you could have a corresponding view which will call a partial:

@model myviewmodel
@html.partial("_mypartial", model)

and the _mypartial.cshtml:

@model myviewmodel
@html.dropdownlistfor(x => x.selectedoption, model.selectoptionoptions)

notice how you need 2 properties on your view model in order to create a dropdown list => a scalar property (selectedoption) that will be used to bind the selected value and a collection property that will contain the list of values you would like to display in the dropdown (selectoptionoptions).

in your code you are using the same property for both which is wrong:

@html.dropdownlistfor(m => m.selectoption, model.selectoption)

Tags: Asp.Net Mvc 4