How does one obtain the form data after submitting it?
<form target="_self" runat="server">
<p>
<select id="BLAHBLAH2">
<option>2010</option>
<option>2011</option>
<option>2012</option>
<option>2013</option>
</select>
<input type="submit" runat="server" value="Change Year" />
</p>
</form>
This hits the controller's Index method. But, there's nothing in Request.Form. Why?
Second, can I use
<input type="button" instead of type=submit? That is, without introducing ajax via onclick.
Finally, how do I submit to a different method in the controller, e.g. Create?
Try removing those runat server tags. They should not be used in ASP.NET MVC. Also your select doesn't have a name. If an input element doesn't have a name it won't submit anything. Also your option tags must have value attributes which indicates what value will be sent to the server if this options is selected:
<form action="/Home/Create" method="post">
<p>
<select id="BLAHBLAH2" name="BLAHBLAH2">
<option value="2010">2010</option>
<option value="2011">2011</option>
<option value="2012">2012</option>
<option value="2013">2013</option>
</select>
<input type="submit" value="Change Year" />
</p>
</form>
But the correct way to generate forms in ASP.NET MVC is to use HTML helpers. Depending on the view engine you are using the syntax might be different. Here's an example with the Razor view engine:
#model MyViewModel
#using (Html.BeginForm("Create", "Home"))
{
<p>
#Html.DropDownListFor(x => x.SelectedYear, Model.Years)
<input type="submit" value="Change Year" />
</p>
}
Here you have a strongly typed view to some given view model:
public class MyViewModel
{
public string SelectedYear { get; set; }
public IEnumerable<SelectListItem> Years
{
get
{
return Enumerable
.Range(2010, 4)
.Select(x => new SelectListItem
{
Value = x.ToString(),
Text = x.ToString()
});
}
}
}
which is populated by some controller action that will render this view:
public class HomeController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Create(MyViewModel model)
{
... model.SelectedYear will contain the selected year
}
}
None of your <option> tags have a value:
...
<option value="2010">2010</option>
...
As noted by David, runat="server" is most definitely a WebForms thing, so you can 86 that.
If you want to submit to a different method on your controller you just need to specify the URL for that method.
Easy way using Html.BeginForm:
#using (Html.BeginForm("AnotherAction", "ControllerName")) {
<!-- Your magic form here -->
}
Using Url.Action
<form action="#Url.Action("AnotherAction")" method="POST">
<!-- Your magic form here -->
</form>
You can also use
In Controller
int Value = Convert.ToInt32(Request["BLAHBLAH2"]); //To retrieve this int value
In .cshtml file use
<select id="IDxxx" name="BLAHBLAH2">
//Request[""] will retrieve the VALUE for the html object ,whose "name" you request.
Related
I am working on this ASP.NET Core MVC where I have this DropDownLisit which gets its values from Controller using ViewBag.DishTypes. However, upon submitting the form, the POST method is not getting the value of the option selected in the DropDownList. The code snippets are as follows:
Controller: GET Method
var allDishTypes = _context.DishType
.ToList()
.Select(dt => new SelectListItem { Value = dt.DishTypeId.ToString(), Text = dt.DishTypeName.ToString() }).ToList();
ViewBag.DishTypes = allDishTypes;
return View();
View
<form asp-controller="Home" asp-action="AddMenuItems">
<div class="row">
<label class="my-1 mr-2" for="inlineFormCustomSelectPref">Dish Type</label>
<div class="input-group">
<div class="fg-line form-chose">
<label asp-for="DishTypeId" class="fg-labels" for="DishTypeId">Dish Type</label>
<select asp-for="DishTypeId" asp-items="ViewBag.DishTypes" class="form-control chosen" data-placeholder="Choose Dish Type" required name="dishtype" id="dishtype">
<option value=""></option>
</select>
</div>
</div>
....
Controller: POST Method
[HttpPost]
public IActionResult AddMenuItems([Bind("DishTypeId, DishName, Cost")] Dishes dishesObj)
{
....
}
the POST method is not getting the value of the option selected in the DropDownList
Note that you specified a name=dishtype within your code. By this way, the field name is
always the same as this name attribute, i.e, dishtype instead of DishTypeId, which will not be recognized by ASP.NET Core by default.
To fix that issue, simply remove that attribute such that it uses asp-for to generate the name attribute automatically:
<select asp-for="DishTypeId" asp-items="ViewBag.DishTypes"
class="form-control chosen" data-placeholder="Choose Dish Type" required
name="dishtype" id="dishtype"
>
<option value=""></option>
</select>
I am using Remote Attribute validation
The method is invoked successfully on textchange event. However, the parameters in the action method of the controller does not get the value of the field.
Here is the Action Method in the HomeController.cs. When invoked the Name parameter remains null. I will be pleased if someone solve this problem
[AcceptVerbs("Get", "Post")]
public async Task<ActionResult> IsExist(string Name)
{
List<Keywords> keywords = new List<Keywords>();
HttpClient client = _api.Initial();
HttpResponseMessage res = await client.GetAsync("api/Keywords");
if (res.IsSuccessStatusCode)
{
var result = res.Content.ReadAsStringAsync().Result;
keywords = JsonConvert.DeserializeObject<List<Keywords>>(result);
}
if (keywords.FirstOrDefault(x => x.Name == Name) == null)
{
return Json(false);
}
else
{
return Json(true);
}}
Here is the Model
public partial class Keywords
{
public int Id { get; set; }
[Display(Name = "Name of Keyword")]
[Required]
[Remote(action: "IsExist",controller: "Home", ErrorMessage = "Keyword already present!")]
public string Name { get; set; }}
Here is the razor page in which I want to implement validation
#page
#model Consumer.Pages.Keyword.CreateModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Keywords</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Keywords.Name" class="control-label"></label>
<input asp-for="Keywords.Name" class="form-control" />
<span asp-validation-for="Keywords.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Keywords.Department" class="control-label"></label>
<select asp-for="Keywords.DepartmentId" class="form-control" asp-items="ViewBag.Department"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I found the solution. It is to
Remove partial in the model class definition.
Replace in the Razor Page
<input asp-for="Keywords.Name" class="form-control" />
with
<input asp-for="Keywords.Name" name="Name" class="form-control" />
The [Remote] attribute is all but useless. There's a long-standing problem from the ASP.NET MVC days that migrated its way into ASP.NET Core unabated, as well. Namely, the action that handles the remote must take a param that matches the input name of what what's being validated. In other words, your action takes a param, name, but what's being sent to it is Keywords.Name, which cannot bind to name. You'd have to do something like:
public async Task<ActionResult> IsExist(Keywords keywords)
And then, then the value will be available via keywords.Name. This obviously makes the usage highly dependent on the particular implementation, so if there's a situation with a different input name like Foo.Keywords.Name, then you'd need an entirely different action, to match that binding, and basically duplicate the logic (though you could factor out the majority the logic into some common method all the actions could utilize).
Long and short, you're probably better off just handling this yourself manually. Just bind to the input event on the name input, and then call your method via AJAX. Then you can explicitly control the key that's sent in the request body, such that it will always match your action. Just note that you'll also want to debounce sending that AJAX request so that it only happens every few keystrokes or so. Otherwise, you're going to hammer your action.
I have trouble with formcollection.
I have some form in view, and two submit buttons both with unique name="". On debug I get on controller in formcollection all data except "name" of submitted button... I don't know why, 'cos I use this in other forms and there it works good. So I look at the code, but I couldn't find any differencies in using formcollection on not working formcol and working formcol. I tried rename buttons, move them, add referencies which I thought that could help... Nothing. On every submit skip my condition because it give me back only "false" and formcollection my "upload" or "save" button on onclick doesn't contain...
So I would like to ask you for help with this. Can you tell me where could be an error? Thanks to all!
This is in controller:
[HttpPost]
public ActionResult EditUser(EditUserVM model, int id, FormCollection c)
{
//c["upload"] is everytime set to null, 'cos c does't contain it
if (c["upload"] != null)
{
//.... some code
return RedirectToAction("Index", "Home");
}
if (ModelState.IsValid)
{
//.... next code
}
return View("EditUser", model);
}
This is in View:
#model PrukazOnline.ViewModels.EditUserVM
#{
ViewBag.Title = "EditUser";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script type="text/javascript" src="#Url.Content("~/Scripts/jquery.validate.min.js")"></script>
<script type="text/javascript" src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>
<script type="text/javascript" src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")"></script>
#using (Html.BeginForm("EditUser", "User", null, FormMethod.Post, new { #class = "form-horizontal", id = "formstep", enctype = "multipart/form-data" }))
{
#*Here is some code - #Html.TextBoxFor(x => x.something) and #Html.ValidationMessageFor(x => x.something) - all of these are in formcollection*#
.
.
.
.
<div>
<input type="submit" value="Nahrát" class="upl" name="upload" id="upload" />
</div>
<div class="submit_buttons">
<input type="submit" name="save" id="save" value="Uložit" />
</div>
}
Issue is in multiple submit inputs in single form. There will only clicked button in FormCollection.
I suppose you try to vary form processing base on clicked button, in this case it's better to use different Actions for each button like this:
View:
<div>
<input type="submit" value="Nahrát" class="upl" name="upload" id="upload" />
</div>
<div class="submit_buttons">
<input type="submit" name="save" id="save" value="Uložit" />
</div>
Controller:
[HttpParamAction]
[HttpPost]
public ActionResult Upload(EditUserVM model)
{
...
}
[HttpParamAction]
[HttpPost]
public ActionResult Save(EditUserVM model)
{
...
}
How can I send to action of controller different values for same field? Which input parameters should I define in action? And how can I show url with different values of identical fields in querystring? I want to get such url: site.com/directory?metro=2&metro=3
Thanks!
Here's an example of how this would be done in the case of checkboxes. Note that you wouldn't see the same querystring parameter repeated in the case of a GET request. Instead you would see "?metros=1,3" if checkboxes 1 and 3 were checked.
HTML
<form action="http://site.com/directory" method="get">
<input type='checkbox' name='metros' value='1' />
<input type='checkbox' name='metros' value='2' />
<input type='checkbox' name='metros' value='3' />
</form>
Controller
public class DirectoryController : Controller {
public ActionResult Index(IEnumerable<int> metros) {
foreach (var metro in metros) {
// do something
}
return View();
}
}
I am currently having an issue with multiple action buttons being in the same form.
The first button would perform verification while the second button would save profile. The third would simple redirect the user out of the page, but they are still required to go through controller some tracking purposes. Last button is delete. Because they are placed together and I do need ModelBinding passed through POST, it's impossible to separate them into multiple forms.
Currently, in order to differentiate which action is being clicked, I have a hidden input in my form and onclick, javascript would update the hidden input so that it will be passed back to the controller.
The reason I did this was because for some weird reasons, FormCollection doesn't want to hold my submit values. I tried accessing buttons in controller via
formCollection["verify"]
But it turns out to be null. Both id and name of the input submit is set to verify.
I also tried a lot of other suggestions like this and this but to no avail. Is there a better approach to my problem without using javascript to alter hidden inputs?
The best approach is to have separate actions handling the different button calls as explained in this article.
If you want to have one ugly action doing all the stuff then you could give your submit buttons names:
#using (Html.BeginForm())
{
... input fields for the model
<button type="submit" name="btn" value="verify">Verify data</button>
<button type="submit" name="btn" value="save">Save data</button>
<button type="submit" name="btn" value="redirect">Redirect</button>
}
You don't need any hidden fields or javascript. And then in your controller action you would check for the value of the btn parameter (which obviously will be part of you view model):
[HttpPost]
public ActionResult Foo(MyViewsModel model)
{
if (model.Btn == "verify")
{
// the Verify button was clicked
}
else if (model.Btn == "save")
{
// the Save button was clicked
}
else if (model.Btn == "redirect")
{
// the Redirect button was clicked
}
else
{
// ??? throw
}
...
}
Of course if you follow my advice and separate your actions (as outlined in the article):
#using (Html.BeginForm("Action", "Home"))
{
... input fields for the model
<input type="submit" name="verify" value="Verify data" />
<input type="submit" name="save" value="Save data" />
<input type="submit" name="redirect" value="Redirect" />
}
and then:
[HttpParamAction]
[HttpPost]
public ActionResult Verify(MyViewModel model)
{
...
}
[HttpParamAction]
[HttpPost]
public ActionResult Save(MyViewModel model)
{
...
}
[HttpParamAction]
[HttpPost]
public ActionResult Redirect(MyViewModel model)
{
...
}
which is a far cleaner code which doesn't violate the Single Responsibility Principle.
I do something slightly different;
<input type="submit" name="submit" value="Save Draft" />
<input type="submit" name="submit" value="Publish" />
Then you can get at the values using;
FormCollection["submit"]
Thanks,
Matt
<input type="submit" name="nameONE" />
<input type="submit" name="nameTWO" />
[HttpPost, ActionName("OrginalActionName")]
[FormValueRequired("nameONE")]
public ActionResult WhateverYouWantONE(type name)
{
code...
}
[HttpPost, ActionName("OrginalActionName")]
[FormValueRequired("nameTWO")]
public ActionResult WhateverYouWantTWO(type name)
{
code...
}