I have got several forms on my page. Here is an example of one.
<table width="400px">
#foreach (var menu in Model)
{
using (Html.BeginForm())
{
<tr>
<td>
#menu.Menus
</td>
<td>
#menu.Submenu
</td>
<td>
<input type="submit" name="update" value="Update" />
<input type="submit" name="delete" value="Delete" />
#Html.Hidden(#menu.MenuID.ToString());
</td>
</tr>
}
}
</table>
I want each of my actions to be invoked by the forms submit button. So the an action will be invoked depending on whether there was the update submit button pressed, or delete button pressed or whether a button was pressed with no name attribute...I also want my form to be post for only.
How can I achieve all that?
In the view you wrap each button with its own form. For example for update
#using (Html.BeginForm("Update","Home"))
{
#Html.Hidden(menu.MenuID.ToString());
}
#using (Html.BeginForm("Delete","Home"))
{
#Html.Hidden(menu.MenuID.ToString());
}
For each action you'll have in the controller
[HttpPost]
public ActionResult Update(int id)
{
//do stuff
}
[HttpPost]
public ActionResult Delete(int id)
{
//do stuff
}
I believe though the update needs more than an id, those fields should be included in the form and in the action's arguments.
Related
I am trying to accomplish the following:
I have a list of fruits, that are stored in a table with two columns "id", "name" and "color".
Next to each fruit, I got a "modify" button. What I want to do here is being able to display the fruit in a form and being able to modify the "name" and "color" attributes.
I don't understand why, but when I click the "modify" button, the form is being displayed but the properties of the fruits that I clicked are not.
Here is the code:
Controller:
#RequestMapping(value = "/fruit/modify", method = RequestMethod.POST)
public String modifyFruit( #RequestParam("id") int id, ModelMap model) {
Fruit fruit = fruitManager.getFruitById(id);
model.addAttribute("fruit", fruit);
return "redirect:/modifyfruit";
}
#RequestMapping(value = "/modifyfruit", method = RequestMethod.GET)
public String showAddForm(#ModelAttribute("fruit") Fruit fruit, ModelMap model) {
model.addAttribute("fruit", fruit);
return "/secure/modifyfruit";
}
Here is the modify button that I am displaying next to each fruit in my list:
<td>
<c:url var="modifyUrl" value="/fruit/modify.html"/>
<form id="${fruitForm}" action="${modifyUrl}" method="POST">
<input id="id" name="id" type="hidden" value="${fruit.id}"/>
<input type="submit" value="modify"/>
</form>
</td>
Here is the modifyfruit.jsp that I am using to display the form that I want to populate:
<body>
<form:form method="post" commandName="fruit">
<table width="95%" bgcolor="f8f8ff" border="0" cellspacing="0"
cellpadding="5">
<tr>
<td align="right">Name:</td>
<td><form:input path="title" value="${fruit.name}"/></td>
</tr>
<tr>
<td align="right">Color:</td>
<td><form:input path="color" value="${fruit.color}"/></td>
</tr>
</table>
<br>
<input type="submit" align="center" value="Post Ad">
</form:form>
</body>
Your redirect is simply going to that new URL without any request params being added. Therefore your fruit ID is being discarded, which is why nothing gets displayed.
The redirect seems pointless - why not return the same view name string as the GET version instead?
To redirect with the params, try:
return "redirect:/modifyfruit?id=" + id;
EDIT: just noticed you have added the Fruit to the model - this does not get transferred in a redirect and wouldn't work anyway.
Is this possible to use Ajax.Beginform with update target inside of ajax form. like this:
using(Ajax.BeginForm("EditPhone", new { id = item.Id.Value }, new AjaxOptions {
UpdateTargetId = "TRTarget"})) {
<tr class="gradeA odd" id="TRTarget">
<input type"submit" value="submit" />
</tr>
}
Update
OK if it's possible so what is wrong with this?
This is my partial view that another partial view rendered inside it:
using(Ajax.BeginForm("EditPhone", new { id = item.Id.Value }, new AjaxOptions {
UpdateTargetId = "TRTarget"})) {
<tr class="gradeA odd" id="TRTarget">
#{Html.RenderPartial("_PhoneRow", item);}
</tr>
}
and _PhoneRow:
#model MyModel
<td>#Html.DisplayFor(model=>model.Number)</td>
<td>#Html.DisplayFor(modelItem => Model.PhoneKind)</td>
<td><input type="submit" value="Edit" class="button" /></td>
And EditPhone Action:
public ActionResult EditPhone(long Id){
//Get model
return PartialView("_EditPhoneRow", model);
}
And _EditPhoneRow:
<td>#Html.EditorFor(model => model.MainModel.Number)</td>
<td>#Html.EditorFor(model => model.MainModel.PhoneKind)</td>
<td><input type="submit" value="Save" class="button" /></td>
Actually each of my rows have an Ajax form so when click on edit I want to replace the row with another as you see, but when I add the Edit, all of my page destroyed and just _EditPhoneRow shown like I select all page for updateTrget where is the problem? and what is your suggestion to change all the specific row like this?
According to the HTML specification forms cannot be nested. This produces invalid HTML and depending on the user agent either the outer or the inner <form> simply won't work. That's a limitation of the HTML specification, don't be confused with ASP.NET MVC, it has nothing to do with it. One possibility is to replace your Ajax.BeginForm with an Ajax.ActionLink:
<tr class="gradeA odd" id="TRTarget">
#Ajax.ActionLink(
"Submit",
"EditPhone",
new { id = item.Id.Value },
new AjaxOptions { UpdateTargetId = "TRTarget" }
)
</tr>
UPDATE:
After you have updated your question and explained the symptoms I think you might have forgotten to reference the jquery.unobtrusive-ajax.min.js script to your page:
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.unobtrusive-ajax.min.js")"></script>
If you don't include this script the Ajax.* helpers such as Ajax.BeginForm and Ajax.ActionLink will be simple HTML forms and anchors. No AJAX at all. It is this script that reads the HTML5 data-* attributes emitted by those helpers and unobtrusively AJAXifies them.
I have an Entity (QuoteSheet) that contains a child entity (QuoteTask), which is loaded using the EntityFramework. However, I am receiving an error when I submit this form.
I have created an edit page for the QuoteSheet entity, which then uses an EditorTemplate to edit the QuoteTask child entity.
The controller code is as follows:
public ActionResult TestEdit(int Id)
{
var quote = DataContext.QuoteSheets.Where(x => x.ID == Id).FirstOrDefault();
return View(quote);
}
[HttpPost]
public ActionResult TestEdit(Models.QuoteSheet quote)
{
return View(quote);
}
A stripped down version of the view is as follows:
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.HiddenFor(x => x.JobID);
<div class="sectionHeader">Sheet Details</div>
<div class="sectionContent">
<table>
<tr>
<td width="150">Sheet Desc.</td><td>#Html.TextBoxFor(x => x.Description, new { size = "50" })</td>
</tr>
<tr>
<td>Quantity Required</td><td>#Html.EditorFor(x => x.Quantity)</td>
</tr>
</table>
</div>
<div class="sectionHeader">Tasks</div>
<div class="sectionContent">
<table id="Tasks">
<tr>
<th>Labour Group</th>
<th>Task Description</th>
<th>Total Hrs</th>
<th>Rate</th>
<th>Cost</th>
</tr>
#Html.EditorFor(x => x.QuoteTasks)
</table>
<input type="button" name="AddTasks" id="AddTasks" value="Add" />
</div>
<input type="submit" value="Submit" />
#Html.ValidationSummary()
}
And the EditorTemplate is:
#model Ornavi.Models.QuoteTask
<tr>
<td>#Html.EditorFor(x => Model.LabourGroup)</td>
<td>#Html.EditorFor(x => Model.Description)</td>
<td>#Html.EditorFor(x => Model.TotalHours)</td>
<td>#Html.EditorFor(x => Model.Rate)</td>
<td>#Html.HiddenFor(x => Model.ID)</td>
</tr>
When I submit the form, I am getting the following error:
The EntityCollection has already been initialized. The InitializeRelatedCollection method should only be called to initialize a new EntityCollection during deserialization of an object graph.
This only occurs when I use the EditorTemplate - if I remove the editor template and just submit the main entity, it works fine.
I have placed a breakpoint in the [httppost] TestEdit function, but the exception occurs before it reaches this point.
Any ideas on how to successfully use an EditorTemplate to edit a child entity?
The problem is, that the default modelbinder tries to instantiate your EF class and set the navigation properties when binding the form data to your parameter types.
See some similar questions like this one.
You have two options:
Don't use your EF classes as viewmodels but create own viewmodel classes to pass the data between controller and view.
Don't bind directly to the EF class in your Edit controller action but use a FormCollection parameter and bind yourself with UpdateModel as shown in the linked question.
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...
}
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.