I can post elements from my form if I make a direct reference to the named element in the controller parameters. I am trying to use FormCollection so I do not have to type every element from the form in the post ActionResult parameters.
The HTML form:
#using (Html.BeginForm("legallabels", "Reports", FormMethod.Post, new { id = "reportForm", #class = "report-form col-9" }))
{
<div class="col-12">
<b>Beginning </b><input type="text" class="form-control col-2" id="beginningDatePicker" name="beginningDate" value="#DateTime.Today.Date.ToString("MM/dd/yyyy")" />
</div>
<input type="submit" value="submit">
}
Controller using named parameter (beginningDate):
[HttpPost]
public ActionResult LegalLabels(string beginningDate)
{
return View();
}
When using FormCollection, it does not get passed to the controller:
[HttpPost]
public ActionResult LegalLabels(FormCollection form)
{
return View();
}
Using a breakpoint in the controller, I can see that the form is posting correctly and everything works fine when naming form elements (beginningDate) in the parameters. I've looked at similar samples of code that use FormCollection and they seem to work fine. Why are my FormCollection values not getting passed to the controller?
Tested your code, it works fine. If you see the snippet below you can loop through all posted values and check.
[HttpPost]
public ActionResult LegalLabels(FormCollection form)
{
StringBuilder sb = new StringBuilder();
foreach (var key in form.AllKeys)
{
sb.AppendLine(string.Format("Key: {0}. Value: {1}.<br>", key, form[key]));
}
ViewBag.FormData = sb.ToString();
return View();
}
On the cshtml
<div>
#Html.Raw(ViewBag.FormData)
</div>
After renaming FormCollection to IFormCollection, everything is working properly.
Related
I spent a good margin of time searching and working through this problem yesterday without coming up with a solution. Here is the guide I used as reference.
The problem is that the data from the form is not reaching my controller. My goal is to take the form data and pass it to my controller/ model so that I can use the values throughout my code and store them in the database. Below is what I have so far...
In my Browse.cshtml (View)
#model Collect
<form asp-action="Collect" asp-controller="Collect" method="post">
<input type="hidden" asp-for="GameId" name="#game.id"/>
<button type="submit" class="dropdown-item btn btn-block">Default</button>
</form>
In my CollectController.cs (Controller)
using System;
using GameLibrary.me.Models;
using Microsoft.AspNetCore.Mvc;
namespace GameLibrary.me.Controllers
{
public class CollectController : Controller
{
[HttpGet]
public IActionResult Collect()
{
return View();
}
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Collect(Collect model)
{
Console.WriteLine("**********\n"+model.GameId+"\n**********");
return Content($"Hello {model.GameId}");
}
}
}
In my Collect.cs (Model)
namespace GameLibrary.me.Models
{
public class Collect
{
public int GameId { get; set; }
}
}
EDIT: Here is what my IDE is telling me...
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST https://localhost:5001/browse?game=eevee application/x-www-form-urlencoded 7
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 1.0139ms 200
info: Microsoft.AspNetCore.Server.Kestrel[32]
Connection id "0HLJHIUOU6AKO", Request id "0HLJHIUOU6AKO:00000003": the application completed without reading the entire request body.
Any guidance on what I am doing wrong would be great appreciated... Also can I send multiple values through the hidden field type, or should I make a new hidden field type for each value?
There was a lot of different help here, thanks especially to Kirk Larklin! There were three issues that was preventing my controller from picking up the data.
Browse.cshtml was missing the #addTagHelpers... I added the following:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#addTagHelper *, AuthoringTagHelpers
My CollectController.cs was missing a route... I added the following:
[HttpPost, ValidateAntiForgeryToken]
[Route("Index/Collect")]
Finally, I renamed my controller post method from 'Collect' which conflicting with another method to Index and updated the asp-action in my Browse.CSHTML file to match.
public IActionResult Index(Collect model)
Thanks for all the help!
-Travis W
First I would pass in the model to the view with the Id initialised:
public IActionResult Collect()
{
return View(new Collect { GameId = "5"});
}
Inside your view update the form to the following:
<form asp-action="Collect" asp-controller="Collect" method="post">
#Html.HiddenFor(m => m.GameId)
<button type="submit" class="dropdown-item btn btn-block">Default</button>
</form>
The Html helper will create the html coded for the field. Once you click submit the value will be correct.
In my case, I updated the login view with this line of code. that's fixed.
<form method="post" asp-controller="account" asp-action="login">
Based on your example I do not believe that you want custom actions, you should be able to achieve your goal with the default framework mapping.
public class CollectController : Controller
{
[HttpGet]
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index([FromBody]CollectionModel model) => Content(...);
}
To clarify the magic, the declared Post method I utilize a HttpBody attribute, otherwise the framework will expect the parameter contents to be defined via Query String in the URL. The framework by default looks for Index when a Controller has been hit, if you do not need the URL to represent that, then do not use it.
The routing pattern usually follows:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
URL:
http://www.sample.com/Collect ---> Initial index Get would be hit
I recently came upon this question as I have a solution I need to work on.
I have a controller which does some logic, with some method:
public Controller1: MvcController
{
public ActionResult SomeLogic(Model model)
{
return view();
}
public ActionResult SomeLogic2(Model model)
{
return view();
}
}
And I have a second controller which does different logic:
public Controller2: MvcController
{
public ActionResult SomeLogic(Model model)
{
return view();
}
public ActionResult SomeLogic2(Model model)
{
return view();
}
}
Both are working on the same view:
MyView.cshtml
The view contains a begin form that works on submit:
#using(Html.BeginForm("SomeLogic", "Controller1", FormMethod.Post)
and 4 submit buttons:
<input type='submit' formaction='SomeLogic' value='submit' />
<input type='submit' formaction='SomeLogic2' value='submit2' />
<input type='submit' formaction='SomeLogic' value='submit3' />
<input type='submit' formaction='SomeLogic2' value='submit4' />
I know I can control the action to which I am redirecting the submit using the:
formaction attribute.
I there a way to change the controller as well? meaning that:
<input type='submit' formaction='SomeLogic' value='submit3' />
<input type='submit' formaction='SomeLogic2' value='submit4' />
Will submit to controller2 instead of Controller1 which is defined in my begin form.
This will solve my debate whether to create a new controller or use the existing one, which is already full of complex logic.
You need to include controller name in the formaction attribute.
Note that your controller names are not valid, but assuming your have
public FirstController: Controller
{
[HttpPost]
public ActionResult SomeLogic(Model model)
{
return view();
}
}
public SecondController: Controller
{
[HttpPost]
public ActionResult SomeLogic(Model model)
{
return view();
}
}
Then you can post to the correct controller using
// Post to the SomeLogic method of FirstController
<input type="submit" formaction="#Url.Action("SomeLogic", "First")" value="submit" />
// Post to the SomeLogic method of SecondController
<input type="submit" formaction="#Url.Action("SomeLogic", "Second")" value="submit3" />
Just intercept the submit with jQuery
You can add an ID to your form, to be able to select it easily in jQuery
#using(Html.BeginForm("SomeLogic", "Controller1", FormMethod.Post, new { #id = "yourForm" })
Then
$("#yourForm").submit(function(e){
e.preventDefault();
var url1 = '#(Url.Action("SomeLogic1", "SomeController", null, Request.Url.Scheme))';
var url2 = '#(Url.Action("SomeLogic", "SomeController2", null, Request.Url.Scheme))';
//Whatever logic should you do to select which URL you want to POST to
$.ajax({
type: 'POST',
url: url_selected_above,
data: $('#yourForm').serializeArray(),
success: function (xhr, response) {
//...
},
error: function (xhr, response) {
//...
}
});
});
This should work if all the methods expect the same view model.
Alternatively, if you do not want to mess around in the UI, you can do something as such in the action that generates the form.
var controllerName = "SomeController";
var actionName = "Action1";
//do your logic to select whichever controller/action.
viewModel.ControllerName = controllerName;
viewModel.ActionName = actionName;
And in the view that has the form:
#using(Html.BeginForm(#Model.ActionName, #Model.ControllerName, FormMethod.Post)
Again, if all the actions expect the same view model, that should work.
Ajax.BeginForm() not working when Html.EditorFor() contains special charactor <
It does not invoke the controller action on submit the form
Have you tried the following:
Index.cshtml
#using (Ajax.BeginForm("PostForm", "Home", new AjaxOptions()))
{
string myFormValue = "";
#Html.EditorFor(x => myFormValue)
<input type="submit" value="Submit"/>
}
HomeController.cs
public class HomeController : Controller
{
public ActionResult PostForm(string myFormValue)
{
return View("Index");
}
When i debug it seems to work:
May you post your code?
Using asp.net core razor. My current if statement is wrong, but it is the only way to get the error messages to show up. The current if statement is if the ModelState is not valid return to view. On the new view it shows the error messages. However, what I want is if the ModleState is not valid redirect to the Index.cshtml page and show the errors their. When I flipped around my if condition the error messages do not show up in the Index.cshtml page.
Here is my Controller.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using quotingDojo.Models;
namespace quotingDojo.Controllers
{
public class HomeController : Controller
{
// GET: /Home/
[HttpGet]
[Route("")]
public IActionResult Index()
{
return View();
}
[HttpPost]
[Route("quotes")]
public IActionResult Quotes(Home model)
{
if(!ModelState.IsValid)
{
return View();
}
//continue your code to save
return RedirectToAction("Index");
}
}
}
Here is my Index.cshtml
#model quotingDojo.Models.Home
<h1>Welcome to the Quoting Dojo</h1>
#using(Html.BeginForm("Quotes","Home"))
{
<p>#Html.ValidationSummary()</p>
<p>
<label>Your Name</label>
#Html.TextBoxFor(s=>s.Name)
#Html.ValidationMessageFor(s => s.Name)
</p>
<p>
<label>Your Quote</label>
#Html.TextAreaFor(d=>d.Quote)
</p>
<input type="submit" name="submit" value="Add my quote!"/>
}
<form action="quotes" method="get">
<input type="submit" name="submit" value="Skip to quotes!"/>
</form>
Here is my Quotes.cshtml where the error messages currently show up.
<h1>Test</h1>
<p>#Html.ValidationSummary()</p>
Here is my models page
using System.ComponentModel.DataAnnotations;
namespace quotingDojo.Models
{
public class Home
{
[Required(ErrorMessage ="Please enter your name")]
[DataType(DataType.Text)]
[MinLength(3)]
public string Name {get; set;}
[Required]
[MinLength(5)]
public string Quote{get; set;}
}
}
Your issue is here:
return View();
That will return a view named "Quotes" since your action method is named Quotes (this is MVC convention). You also do not pass a model to it so even if the view existed, it will not know of the errors.
Two ways to fix your issue:
1.
You have to pass the model to your Index view so you need to explicitly return the Index view.
if(!ModelState.IsValid)
{
return View("Index", model);
}
2.
I personally prefer this method. Name your first action which serves the original form the same as the one your are posting to and then you can do this (note: you will also need to rename the view):
// GET: /Home/
[HttpGet]
[Route( "" )]
public IActionResult Quotes() {
return View();
}
[HttpPost]
[Route( "quotes" )]
public IActionResult Quotes(Home model) {
if( !ModelState.IsValid ) {
return View(model);
}
//continue your code to save
return RedirectToAction( "Index" );
}
This way both of them return the view named Quotes so you do not have to explicitly mention it.
The standard practice is, If model state is not valid, you return the same view (which user submitted) where you will show the validation error messages. You just need to change your Quotes action method to Index action method.
[HttpPost]
[Route("quotes")]
public IActionResult Index(Home model)
{
if (!ModelState.IsValid)
return View();
// to do :continue saving
}
Make sure to update your form to post to this action.
Not sure why you want to redirect to another page where you want to show the errros.
If you absolutely want to show the error messages in another view which was loaded via RedirectToAction method call (hence a totally new GET call), You need to use TempData to transfer the errors.
public ActionResult Quotes(Home model)
{
if (!ModelState.IsValid)
{
List<string> errors = new List<string>();
foreach (var modelStateVal in ViewData.ModelState.Values)
{
foreach (var error in modelStateVal.Errors)
{
errors.Add(error.ErrorMessage);
}
}
TempData["errors"] = errors;
return RedirectToAction("Index");
}
//continue your code to save
}
And in your index view,
#if (TempData["errors"] != null)
{
var errors = (List<string>) TempData["errors"];
foreach (var error in errors)
{
<span class="alert warning">#error</span>
}
}
I am new to mvc 3. i am trying to create a view and controller. my scenario is that i have controller which has two actions. while i call first action inside controller i have to load default values to view. inside view i have a buttons to post back values. so while click one of the button inside view, it has to go second action in same controller and has to return back to same view with success or failure message.
My view is like this
#using (Html.BeginForm())
{
<table>
<tr>
<td>
#Html.TextBox("txtTitle")
</td>
<td>
<input type="button" value="Go Somewhere Else" onclick="location.href='#Url.Action("Postback", "Default1")'" />
</td>
</tr>
</table>
}
My Controller,
public class Default1Controller : Controller
{
//
// GET: /Default1/
public ActionResult Index()
{
// has to default load value to text box
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Postback(FormCollection obj)
{
// has to take value from txt box
object obj1 = Request["txtTitle"];
return View("Index");
}
}
My problem if call Postback action from any other view it works. but inside same view if i click on button the error shows like "http://localhost:14953/Default1/Postback".
What is solution here? i expect to navigate to same controller as well as to other controller inside same form and return to same view.
As reference to this article.....
public class HttpParamActionAttribute : ActionNameSelectorAttribute {
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
return false;
var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
In view form like this...
#using (Html.BeginForm("Action", "Post")) {
<!— …form fields… -->
<input type="submit" name="SaveDraft" value="Save Draft" />
<input type="submit" name="Publish" value="Publish" />
}
and actions in controller...
public class PostController : Controller {
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveDraft(…) {
//…
}
[HttpParamAction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Publish(…) {
//…
}
}
You can specify an ActionName to your Postback method like this:
[ActionName("Index")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Postback(FormCollection obj)...
So F5 repost the same values and calling the URL directly from the address bar returns the View as expected.
There is a constructor for Html.BeginForm Helper method which takes action name and controller name. Use that
public static MvcForm BeginForm(
this HtmlHelper htmlHelper,
string actionName,
string controllerName
)
http://msdn.microsoft.com/en-us/library/dd492590.aspx
So your form should look like this. You dont need the onclick function in your input element.
#using (Html.BeginForm("Postback", "Default1"))
{
//Your elements
<input type="button" value="Go Somewhere Else" />
}
That will render the HTML markup like this
<form action="/Default1/Postback" method="post">
// Your elements
<input type="button" value="Go Somewhere Else" />
</form>
If you want multiple submit button in the same form, It is answered here
I think we can specific action and controller at Html helper.
#using (Html.BeginForm("Postback", "Default1"))
{
<table>
<tr>
<td>
#Html.TextBox("txtTitle")
</td>
<td>
<input type="submit" value="Go Somewhere Else" />
<!-- the input should be submit?-->
</td>
</tr>