Multiple different submit buttons in one form - html

I've tried implementing the option explained in 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;
}
}
My controller actions:
[HttpParamAction]
[HttpPost]
public virtual ActionResult EditAccouncement(_AccouncementPostViewModel m)
[HttpParamAction]
[HttpPost]
public virtual PartialViewResult DeleteAnnouncement(int id)
My form:
#using (Ajax.BeginForm("Action", ajaxOptions: new AjaxOptions()
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "announcement" + #Model.id
}))
{
//form values omitted
<button type="submit" class="submitbutton" name="edit">Change details</button>
<button type="submit" class="submitbutton" name="delete">Delete</button>
}
However the controller action being called is still just the Action method (which doesn't exist). Am I missing something?

Your problem is caused by mismatch between your action names and the button name attributes.
The value of the name attribute on your buttons needs to match the action names, like so:
<button type="submit" name="EditAccouncement">Change details</button>
<button type="submit" name="DeleteAnnouncement">Delete</button>
Update: I would suggest a different approach all together. This solution seems counter-intuitive to me, and not particularly easy to follow.
You could just as easily use JavaScript (e.g. jquery) to handle the form submits 'manually', by hooking up two different event handlers to your buttons. One event would make a POST to the EditAccouncement (typo!) action and one would make a POST to the DeleteAnnouncement action.
I made a mockup on jsfiddle which demonstrates the code: http://jsfiddle.net/wmWNj/3/
Update 2: fixed typos in jsfiddle

Related

Thymleaf how to take an input and then redirect to another page

I'm learning Spring boot. I have a list of products with unique ids, and I want to implement a "lookup by id" functionality, but I don't know how to do it, I searched but got totally different stuff.
I already have a #Getmapping method like this:
#Getmapping(/products/{id})
If I manually type in the id in the url I'll get what I what. But I want to have an input box in the HTML page like:
<form>
Look up by id: <input/>
</form>
and after I submit the form it'll redirect to that page. For example, if I enter input of 1, it'll go to localhost:8080/products/1
I've been searching but all I got was stuff about #Postmapping.
Add a #PostMapping to your controller:
#Controller
#RequestMapping("/products")
public class ProductController {
#GetMapping //Controller method for showing the empty form
public String index(Model model) {
model.addAttribute("formData", new SearchFormData()); // Create an empty form data object so Thymeleaf can bind to it
return "index";
}
#PostMapping
public String searchById(SearchFormData formData) {
return "redirect:/products/" + formData.getId(); //Use the value the user entered in the form to do the redirect
}
#GetMapping("/{id}")
public String showProduct(#PathVariable("id") long id) {
...
}
}
With SearchFormData representing the form fields (there is only 1 field in this case):
public class SearchFormData {
private long id;
// getters and setters
And update Thymeleaf template like this:
<form th:action="#{/products}" th:method="post" th:object="${formData}">
<input th:field="*{id}" type="number">
<button type="submit">Search</button>
</form>
Note that the value of th:object needs to match with the name used to add the SearchFormData instance to the model.
See Form handling with Thymeleaf for more info.
The following simple code will direct you to a URL that is generated from a concatenation of the base address of the <form>'s action attribute and the value of its first <input>:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
this.action="/product/"+this.querySelector("input").value;
console.log(this.action);
// in real code: uncomment next line!
// this.submit()
})
<form>
Look up by id: <input type="text" value="123" />
</form>
In the real code you will delete the console.log() und uncomment the following line: this.submit().
Alternatively you could also do:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
location = "/product/"+this.querySelector("input").value;
})
This will redirect you to the page without actually submitting the form.

.net mvc html help ActionLink and parameters to controller class

so simply want to make a button that will call the controller action passing a parameter...
have everything I believe in place but unable to configure/reference the parameter in the actionlink helper...
Yes I will be refactoring my button onclick once I get through this html helper setup...
<h1 style="font-size:30px">Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
<form action="/action_page.php">
<label for="productKey">Product Key:</label>
<input type="text" id="productKey" name="productKey"><br><br>
</form>
<p>Click the "Get Key" button and a trial key will be generated custom to your IBMi hardware".</p>
<p>
#Html.ActionLink(
"Get Key",
"GetTrialKey", // controller action
"HomeController", // controller
new { productKey }, // IT DOES NOT LIKE PRODUCTKEY (REFERENCED ABOVE)
new { #class = "btn btn-info" }) // html attributes
</p>
<div class="display-2">
<a button class="text-center, btn btn-info form-control text-white" typeof="button" onclick="location.href='#Url.Action("GetTrialKey(productKey)")'">Get Key</button></a>
<p>
<br />
</p>
</div>
refactored to...
view...
<form action="HomeController/getTrialKey" method="POST">
<label for="productKey">Product Key:</label>
<input type="text" name="productKey" maxlength="22" value="xxx xxxxxxx xxxx xxxxx"><br><br>
<input type="submit" value="Get Trial Key" class="btn btn-primary" />
</form>
controller...
[HttpPost]
public async Task<IActionResult> getTrialKey(string productKey)
{
when I run it i get...
This localhost page can’t be foundNo webpage was found for the web address: https://localhost:44346/HomeController/getTrialKey
Referring back to one of the comments:
I didn't discourage you from using HTML Helpers. I just meant the way you constructed the form and you used ActionLink was wrong. And it would be easier to just have an input for the product key inside the form, if that's the only thing you want to post back to the server.
And I would highly recommend you to read through documentations from Microsoft, at least this one: https://dotnet.microsoft.com/apps/aspnet/mvc to understand what a MVC is. From your code sample, I didn't see you used M - Model at all.
Anyway, if you just only want to get the product key the user types in, I would do it like this:
Define a Controller
I dislike the idea of putting everything under /home (i.e., HomeController). Just think about the URL to the page that would make sense to the user.
Now I am guessing what you are trying to do. I saw terms like product keys and trial keys. What about a controller called ProductKeyController:
public class ProductKeyController : Controller
{
// This corresponds to /productkeys, and you can list all the product keys
// on the view it returns.
public ActionResult Index()
{
return View();
}
// This corresponds to /productkeys/create, and you can create a specific product
// key by asking the user to provide a trial key?
// The view this returns might be the page where you build the form
public ActionResult Create()
{
...
return View();
}
// This corresponds the form post.
[HttpPost]
public ActionResult Create(CreateProductKeyViewModel model)
{
...
return View(model);
}
}
The view model
Your MVC controller is responsible to fetch data, if needed, build a view model, and pass it to the view. When you create a product key, if you need to ask the user to enter anything, you can declare a model and properties within it:
public class CreateProductKeyViewModel
{
[Required]
[Display(Name = "Trial Key")]
[MaxLength(22)]
public string TrialKey { get; set; }
}
The View Create.cshtml
Since you know the controller will be passing the view model to the view, you can declare it on top of the view so that everything you do with the view model inside the view is strongly-typed.
#model CreateProductViewModel
#{
Layout = "xxx";
}
<h1>Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
#using(Html.BeginForm("create", "productKey", new { area = "" }, FormMethod.Post))
{
#Html.LabelFor(x => x.TrialKey)
#Html.TextBoxFor(x => x.TrialKey)
<button type="submit">Create</button>
}
See how everything within the view is strongly-typed? You don't have to manually create the form and the input for asking user for the trial key.
And when the user enters the trial key and presses submit, it will post back to the Post method of Create. Since the view is declared with the view model, and the view model is the parameter of the create method, MVC has already done the model binding for you hence you will get what user entered on the post back.
This is at least something to get you started.
Note: I wrote everything by hand. Not tested.

Multibutton form doesn't map data back to textboxes .net core 1.1

Ok, so now I'm trying to learn .net core mcv and I'm having a problem mapping data from MySQL back to my form. When I make the form as a single text box with a single button, and the other fields outside the form (but on the same page), the mapping works fine. If I include all the fields within the form, the data is obtained but not displayed on the page. I have even gone so far as to code one of the multiple submit buttons as an update of the data. I use the first text box to get the item from the database, which it does (but does not map to the text-boxes), then in the second text box (which should have the existing data, but is empty) I put the information to update in the database, click on the submit button for that text box, and the database is updated (but the text boxes in the view remain blank).
My model:
using System;
namespace DbTest.Models
{
public class ProductInventory
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public int Field4 { get; set; }
}
}
my controller:
using System;
using Microsoft.AspNetCore.Mvc;
using MySql.Data.MySqlClient;
using Microsoft.AspNetCore.Authorization;
using DbTest.Models;
namespace DbTest.Controllers
{
public class InventoryController : Controller
{
// [Authorize]
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ProcessForm(string button, ProductInventory p)
{
IActionResult toDo = null;
if (button == "Button1")
{
toDo = GetItem(p);
}
if (button == "Button2")
{
toDo = UpdateField2(p);
}
if (button == "Button3")
{
toDo = UpdateField3(p);
}
if (button == "Button4")
{
toDo = UpdateField4(p);
}
return toDo;
}
// [HttpPost]
public IActionResult GetItem(ProductInventory p)
{
//CODE SNIP - DATABASE QUERY, IT ALL WORKS, SO WHY BOTHER YOU WITH THE DETAILS?
return View("Index", p);
}
public IActionResult UpdateField2(ProductInventory p)
{
//CODE SNIP - DATABASE UPDATE, ALL WORKS, NOTHING TO SEE HERE
return View("Index", p);
}
}
}
And finally, my view:
#model DbTest.Models.ProductInventory
#{
ViewData["Title"] = "Inventory Page";
}
#using (Html.BeginForm("ProcessForm", "Inventory", FormMethod.Post))
{
<div>
Search Item (Field 1):
#Html.TextBoxFor(model => model.Field1)
<input type="submit" name="button" value="Button1" />
</div>
<div>
Field 2:
#Html.TextBoxFor(model => model.Field2)
<input type="submit" name="button" value="Button2" />
</div>
<div>
Field 3:
#Html.TextBoxFor(model => model.Field3)
<input type="submit" name="button" value="Button3" />
</div>
<div>
Field 4:
#Html.TextBoxFor(model => model.Field4)
<input type="submit" name="button" value="Button4" />
</div>
}
To reiterate, if I close the form after Button1:
#using (Html.BeginForm("ProcessForm", "Inventory", FormMethod.Post))
{
<div>
Search Item (Field 1):
#Html.TextBoxFor(model => model.Field1)
<input type="submit" name="button" value="Button1" />
</div>
}
<div>
Field 2:
//etc.
the mapping works, but only the first field and button of the form work. With the form around all four fields and buttons, the mapping doesn't work, but the coding of the second button DOES update the database on clicking Button2.
Can someone explain what I've done wrong here?
Thanks!
At first, don't use html helpers in ASP.NET Core.They work but it is not best practice. Instead use tag helpers wherever possible. Furthermore, don't use your db models as view models.
Regarding your Index action: You forgot to pass a view model to your view.
In your ProcessForm action you instantiate IActionResult and then assign it with a (action) function. Don't do that. Instead use return RedirectToAction("ActionName");.
In your case I would handle the DB updates inside the ProcessForm action or in a function, which doesn't return IActionResult.
In conclusion, I can only recommend you to read the ASP.NET Core documentation and then ask again if you still don't get it to work. I recommend you to start with reading this.

Forwarding the partial view's details to HttpPost method of a controller when button in partial view is clicked

Whenever following HttpGet method in controller is called it generates a partial view.
Controller HttpGet Method
[HttpGet]
public ActionResult AddCredit(Guid creditBalanceId)
{
var newCredit = new AddCredits()
{
CreditBalanceId = creditBalanceId
};
return PartialView(newCredit);
}
View
#model AdminPortal.Areas.Customer.Models.ViewModels.AddCredits
#Html.HiddenFor(m=>m.CreditBalanceId)
<div class="input-small" id="credit">#Html.EditorFor(m=>m.CreditToAdd) </div>
#Html.ActionLink("Add","AddCredit", new {#class="btn"})
Whenever Add button is clicked in the partial view, I want it to be forwarded to HttpPost method of my controller with HiddenFor(CreditBalanceId) and CreditToAdd value
[HttpPost]
public ActionResult AddCredit(AddCredits credits)
{
_businessUnitRepository.AddCredits(credits);
Information("Credits Successfully added!");
return RedirectToAction("LicenseDetails");
}
Question
What changes do I need to make to my view so that when the button is clicked, i get forwarded to httppost method in controller with all the values?
Have you tried this?
#using(Html.BeginForm("Add", "AddCredit"))
{
#Html.HiddenFor(m=>m.CreditBalanceId)
<div class="input-small" id="credit">#Html.EditorFor(m=>m.CreditToAdd) </div>
<button type="submit" class="btn">Add Credit</button>
}
You could do it quite easily using something like Html.BeginForm and an type of submit. This link is a pretty good example MVC4 forms that save to entity framework models

Different behaviours for two different submit buttons?

I am trying to have two submit buttons in my form - one accepts meetings; the other declines them. They will both have different behaviours. How can I do is in my C# code?
The functionality I want is essentially
if(isPost) {
if(//accept button pressed(Request.Form[???]))
{
}
else
{
}
}
Here is my HTML :
<button name="accept" type="submit">Accept</button>
<div class="spacer"></div>
<button name="decline" type="submit">Decline</button>
<div class="spacer"></div>
Simple enough, but I cannot find a test for this on the Internet or in any documentation. Does anyone know what I would have for this ?
Give each button element the same name (in this example, 'SubmitButton') but a different value, then do a check for the different values in your server code, ie:
<button type="submit" name="SubmitButton" value="Accept">Accept</button>
<button type="submit" name="SubmitButton" value="Decline">Decline</button>
Then in your server code:
string buttonClicked = Request.Form["SubmitButton"]
if(buttonClicked == "Accept")
{
// Accept code
}
else if(buttonClicked == "Decline")
{
// Decline code
}
Be aware that this won't work on some earlier versions of IE though, and you may have to do a little javascript on the client prior to the post request being made.
As far as I remember, you have a controller action looking like this:
public ActionResult MyAction(string accept, string decline)
{
if (!string.IsNullOrEmpty(accept))
{
//do something
}
else
{
//do something else
}
}
Presuming you are using MVC3 you would do something like
#using (Html.BeginForm("Accept","Meeting"))
{
<input type="submit" value="Accept" />
}
#using (Html.BeginForm("Decline","Meeting"))
{
<input type="submit" value="Decline" />
}
You would then just have your accept and decline code in your Accept and Decline actions of your meeting controller respectively. No need for an if statement at all.
Example controller
public class MeetingController : Controller
{
[HttpPost]
public ActionResult Accept()
{
//Do accept stuff
return View();
}
[HttpPost]
public ActionResult Decline()
{
//Do decline stuff
return View();
}
}
Generally in the codebehind in c# you'd be looking for which button was clicked. You'd have an event handler for each button, and you code would react according to which button was clicked.
this.btnRejectAppointment.Click += new System.EventHandler(this.btnRejectAppointment_Click);
And then your method
private void btnRejectAppointment_Click(object sender, System.EventArgs e)
{
//code to reject appt.
}
And you'd have the SAME set for the other button.
ASP.NET Button control has OnClick event, so add different handler to every your button:
<asp:Button OnClick="MyHandler1" ... />
in code behind:
protected void MyHandler1(object sender, EventArgs args)
{ /* do stuff */ }
One of the possible solutions - use two buttons of type LinkButton but specify slightly different PostBackUrl:
<!-- Accept meeting button -->
<asp:LinkButton ID="acceptMeeting" runat="server"
PostBackUrl="MeetingsManager.aspx?Mode=Accept" />
<!-- Decline meeting button -->
<asp:LinkButton ID="declineMeeting" runat="server"
PostBackUrl="MeetingsManager.aspx?Mode=Decline" />
And in code behind:
public enum Mode
{
Unknown,
Accept,
Decline
}
protected void Page_Load(object sender, EventArgs args)
{
Mode currentMode = Mode.Unknown;
var rawMode = Request.QueryString["Mode"];
if (rawMode != null)
{
currentMode = (Mode)Enum.Parse(
typeof(Mode),
rawMode.ToString())
}
}