MVC BeginForm - passing parameters to controller using GET - c#

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

Related

Controller action method not being called making a post request in ASP.NET Core

I am trying to use model binding to send a file selected by the user to the controller, this action method is not being hit and I am not sure why. I put a breakpoint inside but it will not get hit.
Here is my import view:
#model FileModel
<h1 class="title-underline">Import file</h1>
<div class="form-group">
<form asp-controller="Import" asp-action="FileImport" method="POST" enctype="multipart/form-data">
<label for="importFile">Select a past grant to import</label>
<input type="file" id="importFile" name="importFile" asp-for="ImportFile">
</form>
<button type="submit" id="btnSubmit" class="btn btn-tnl btn-block">Import</button>
</div>
Here is my model:
public class FileModel
{
public IFormFile ImportFile { set; get; }
}
Here is my action method inside my ImportController:
[HttpPost, ValidateAntiForgeryToken]
public IActionResult FileImport(FileModel model)
{
IFormFile file = model.ImportFile;
var fileName = Path.GetFileName(file.FileName);
var contentType = file.ContentType;
return View("Index");
}
I have provided the controller, action, method, and enctype so I'm not sure what else is missing which is stopping the method from being hit. Is it something to do with the model in the parameter?
One problem that I see is the submit button is outside of the form. It is not doing anything when you click it. Try bringing it into the <form>...</form> block and make the input type = "submit", not "button". Or write a click handler for it that would call submit on the form.
You can check the syntax here
https://www.w3schools.com/html/html_form_elements.asp
<div class="form-group">
<form asp-controller="Import" asp-action="FileImport" method="POST" enctype="multipart/form-data">
<label for="importFile">Select a past grant to import</label>
<input type="file" id="importFile" name="importFile" asp-for="ImportFile">
<input type="submit" id="btnSubmit" class="btn btn-tnl btn-block" value="Import">
</form>
</div>
Your button should be inside form tag. Not outside.
<form>
<button type="submit">Import</button>
</form>

Populating Html.ActionLink() with a variable name results in an incomplete href

Due to some unusual requirements I am getting the name of my controller while in my partial view and trying to populate an ActionLink with the controller name, like so:
#{
var controllerName = this.ViewContext.RouteData.Values["controller"].ToString(); // controller name is "Office"
#Html.ActionLink("Login", "Login", controllerName, null, new { #class = "btn btn-primary" })
}
It is generating this HTML:
<a class="btn btn-primary" href="/Office">Login</a>
whereas it should be generating this HTML:
<a class="btn btn-primary" href="/Office/Login">Login</a>
The odd thing is that the link still directs to the Login action in the Office controller. What exactly is going on here?

Can I get Razor to use some form input as part of the form action URI?

I have a problem with setting correct action attribute value using ASP.NET MVC routing mechanism.
I am using routes.MapMvcAttributeRoutes(); in RouteConfig class and I am setting URL address for every action by myself. And now I want form to move to defined URL address after I submit it, but there in no correct URL in action attribute (or there is none).
Here is code I use.
Controller:
[RoutePrefix("search")]
public class SearchController : BaseController
{
[HttpGet]
[Route("search/{query}/{author?}/{category?}/{mediaType?}/{color?}/{resultPerPage?}/{startElement?}", Name = "Search_Search")]
public ActionResult Search(string query, string author = "none", int category = 0, int mediaType = 0, int color = 0, int resultsPerPage = 15, int startElement = 0)
{
//doing some stuff here...
}
}
And now the .cshtml file fragment:
#using (Html.BeginForm("Search", "Search", FormMethod.Get))
{
}
Action attribute in generated form is like "/Search/Search", but that address does not work (404 error). I've tried use Html.BeginRouteForm with and without additional route configuration:
routes.MapRoute(
name: "SearchFormRoute",
url: "search/search/{query}/{author}/{category}/{mediaType}/{color}/{resultPerPage}/{startElement}",
defaults: new
{
author = "none",
category = 0,
mediaType = 0,
color = 0,
resultsPerPage = 15,
startElement = 0
},
constraints: new
{
httpMethod = new HttpMethodConstraint("GET")
}
);
I have tried it two ways, Html.BeginRouteForm("SearchFormRoute") and Html.BeginRouteForm("Search_Search"). Sometimes action attribute is set (but it's incorrect) and sometimes it's empty.
I really don't know how I shoud set it all up to work correctly. Is there any way to redirect form to manually defined with routing settings URL address?
EDIT: added whole form code
There is form code I am using:
#using (Html.BeginForm("Search", "Search", FormMethod.Get))
{
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="input-group">
<div class="col-md-9 nopadding">
<input type="text" id="query" name="query" class="form-control navSearchInput" placeholder="search...">
</div>
<span class="input-group-btn">
<button class="navSearchButton" type="submit">
<i class="fa fa-search-plus"></i>
</button>
</span>
</div>
</div>
</div>
}
EDIT: added generated form code
<form action="/Search/Search" method="get">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="input-group">
<div class="col-md-9 nopadding">
<input type="text" id="query" name="query" class="form-control navSearchInput" placeholder="search...">
</div>
<span class="input-group-btn">
<button class="navSearchButton" type="submit">
<i class="fa fa-search"></i>
</button>
</span>
</div>
</div>
</div>
</form>
If you try to access /Search/Search (just that) you'll get a 404 because your query route parameter is required. Any requests to /Search/Search will therefore not match a route.
But the same is true of requests of the form /Search/Search?query=blah, because your route is requiring that query be a route parameter, whereas here query is coming from the query string. So again, 404.
To get what you want you'll have to use Javascript to handle the form submission, and make sure that query is part of the route. Something like:
$("formIdHere").on("submit", function () {
this.action += WHATEVER_YOU_WANT_TO_ADD;
this.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)
});

Passing value of an input control to controller

I have the following:
#Html.BeginForm("AddComment", "Shared", new { guid = Model.VideoGuid, content="", userid = authenticatedUserId.ToString()}){
<div class="input-group">
<input name="txtCommentContent" type="text" class="form-control"><span class="input-group-btn">
<button class="btn btn-default">
<img src="~/Content/images/Message-Add.png" /></button>
</span>
</div>
}
I need to pass the text input inside the input control to the control as part of the routeValues. The above content="" is going to do that.
How can this be done?
You should be using the HTML Helpers:
#Html.EditorFor(m => m.CommentContent);
This will model bind the value in CommentContent (Which should be a property in your model) and pass it back up to the server when the form is POSTED.
As an alternative solution, you can also serialize the form and pass it up via AJAX.
As #Ic has pointed out you could also use:
#Html.TextBoxFor(m => m.CommentContent, new { #class = "form-control" });
Which will change the input type to text and also add your CSS class form-control.

Categories