MVC newbie question; I'm learning by playing around rather than Reading The Manual... :)
I see when I create an "Edit" view that the auto-generated view includes a "submit" button:
<input type="submit" value="Save" />
But what code gets called behind the scenes to do this save? Specifically, the model underlying this view has its own fancy save logic in code that I would want to call. How do I get the view to invoke my code instead of whatever standard code is being called invisibly behind the scenes?
It's not the button that defines what happens, but the form itself. The button of type submit (one per form) just triggers the form submission, which is handled by the form itself.
A form has an action - e.g.:
<form name="input" action="users/save" method="post">
<!-- Form content goes here -->
<input type="submit" value="Submit" />
</form>
The action is an URL and what happens is that the browser collects the values of all the fields in the form (<input...>) and posts them to the specified url.
In ASP.NET MVC forms are usually defined using the Html helpers, so that building the URL for the form action is delegated to ASP.NET MVC. For the above for example:
<% using(Html.BeginForm("Save", "Users")) %>
<% { %>
<!-- Form content goes here -->
<input type="submit" value="Save" />
<% } %>
Which in this case will create a url /users/save and the form will post to that url. That in terms will trigger the ASP.NET routing which will handle the /users/save url and break it into chunks so that it knows that it has to invoke the "Save" action method on the "Users" controller class. It will then read all the incoming field name-value pairs and try to map them to the method parameter names if any.
It would call whatever public action method the form action is pointing to on your controller. You can then call save on the view model.
public virtual ActionResult Save(MyViewModel model) {
model.Save();
--- more code to do stuff here
}
Set your form action to MyController/Save
You can also use using (Html.BeginForm... in your code to point the form to a specific action method on a specific controller.
when you click submit button, request goes to the HTTp Module which directs it to corresponding controller action. when edit view is created from template the post address of the form is same as of the edit form i.e if you are visiting /home/edit you can see following html in form's opening tag
<form method="post" action="/home/edit">
you can have another action method that only accepts post requests like
[HttpPost]
public ActionResult Edit(int id, ViewModel model)
{
//put your logic here handling submitted values
}
HttpPost attribute tells that it will only handle post request as opposed to get requested used to render the form
it calls the Action method defined in the action part of the form element
eg:
<form action="/Account/LogOn" id="loginForm" method="post">
The LogOn action in the Account controller will be invoked in this form
The ViewPage has a BeginForm Method using (Html.BeginForm() at the top which would render the FormTag. This method has a overload which takes ActionName and controller Name. So you can specify the action in your controller which has to be called.
Related
Can someone explain the following unexpected behaviour. I've tested this in both core 2.2 and 3.1.
I'm trying to make sense of posting 2 different forms on the same razor page.
Here's the HTML:
<form method="post">
<button type="submit">post</button>
</form>
<form method="post">
<button type="submit" asp-page-handler="WTF" >post wtf</button>
</form>
Here's the razor page behind:
public void OnGet()
{
Debug.WriteLine("Get");
}
public void OnPost()
{
Debug.WriteLine("Post");
}
public void OnPostWTF()
{
Debug.WriteLine("PostWTF");
}
I expect that when I press the 'post' button, the OnPost action gets called. When I press the 'post wtf' button, the OnPostWTF action gets called. That kind of happens. If I press the 'post' button initially, the expected action is called. But, as soon as I press the 'post wtf' button, ALL subsequent posts only call the OnPostWTF action, regardless which button is pressed!
Very nice guide here: https://www.learnrazorpages.com/razor-pages/handler-methods
The name of the handler is added to the form's action as a query string parameter.
So when you are posting without a handler then the current url is being posted to.
In your case it will be the one you have switched to with the query parameter handler=WTF.
The best practice when you have multiple forms in the same page, is to setup a handler for each request to avoid such troubles.
I have a 'mail server configuration' kind of view. On this view are 2 buttons:
[SOME FORM FIELDS]
<input class="button" type="submit" value="#T("Save")" />
<input class="button" type="submit" value="#T("Send Test Email")" />
The first button calls off to my controller and returns the same view with any validation/success messages (this is a form):
[HttpPost]
public ActionResult Index(MailServerSettingsViewModel viewModel)
{
...
That works brilliantly, but obviously the 'Send Test Email' button will do the same.
Assuming I don't want to come away from the page (i.e. load a different view), but want to call off to another controller to send a test e-mail, is the 'proper' way to do this to use the ActionLink helper? From this other controller can I then return this same form view? Or can I somehow use the same controller but determine which button was pressed to decide whether to validate the view model or just call off to another service/class/whatever to send the test e-mail and responding appropriately?
You can probably tell from the way I'm asking this that I come from a WebForms background and it's still a case of me getting used to what's what with MVC.
What I've Tried
For now, I'm actually calling off to this other controller asynchronously with AJAX. It actually works well, and is probably most appropriate, but this won't always be the case so I want to know how I'd achieve the above for other scenarios.
If you don't want ajax you can start a thread to send the email in your controller.
#Html.ActionLink("Send Test Email",
"actionName", "ControllerName",
new { MailServerSettingsViewModel }, new { #class = "button" })
If you set the 'name' attribute on both submit buttons to the same value, you can detect which was clicked in your controller by inspecting the value.
I have this:
<form id="import_form" method="post" enctype="multipart/form-data" action="/Blah/Blah">
<input type="file" id="fileUpload" name="fileUpload"/>
<input type="submit" name="submit" value="Import"/>
</form>
$('#import_form').submit(function () {
...
});
Here is the c# method:
[AcceptVerbs(HttpVerbs.Post)]
public string Blah(HttpPostedFileBase fileUpload, FormCollection form)
{ ... }
I want when Blah finishes executing a javacript code to start executing. How to do this?
The submit event is called before it.
This depends on how the form is being submitted.
If you're submitting the form via AJAX then you can execute some JavaScript in the handler for the response. However, given that there's a submit button, I'm assuming for the moment that you're not doing this via AJAX and are instead posting the whole page to the server and rendering a response.
In this case, to execute some JavaScript after the form post, you're going to need to render that JavaScript in the response from the server as part of the next page. When the server constructs the view, include the JavaScript you want to execute in that view.
Keep in mind the request/response nature of the web. When something on the server executes, the client is unaware of it and disconnected from it. The end result of any server-side processing should be an HTTP response to the client. In the event of submitting a form or clicking a link or anything which results in a page reload, that response is in the form of a new page (view). So anything that you want to do on the client after the server-side processing needs to happen as part of that response.
Edit: I just noticed that Blah is returning a string. Is this even working for you? How does the form submit result in a new view? Or am I unaware of a feature in ASP.NET MVC?
This is what I did in the end.
My form returns the exact same view with whom it was called.
I add a ViewData in the Blah method.
In the view, in the $(function()) event I check if ViewData has a value, I execute the javascript code.
I have a PartialView which I need to create a button on, and once a user clicks on this button, it must send a HTTPGET to a controller that receives the model. How do I call the HTTPGET action from the PartialView?
Any idea as to how to do this in MVC3?
may be this solution work :
<form method="get" action="controllerName" enctype="multipart/form-data">
#html.partial("viewName")
<input type="submit" value="Send" ... />
</form>
There are a few ways that you can achieve this. The easiest way is to use an ajax request to send the data back to the controller using jQuery (http://api.jquery.com/jQuery.get/). However I would not use a HTTP GET for this. Although not enforced it is best to stick to using the HTTP verbs in the way that they were intended ie GET is for receiving data, POST is for sending data to the server.
use this to create the form in your partial view,
#{using (Html.BeginForm("Create", "Person", FormMethod.Get, new {
enctype = "multipart/form-data",
id = "<id of the form>" }))
{
//body of your form
}
Here you can see that, type of the form method has been passed in to the Html.BeginForm method as "FormMethod.Get". If you want to sent the response to a Post method, uset "FormMethod.Post".
How do I have 2 buttons and each button to perform a different post back action?
For example :
I want button1 to submit the
contents of the form.
I want button2
to display some data from the
database without storing whats in
the form.
Right now,since im using form collection,both buttons seem to store the data.How do I differentiate between the buttons?
Thanks
You need to check for the existence of the relevant Submit button in the Form collection and do the relevant action.
e.g assuming Button1 & Button2 and you only want one form
in controller action method that accepts POST
public actionresult SomeMethod(FormCollection form)
{
if (form.AllKeys.Contains("Button1")
{
doSomething();
}
else // assuming only two submit buttons
{
doSomethingElse();
}
}
HTH ,
Dan
You can put each button in a different form and have different actions handle that submit. Like this:
<% using (Html.BeginForm("/FirstAction")) {%>
<input type="submit" />
<% } %>
<% using (Html.BeginForm("/SecondAction")) {%>
<input type="submit" />
<% } %>
The two form approach is fine as long as the forms don't need to share input fields (textboxes etc.). If they need to share, or the buttons are to be positioned adjacent to each other (such that the forms overlap in some way) then using two forms begins to break down.
Also, I'm less keen on dispatching within the controller, based on detecting which button has been clicked from the FormCollection. The dispatching method produces a layer of indirection which makes things less clear: you cannot decorate the methods to which you dispatch with AcceptVerbs, for instance.
Instead, we simply catch the JavaScript onclick event and set the form's action.
<% using (Html.BeginForm("Save", "Contact"))
{ %>
<input type="submit" value="Save" />
<input type="submit" value="Find Similar" onclick="this.form.action = rootDir + 'Contact/Find'" />
<% }%>
Buttons do not store anything. Buttons initiate HTTP POST request.
Now if you wish those POSTs to hit different controller actions, you need to put them into two diferent forms and specify two different form urls.
<% BeginForm ("/action1"); %>
<input type="submit" value="Action1" />
<% EndForm ();
<% BeginForm ("/action2"); %>
<input type="submit" value="Action2" />
<% EndForm ();
Then you need to map those routes to two different controller actions (in your Global.asax):
routes.MapRoute(
"Action1",
"/action1",
new { controller = "Test", action = "PostAction1" });
routes.MapRoute(
"Action2",
"/action2",
new { controller = "Test", action = "PostAction2" });
And your controller is there waiting for those actions:
public class TestController : Controller
{
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult PostAction1 (FormCollection form)
{
// Do something
return View ();
}
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult PostAction2 (FormCollection form)
{
// Do something
return View ();
}
}
Then you decide yourself whatever you wish to happen on those actions, save data or do something else.