ASP.net MVC Can you overload an Index() HttpPost? - c#

Let's say I have a page with 2 forms that both submit different data on the same page, one submits two ID's, and the other submits only 1 ID. They are both posting back to the same page (itself). Here is what the HTML would look like...
<form method="post">
<select name="regID">
...
</select>
<select name="jobID">
...
</select>
<input type="submit" value="Add">
</form>
<form method="post">
<button name="ID" type="submit" value="#ID">Remove</button>
</form>
Now, to handle the first form in the controller I can do
[HttpPost]
public ActionResult Index(int regID, int jobID)
{
....
}
However, if I try to handle the second form by adding
[HttpPost]
public ActionResult Index(int ID)
{
....
}
When I click the submit button, I will now get the error
The current request for action 'Index' on controller type 'UserJobController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Index(Int32) on type careerninja.Controllers.UserJobController
System.Web.Mvc.ActionResult Index(Int32, Int32) on type careerninja.Controllers.UserJobController
So, is it possible in the controller to overload the [HttpPost] method with different values to handle 2 different sets of form data, or is this not possible? Is there another solution I might not be grasping to handle this sort of issue?
Basically, for the second form I want to have a "Remove" button that when clicked, calls the controller to remove the item, removes the item, then returns the Index() view.

I think an improvement in your design would make the problem you are having not an issue anymore. It sounds like you believe everything might have to go through your Index() method, which is not the case. Redesigning your method's name to the behavior of what the action is doing is typically how I name my methods.
Basically, for the second form I want to have a "Remove" button that when clicked, calls the controller to remove the item, removes the item, then returns the Index() view.
So create your method called Remove() and have it redirect to the Index()
public ActionResult Remove(int id)
{
// do some work
this.RedirectToAction("Index");
}
I would recommend making your method names represent what they are doing.
public ActionResult Add(int regID, int jobID)
{
// do some work
this.RedirectToAction("Index");
}
Note: This is also an important design for the User Interface. When a page typically does a POST to the server, and then the server returns HTML, if the users decides to refresh the page , a popup will typically be presented asking them if they want to resubmit data. Instead the previous examples did a server side redirect, which starts a second request as a GET and prevents the popup from occurring on refresh.

Related

Return to the same view after asp-action

This is about an MVC WebApp (that also uses EF). My question is about returning to the same view after an asp-action.
I display a list of users (db records on my .cshtml page). Next to each user, I have a Reset Password hyperlink. Upon clicking the hyperlink, I use EF to reset the user's password. This is done through an asp-action
<a asp-action="ResetPassword" asp-route-userid="#item.userid">Reset Password</a>
This reset asp-action is all happening fine.
But post asp-action, I would like to return back to the same view (from where I originally clicked the Reset Password hyperlink).
I do not need any refreshes or re-launch of the view. I find examples in StackOverflow about using Ajax if its a form/submit button action. In my case there's no submit button action or form or another view being shown.
Any suggestions on how to return back to the same View?
In your controller ResetPassword method, you can redirect to the action that initially displayed your view. You don't have your controller or view names listed in your question, so I'll make some guesses here:
public class UsersController : Controller
{
public ActionResult Index()
{
//get user data/setup model (or do it from the view via ajax)
return View(model);
}
public ActionResult ResetPassword(int id)
{
//do reset password work
//return redirect to the Index page that lists your user data
return RedirectToAction("Index", "Users");
}
}
Now, while the above will work, it would be a better experience for the user (and lower server/db impact) if the call to ResetPassword was done via ajax, since a full page refresh wouldn't be needed.

Why does clicking #Url.Action throws an error?

When I click a button input which triggers an action ImagePopup inside a controller Stories but it throws an error.
Code:
#{
var listData = (List<HimHer.Models.Stories>)ViewBag.Grid;
foreach (var imageName in listData)
{
<div class="col-md-4">
#Html.HiddenFor(model => model.Story)
<input
class="img-responsive img-thumbnail"
type="image"
onclick="location.href='#Url.Action("ImagePopup", "Stories", new {story= imageName.Story})'"
src="#("/UploadedFiles/"+ imageName.Image)"
alt="Submit"
width="100%"
height="100%"/>
</div>
}
}
Upon clicking an input it throws an error:
The resource cannot be found. Requested URL: /Stories/ImagePopup
Even though it exists. It is right inside the Stories folder. It's a partial view without a model.
[HttpPost]
public ActionResult ImagePopup(string story)
{
ViewBag.PopupStyle = "";
ViewBag.PopupStory = story;
return View("GetImagesStories");
}
What am I doing wrong?
It's looking for an HTTPGet Action I believe.
If you want to call your post, you'll need to use HTML.BeginForm but it can get hairy if there are too many on a page.
Setting the href location of the current page:
location.href=
Causes the browser to do a Get Request type and does not post any form data back to your controller. Because the method on your controller specifically only works for post requests (because of the [HttpPost] attribute) there is no other matching methods that could work, thus you get an Exception.
Solutions:
You can continue using the Get method. Replace [HttpPost] with [HttpGet] will get you half way there. The other requirement will be for you to make sure the Url.Action code contains all the necessary information to be posted back (for example all the data in #Html.HiddenFor(model => model.Story) is not included, I don't know if you need it or not).
Or
You can modify your code to use the Post method. Change your <input type="image" to a <button type="submit"> and add a form around each button and hidden input element.
try using [HttpGet] attribute for your ImagePopup action method

Basic MVC (button to send e-mail) - ActionLink?

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.

Modify List in View and send back to Controller

I have the following in my controller:
ViewBag.UserList = userlist.ToList();
return View ();
The userlist have the following 3 fields:
First Name, Last Name, Phone Number
How do I go about creating a list in the view where the user can modify the fields?
I am well aware of using the foreach to loop through the list but how do I program the ability to modify the records?
I also need to know how I can then send this list back to the controller so that I can insert the records into my database.
As you said, iterate the list in the view, Use the TextboxFor() method of each property you want to modify, add a submit button, in the controller action add List<T> list to catch the changes and there you go.
I'll assume this is a web mvc project since it's tagged asp.net-mvc.
the view will render html. the html will contain (at a minimum) a form, inputs for each editable value and a submit button. ultimately the markup will look like this
<form id="?" method="post" action="usercontroller/update">
<input name="users.firstname[0]" value="john"/>
<input name="users.lastname[0]" value="doe"/>
<input name="users.phonenumber[0]" value="555-123-4567"/>
<input name="users.firstname[1]" value="jane"/>
<input name="users.lastname[1]" value="smith"/>
<input name="users.phonenumber[1]" value="555-123-4567"/>
<submit value="update"/>
</form>
on the server you would have a controller
class UserController : BaseClassIfNecessary
{
public ActionResult() Update(User[] users)
{
foreach user update database
return RedirectAction("url");
}
}

MVC: what code gets called when you click the "submit" button?

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.

Categories