I have an action as follows:
public ActionResult ChangeFeeCheck(string id)
{
ViewBag.id = id;
return View();
}
on my View, I have the following:
#{
ViewBag.Title = "CreateList";
}
Please enter first name <br /><br />
#using (Html.BeginForm())
{
#Html.Textbox("firstname")
<input type="button" id="SaveChanges" value="Save" />
}
When I click on the button, I was expecting it to to the following
[HttpPost]
public ActionResult ChangeFeeCheck(string firstname)
{
.....
}
I am not sure when MVC will automatically go to the HttpPost or if I when need to manually have it there. In the above, it does not go there directly. I have to use the
window.location.href
and pass the url of the controller/action.
Meaning, isn't the default for
Html.BeginForm()
The HttpPost(same name as the HttpGet)
You need the button to be a submit button:
Change:
<input type="button" id="SaveChanges" value="Save" />
^^^^^^
To:
<input type="submit" id="SaveChanges" value="Save" />
^^^^^^
If you are following "Convention over configuration" rule over here, then the view you have created here must be for ChangeFeeCheck action, and ChangeFeeCheck here looks like will make compiler confused as no overload, same name, same signatures.
and then when method for form is get it would go to the first one, while if method for form is POST, it will call the one decorated with [HttpPost]
And because you are using submit button and by default HTML form generated uses POST action, it call the [HttpPost]
You can refer this article (from the internet archive as original link is now down): https://web.archive.org/web/20120527133344/http://microsoftmentalist.com:80/2011/09/07/asp-net-mvc-difference-between-httpget-and-httppost-with-example/
See for example how GET and POST action methods are overloaded.
First of all same signature same name method can't be compiled in same controller it will give you the compilation error already have member of same parameter type.
You have to differentiate these two similar name method by different signature.
Regarding HttpPost and HttpGet your get method will be called whenever you have to retrive data or your page load is called for that view.
HttpPost method will be called either you are using button of type submit or your input type is button but using jquery you are giving ajax call on button click and your ajax type is "Post"
$.ajax({
url: "Action"
type: "Post"
},succees: function(){alert('succeed');});
Related
I have 2 questions on controllers.
I saw a sample code below and wondering which controller this actionlink will method calls. Delete is actually an action method. What if two controllers have same action method name "Delete"?
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
If no controller name or action method is mentioned in the submit button element like code below, Which controller and action method will it take?
<input type="submit" value="Create" />
I'll answer the 2nd question first, cause it is a part of the 1st one:
2) When you don't specify the controller, it will use current one (if in partial view it uses the controller who's "acting" at the moment of the request).
1) Controllers can have the same Action names, when you have to link a different controller than current one you need to specify it.
[Edit]
Your 2nd question has changed so i update my answer...
The submit button uses the form's "action" value, you can control it as is:
#using (Html.BeginForm("ActionName", "ControllerName"))
{
...
<input type="submit" value="Create" />
}
or directly with html tags
<form action="#Url.Action("ActionName", "ControllerName")" method="POST">
You can also omit controller or/and action parameters if you want to POST using current ones.
I need multiple submit buttons to perform different actions in the controller.
I saw an elegant solution here: How do you handle multiple submit buttons in ASP.NET MVC Framework?
With this solution, action methods can be decorated with a custom attribute. When the routes are processed a method of this custom attribute checks if the attribute's property matches the name of the clicked submit button.
But in MVC Core (RC2 nightly build) I have not found ActionNameSelectorAttribute (I also searched the Github repository). I found a similar solution which uses ActionMethodSelectorAttribute (http://www.dotnetcurry.com/aspnet-mvc/724/handle-multiple-submit-buttons-aspnet-mvc-action-methods).
ActionMethodSelectorAttribute is available but the method IsValidForRequest has a different signature. There is a parameter of type RouteContext. But I could not find the post data there. So I have nothing to compare with my custom attribute property.
Is there a similar elegant solution available in MVC Core like the ones in previous MVC versions?
You can use the HTML5 formaction attribute for this, instead of routing it server-side.
<form action="" method="post">
<input type="submit" value="Option 1" formaction="DoWorkOne" />
<input type="submit" value="Option 2" formaction="DoWorkTwo"/>
</form>
Then simply have controller actions like this:
[HttpPost]
public IActionResult DoWorkOne(TheModel model) { ... }
[HttpPost]
public IActionResult DoWorkTwo(TheModel model) { ... }
A good polyfill for older browsers can be found here.
Keep in mind that...
The first submit button will always be chosen when the user presses the carriage return.
If an error - ModelState or otherwise - occurs on the action that was posted too, it will need to send the user back to the correct view. (This is not an issue if you are posting through AJAX, though.)
ASP.NET Core 1.1.0 has the FormActionTagHelper that creates a formaction attribute.
<form>
<button asp-action="Login" asp-controller="Account">log in</button>
<button asp-action="Register" asp-controller="Account">sign up</button>
</form>
That renders like this:
<button formaction="/Account/Login">log in</button>
<button formaction="/Account/Register">sign up</button>
It also works with input tags that are type="image" or type="submit".
I have done this before and in the past I would have posted the form to different controller actions. The problem is, on a server side validation error you are either stuck with:
return View(vm) leaves the post action name in the url… yuck.
return Redirect(...) requires using TempData to save the ModelState. Also yuck.
Here is what I chose to do.
Use the name of the button to bind to a variable on POST.
The button value is an enum to distinguish the submit actions. Enum is type safe and works better in a switch statement. ;)
POST to the same action name as the GET. That way you don't get the POST action name in your URL on a server side validation error.
If there is a validation error, rebuild your view model and return View(viewModel), following the proper PGR pattern.
Using this technique, there is no need to use TempData!
In my use case, I have a User/Details page with an "Add Role" and "Remove Role" action.
Here are the buttons. They can be button instead of input tags... ;)
<button type="submit" class="btn btn-primary" name="SubmitAction" value="#UserDetailsSubmitAction.RemoveRole">Remove Role</button>
<button type="submit" class="btn btn-primary" name="SubmitAction" value="#UserDetailsSubmitAction.AddRole">Add Users to Role</button>
Here is the controller action. I refactored out the switch code blocks to their own functions to make them easier to read. I have to post to 2 different view models, so one will not be populated, but the model binder does not care!
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Details(
SelectedUserRoleViewModel removeRoleViewModel,
SelectedRoleViewModel addRoleViewModel,
UserDetailsSubmitAction submitAction)
{
switch (submitAction)
{
case UserDetailsSubmitAction.AddRole:
{
return await AddRole(addRoleViewModel);
}
case UserDetailsSubmitAction.RemoveRole:
{
return await RemoveRole(removeRoleViewModel);
}
default:
throw new ArgumentOutOfRangeException(nameof(submitAction), submitAction, null);
}
}
private async Task<IActionResult> RemoveRole(SelectedUserRoleViewModel removeRoleViewModel)
{
if (!ModelState.IsValid)
{
var viewModel = await _userService.GetDetailsViewModel(removeRoleViewModel.UserId);
return View(viewModel);
}
await _userRoleService.Remove(removeRoleViewModel.SelectedUserRoleId);
return Redirect(Request.Headers["Referer"].ToString());
}
private async Task<IActionResult> AddRole(SelectedRoleViewModel addRoleViewModel)
{
if (!ModelState.IsValid)
{
var viewModel = await _userService.GetDetailsViewModel(addRoleViewModel.UserId);
return View(viewModel);
}
await _userRoleService.Add(addRoleViewModel);
return Redirect(Request.Headers["Referer"].ToString());
}
As an alternative, you could post the form using AJAX.
An even better answer is to use jQuery Unobtrusive AJAX and forget about all the mess.
You can give your controller actions semantic names.
You don't have to redirect or use tempdata.
You don't have the post action name in the URL on server side validation errors.
On server side validation errors, you can return a form or simply the error message.
In my index.cshtml page, following line of code DOES fire HttpPost so that I could get into the HttpPost tagged Index method in the controller class.
<input type="submit" value="Run Calculation" />
[HttpPost]
public ActionResult Index(TimberBeamCalculator.Models.Dimensions dim)
{
..................
}
But the following code for kendo ui button, does not fire the HttpPost mentioned above.
#Html.Kendo().Button().Name("setting").Icon("pencil").HtmlAttributes(new { type = "button" }).Content("Run Calculation");
What am I missing??
Kendo.button does not render out as a submit button. You either need to tweak the properties of your kendo button to make it a submit button, or use jquery to do something like
$("#setting").click(function()
{
$("#idOfYourForm").submit();
});
First, you should try setting the HtmlProperties of your kendo button with type of submit. If that doesn't work, go for the jquery route I mentioned.
I have two buttons on my cshtml page - Agree and Disagree - how can I easily pass back there values to my Controller so I can make a decision to either take them to Homepage or Log them back out.
So on my cshtml page I have...
<input type="button" name='Agree' value="I Agree"
onclick="location.href='#Url.Action("DoAccept", "Home")'" />
<input type="button" name='Delete' value="I Disagree"
onclick="location.href='#Url.Action("DoAccept", "Home")'" />
So In my Home Controller then I have a DoAccept method as below:
public ActionResult DoAcceptTC()
{
//if( buttom clicked was Agree)
return RedirectToAction(VIEW_HOMEPAGE);
//else
//return Logout page..
}
So My question is how can I easily get a value back to the controller method?
There's no much point in using inputs without html form hosting them. You could use form, or simple links
//view
#using(Html.BeginForm("DoAccept"))
{
<button name="decision" value="agree">I Agree</button>
<button name="decision" value="disagree">I disagree</button>
}
//controller
public ActionResult DoAcceptTC(string decision)
{
//decision == "agree"
}
You must send paramaters along with Url.Action to the controller so that you can be able to perform your actions easily. Below link may help you.
Url.Action with params
I feel a bit stupid asking this, but I just want to know how I can have a html button call a server site action on my controller.
For example lets say I have a Text Area and a submit button. The idea would be to click the submit button and the text in the text area would get submitted to the Database. Very simple stuff.
Thank you for the help!
in the aspx:
<%Html.BeginForm("Hello","Home",FormMethod.Post);%> <!--Name of method, name of controller, formmethod -->
<input type="text" id="userName" maxlength="50" name="userName"/>
<input id="Submit1" type="submit" value="Say Hello!" />
<%Html.EndForm();%>
<h2><%= Html.Encode(TempData["Message"]) %></h2>
In the Controller (HomeController in this example):
public ViewResult Hello(string userName) //note this variable is exactly what is the name of text input on page
{
//simple example, take input and pass back out
TempData["Message"] = "Hello, " + userName;
return View("Index",TempData);
}
EDIT: To address additional question about maintaining URL
One method to accomplish "staying in place" as far as your URL is to "overload" the Index method of your controller like below
[AcceptVerbs(HttpVerbs.Post)] //This is KEY <-----
public ActionResult Index(string userName)
{
//simple example, take input and pass back out
TempData["Message"] = "Hello, " + userName;
return View("Index",TempData);
}
Then in your Index.aspx change the Html.Begin Form like below, your just pointing to the default action now
<%Html.BeginForm("Index","Home",FormMethod.Post);%>
Since you are posting to the controller and not getting(the default index action) the version that is setup to AcceptVerb POST and take the userName string will run and your URL should be maintained
In the view's HTML, the text area and submit button would be part of a form, and that form's action attribute would point to '' and you'd put a part into the same controller that generated that view that checks for POST data. If there's POST data, you'd write it to the database.
The form would look something like this:
<form action='' method='post'>
<textarea name="text"></textarea>
<input type="submit" value="Submit"/>
</form>
In the POST, you'll see a variable with a key that matches the name attribute of the textarea (in this case, text). You can read it from there.
If you like, you can also change the action attribute on the form to a URL, in which case you'd write the part that checks the POST data in a controller that that URL points to.