Trouble with #Html.BeginForm in ASP.NET MVC - c#

I have a form that looks like this:
#using (Html.BeginForm("Login", "Home", FormMethod.Post, new { #class = "nav-login-form" }))
{
#Html.TextBoxFor(m => m.LoginForm.Username, new {#placeholder = "username"})
#Html.HiddenFor(m => m.LoginForm.Username)
#Html.PasswordFor(m => m.LoginForm.Password, new { #placeholder = "password" })
#Html.HiddenFor(m => m.LoginForm.Password)
<input type="submit" value="login"/>
}
However, when this form is submitted the controller method does not seem to be executed. It has a signature that looks like this:
[HttpPost]
public ActionResult Login(NavBarLogin loginForm)
My form is in a partial view with HomepageViewModel as the model type. This model has a LoginForm object inside of it, which is what the form is supposed to post to the controller. The model is passed into the partial view from the Index view like this:
#Html.Partial("_NavBarPartial", Model)
My best guess as to why this form is not working correctly is because the model defined in the view is not the same as the model object the form is using. However, I can't simply use NavBarLogin as the model type since other parts of the model are used elsewhere in the partial. I could attempt to split things up further, but is that really a requirement to make this form work? Am I even correct in my assumptions?
Edit:
My forum is being rendered like this, is action supposed to be blank?
<form action="" class="nav-login-form" method="post">
<input id="LoginForm_Username" name="LoginForm.Username" placeholder="username" type="text" value="" />
<input id="LoginForm_Password" name="LoginForm.Password" placeholder="password" type="password" />
<input type="submit" value="login"/>

You can use the the Prefix property of BindAttribute
[HttpPost]
public ActionResult Login([Bind(Prefix="LoginForm")] NavBarLogin loginForm)
Since you inputs are being rendered like
<input type="text" name="LoginForm.Username" value=.../>
and NavBarLogin has property Username (not a complex property LogInForm with property UserName), what this does is to effectively strip "LoginForm." from the posted values so they will bind correctly

Related

MVC BeginForm - passing parameters to controller using GET

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?
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:
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" />
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

Passing textbox value to ActionResult TempData in MVC5

I am very new to MVC , and trying to learn from videos given for MVC. In one of the video i have gone through the use of Tempdata. In the video they have shown created action result with HTTPPOST and pass data from textbox to controller and then to view using Tempdata . I have tried to practice the same . But i am not getting value from textbox,always returning as null. Below is my code,
Controller as below,
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(string name)
{
TempData["uname"] =name;
return RedirectToAction("GetUser");
}
public ActionResult GetUser()
{
return View();
}
View for Create as below,
#{
ViewBag.Title = "Create";
}
<h2>Create New User</h2>
#using (Html.BeginForm())
{
<div>
<p>Enter your name : <input type="text" id="id" placeholder="Enter username"/></p>
<p><input type="submit" id="btnSubmit" value="Create"/></p>
</div>
}
View for GetUser,
#{
ViewBag.Title = "GetUser";
}
<h2>GetUser</h2>
<div>
<h3>Welcome #TempData["uname"].ToString()</h3>
</div>
When i enter value in textbox and press button id parameter in Create action is null. Also i have not created any model class, as in video they have just shown as above code. Please give me some suggestions.
Regards
Sangeetha
if you are not using any model to send the data across , you should provide your textbox with a name attribute and that name attribute must match on the controller action , change your create view text box as shown
#using (Html.BeginForm())
{
<div>
<p>Enter your name : <input type="text" name="name" id="id" placeholder="Enter username"/></p>
<p><input type="submit" id="btnSubmit" value="Create"/></p>
</div>
}
I have added the name attribute to the text box
When you are passing values from view to controller by using input type=submit then you must set name attribute to the input elements,then you need to mention that name in method argument.
<input type="text" name="txtname" id="txtname" placeholder="Enter username"/>
<input type="submit" id="btnSubmit" value="Create"/>
Note : It will only work with input type="submit", it won't work with input type="button"
you don't need bind the Tempdata value with .ToString(). you can directly bind with #TempData["uname"]

MVC 5 Viewmodel binding works but post back is partial filled

I have a parameterless Index for the HttpGet which works. But when I post it the HttpPost version of Index is invoked and the viewmodel object is passed in, but there is only the value of the dropdown in it. The rest is null (products, title)
[HttpPost]
public ActionResult Index(ProductsViewModel pvm)
{
// breakpoint on line 36, shows that pvm.Title is null and Products too.
return View(pvm);
}
My compilable and running example can be downloaded from my OneDrive http://1drv.ms/1zSsMkr
My view:
#model KleinKloteProductOverzicht.Models.ProductsViewModel
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect"})
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#foreach (var item in Model.Products)
{
#Html.DisplayFor(i => item.Name);
}
</div>
}
}
If I have this view model:
public class ViewModel
{
public string Name {get;set;}
public string SelectedLocation {get;set;}
public IEnumerable<SelectListItem> Locations {get;set;}
}
And your actions look like this:
public ActionResult MyForm()
{
var vm = new ViewModel
{
Locations = context.Locations.ToList() // Some database call
}
return View(vm);
}
[HttpPost]
public ActionResult MyForm(ViewModel vm)
{
vm.Locations // this is null
}
It is null because the model binder can't find a form control that is setting its data.
The <form> must set some data in the view for the model binder to pick it up.
<form>
Name: <input type="text" id="name" />
</form>
This will set the Name property on the view model, because the model bind can see the id of the form control and uses that to know what to bind to.
So in terms of your view, you need to make sure you wrap any content that you want to post back to the server with #using(Html.BeginForm())
Anyway this is my guess.
Well, you seem to be confused as to how [HttpPost] and form tags interact with eachother.
You see, when .NET MVC binds your parameters in your controller actions, it tries to derive that data from the request. For [HttpGet] it does this by looking at the query string.
For [HttpPost] calls, it also looks at the Request.Form. This variable is populated with the values of all input fields that were inside the form you submitted.
Now, this is your view:
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect" })
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#foreach (var item in Model.Products)
{
#Html.DisplayFor(i => item.Name);
}
</div>
}
}
You only have one select tag (generated by Dropdownlistfor) but no other inputs. That's why .NET MVC cannot infer any other data for your view model.
If you change your view to this:
#model KleinKloteProductOverzicht.Models.ProductsViewModel
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect" })
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#for (var i = 0; i < Model.Products.Count; i++)
{
#Html.DisplayFor(model => model.Products[i].Name)
#Html.HiddenFor(model => model.Products[i].ID)
}
</div>
}
}
You'll see I've added a hidden input (<input type="hidden">) for the product id. Note that the product name still will be null.
I would suggest you follow a tutorial on .NET MVC and read up on some of the concepts behind it, because the very fact that you ask this question reveals that you have much to learn.
Best of luck!
P.S. One last tip: #Html.Blablabla writes directly to your view. You usually don't need that ";" at the end, because it will be inside your generated html.
Your property is not associated with a "postable" control, therefore it will not be submitted along with the form data. If your really want to get the value in your Title property, just set it as a hidden input.
#Html.HiddenFor(m => m.Title)
A label will not be posted when submitting a form but an input will. This is exactly what HiddenFor does; it creates a hidden input element which will be picked up by the form submit.

Send string from View to Controller using httpGet - MVC

I am new to MVC so please bear with me.
I am trying to send a string from a textbox to a controller method so I can find an object in a database. However, I do not know how to send the string successfully from the view to the controller in a HttpGet request (only in HttpPost)
The code in my view
<div>
<label>Email</label>
#Html.TextBox("email")
</div>
<div class="btn btn-success">
#Html.ActionLink("Edit RSVP", "Edit")
</div>
The ViewResult method in my controller
// Problem is the email parameter is always null
[HttpGet]
public ViewResult Edit(string email)
{
// If the email the typed is find, it will display their contents on to a RsvpForm view
return View("RsvpForm", guestRepository.Find(email));
}
Anyone know how I can send this string through, I would be grateful.
Thanks
Like this:
#using (Html.BeginForm("Edit", "ControllerName", FormMethod.Get))
{
<div>
<label>Email</label>
#Html.TextBox("email")
</div>
<div class="btn btn-success">
<input type="submit" value="Edit RSVP" />
</div>
}
Note: I can't tell from your description whether or not you are trying to do this without reloading the page. This option will post the page to the controller, so you will get a page reload.
If you want this to load without posting the page, you can look into Ajax.BeginForm. Here is a StackOverflow article with a decent primer on the AJAX form.
update
For your example, you may could do something like this if you want to use AJAX. This is all untested, but may be close to what you would need.
First you can create a partial view that represents the user data that you want to display:
RsvpForm.cshtml
#model GuestData
<div class="hdr">Name</div>
<div class="value">#Model.Name</div>
<div class="hdr">Email</div>
<div class="value">#Model.Email</div>
Then you want to make sure that your controller returns the partial view based on the email that is sent via the GET:
GuestDataController.cs
[HttpGet]
public ActionResult Edit(string email)
{
// If the email the typed is find, it will display their contents on to a RsvpForm view
return PartialView("RsvpForm", guestRepository.Find(email));
}
Then you create the AJAX form to submit the request via a GET and load the partial view without reloading the page: view.cshtml
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
#using (Ajax.BeginForm("Edit", "GuestData", null, new AjaxOptions { UpdateTargetId = "UserData", HttpMethod = "Get" }, null))
{
<div>
<label>Email</label>
#Html.TextBox("email")
</div>
<div class="btn btn-success">
<input type="submit" value="Edit RSVP" />
</div>
}
<div id="UserData"></div>
The easiest way to do it is to create a form as follow :
#using(Html.BeginForm("Edit", ControllerName, FormMethod.GET))
{
#Html.Label("Email")
#Html.TextBox("email")
<input type="submit" value="Edit RSVP"/>
}
or you can use Jquery to change the link when textbox value change (which I do not recommend):
$('input[name=email]').on('change' function()
{
var value = $(this).val();
var href = $('.btn').next('a').attr('href');
href += '?email='+value;
$('.btn').next('a').attr('href', href)
});

Bind value to model in Asp.Net MVC Application Part 2

This is sort of a follow up to Bind value to model in Asp.Net MVC Application.
I have a Model with different control classes. The relevant code:
public class FileUploadModel
{
public HttpPostedFileBase File { get; set; }
}
I have a partial view with the following relevant code:
#Html.TextBoxFor(x => x.File, new { type = "file", id = "File", name = "File" })
Then there is a main view in which the partial view is rendered with the following relevant code:
#using (Ajax.BeginForm("ActionMethods", "Index", new AjaxOptions { UpdateTargetId = "parameterList" }, new { enctype = "multipart/form-data" }))
{
<div id="parameterList">
<div id="verifyBtnDiv" style="display:none;">
**THIS IS WHERE THE PARTIAL VIEW AS SHOWN ABOVE WOULD BE RENDERED**
<input type="submit" id="verifyBtn" value="Verify"/>
</div>
</div>
}
Now when the submit happens, the file does not binds to the model property. The control passes to the controller but i debug and see that its null. ANy suggestions regarding this?
There are a few issues with the code you posted that will prevent what you are attempting to do from working.
First, I am pretty certain that you cannot use the #Html.TextBoxFor helper and convert it to a file input. If it works now, I would not rely on it as you are overriding what it is meant to put out and might break in the future. Let's just put out a file input with an Id and Name matching your ViewModel property.
<input type="file" name="File" Id="File/>
Next, you cannot use Ajax.BeginForm() to upload files. It is a limitation of AJAX, not an issue with the Ajax.BeginForm. So, we will need to update your form element to a normal, Html.BeginForm, with the proper enctype (this is important)
#using (Html.BeginForm("Upload", "MyControllerName", FormMethod.Post, new { enctype = "multipart/form-data"}))
{
<div id="parameterList">
<div id="verifyBtnDiv" style="display:none;">
<!-- Chose to just put the one line here instead of calling a partial-->
<input type="file" name="File" Id="File/>
<input type="submit" id="verifyBtn" value="Verify"/>
</div>
</div>
}
Lastly, if you have to/required to upload the file via AJAX, there are plenty of good recommendations on libraries to use for Ajax file uploads. I personally like the jQuery.Form plugin as it is pretty transparent in the way it handles file uploads.

Categories