I have a Model that has a properties as name, email... etc. Also has a Collection.
In the View I edit the properties and I kept the Collection UNchanged.
When I try to submit the form, it sends the changed and unchaged values, BUT the Collection is lost. How to handle this?
HTML.HiddenFor() - doesn't work.
It loses all proprties that are not changed in the form !(In the form I use HTML.EditorFor(Model => Model.Name))
Edit:
View:
<% using (Html.BeginForm("Action", "Controller", FormMethod.Post))
{%>
<%: Html.AntiForgeryToken()%>
<table>
<tr>
<td>Name: </td>
<td>
<%: Html.EditorFor(Model => Model.Name) %></td>
</tr>
<tr>
<td>Phone: </td>
<td>
<%: Html.EditorFor(Model => Model.Phone) %></td>
</tr>
#h#</table>
<%} %>
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SaveModelDealer(User ModelReceived)
{
try
{
if (ModelState.IsValid)
{
using (Context db = new Context ())
{
System.Diagnostics.Debug.WriteLine(" " + ModelReceived.ListOfPhones.Count);
}
return View();
}
else
{
}
}
catch (Exception)
{
}
}
Only properties inside a form are send back to the server with a post. You should re-populate the collection after the post the same way you did in the HttpGet action.
If you want to persist items in the form, but not show them, you can use a hidden field. Like this:
#Html.HiddenFor(m => m.ID);
Also, check out this question, I gave an example to solve your problem.
Related
From the menu page a user clicks an "Add to Order" button. The "Add to Order" button triggers the addToOrder function. That function works correctly, the drink is passed and an order is created with the drink information. After clicking the "Add to Order" button the user is sent to a confirmation page where their drink order is displayed and takes in their name in a form. The information passes correctly to the confirmation page, however when the form on the confirmation page is submitted the drink information is not passed but the rest of the form is. Any suggestions on how to get the drink information to pass in the object is greatly appreciated!
In the Home Controller:
From the menu page, this gets the drink information and works correctly:
[HttpGet]
public ActionResult addToOrder(int id)
{
Drink drinkToAdd = _drinkRepository.GetDrink(id);
List<Drink> dList = new List<Drink>();
dList.Add(drinkToAdd);
Order order = new Order();
order.drinkList = dList;
order.total = drinkToAdd.drinkPrice;
ViewData.Model = order;
return View("OrderConfirmation");
}
This method gets the information from the form in the order confirmation page:
[HttpGet]
public ActionResult confirmedOrder(Order order, int drinkID)
{
Drink drinkToAdd = _drinkRepository.GetDrink(drinkID);
List<Drink> dList = new List<Drink>();
dList.Add(drinkToAdd);
order.drinkList = dList;
_orderRepository.Add(order);
return View("ThankYou");
}
This is the order confirmation page:
#model BartenderApp.Models.Order
#{
ViewData["Title"] = "Order Confirmation";
}
<html>
<body>
<h1>Order Confirmation</h1>
<form asp-action="confirmedOrder" method="get" >
<p>#Html.DisplayNameFor(m => m.FirstName): #Html.TextBoxFor(m => m.FirstName)</p>
<p>#Html.DisplayNameFor(m => m.LastName): #Html.TextBoxFor(m => m.LastName)</p>
<table class="table">
<thead>
#foreach (var item in Model.drinkList)
{
<tr>
<td>
#Html.HiddenFor(modelItem => item.id)
</td>
<td>
<b>#Html.DisplayNameFor(modelItem => item.drinkName)</b>
</td>
<td>
<b> #Html.DisplayNameFor(modelItem => item.drinkDescription)</b>
</td>
<td>
<b> #Html.DisplayNameFor(modelItem => item.drinkPrice)</b>
</td>
</tr>
}
</thead>
<tbody>
#foreach (var item in Model.drinkList)
{
<tr>
<td>
#Html.HiddenFor(modelItem => item.id, new { drinkID = item.id } )
</td>
<td>
#Html.DisplayFor(modelItem => item.drinkName)
</td>
<td>
#Html.DisplayFor(modelItem => item.drinkDescription)
</td>
<td>
#Html.DisplayFor(modelItem => item.drinkPrice)
</td>>
</tr>
}
</tbody>
</table>
<p style="float:left">#Html.ActionLink("Modify Order", "Menu")</p>
<p style="float:right"><b>#Html.DisplayNameFor(m => m.total):</b> $#Html.DisplayFor(m => m.total)</p>
<input type="submit" value="Checkout" name="checkout" style="float:right" />
</form>
</body>
</html>
When I hit the submit button on the form I thought the entire object would pass but only the first and last name are. I have tried to then pass the specific drinkID so I could locate it in the repository and add it to the order. However, the drinkID is not passing either (always 0). Why is the drink information not passing with the rest of the object?
Thanks
This is the URL after I hit the submit button on the order confirmation page:
https://localhost:44378/Home/confirmedOrder?FirstName=John&LastName=Jones&item.id=1&item.id=1&checkout=Checkout
In this example, I did add the drink with the ID of 1 to my order. There is also another order in the repository, so the ID of the order is 2.
I am sorry if I ask a newbie question but i am new to asp.net mvc.
So, I have this view :
#model FirstProject.Models.SelectRolesViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("SelectRolesOverall","SelectRoles"))
{
<table class="table">
<tr>
<th>Users</th>
<th>Roles</th>
</tr>
#Html.EditorFor(model=>model.UsersAndRoles)
<tr>
<td></td>
<td>
<input type="submit" />
</td>
</tr>
</table>
}
And the EditorTemplate :
#model FirstProject.Models.UserRole
<tr>
<td>#Model.User.UserName</td>
<td>
#Html.RadioButtonFor(model => model.Role, "Applicant") Applicant
<br/>
#Html.RadioButtonFor(model => model.Role, "Professor") Professor
</td>
</tr>
My question is next : How do I see what radio buttons have been checked in my controller after the submit button has been pressed? I want to have the following logic : If Applicant have been selected then userRole is Applicant , else if Professor has been selected then userrole is Professor. My controller is Empty momentarily because I don't know what to write in it.
If your action method is
public SelectRolesOverall(SelectRolesViewModel model)
then you can access the collection with
IEnumerable<UsersAndRoles> usesAndRoles = model.UsersAndRoles;
and access each item in the collection
foreach (UserRole userRole in model.UsersAndRoles)
{
string role = userRole.Role;
string name = userRole.UserName; // see note below
}
Note you have not included an input for the property UserName so this value wont post back and you might have trouble matching the role to the user. You might want to add #Html.HiddenFor(m => m.UserName) or change <td>#Model.User.UserName</td> to <td>#Html.TextBoxFor(m => m.UserName, new { #readonly = "readonly" })</td>
Try this
public ActionResult [YourActionName](string role)
{
switch(role){
case "Applicant": /*your applicant logic*/
break;
case "Professor": /*your Professor logic*/
break;
/*
Other logic here
*/
}
Replace the action name by your own
Note that the parameter role should have the same name of the radio button in your view, this is to allow data binding to work
how can i get the querystring id in there? is there any way
#using (Html.BeginForm("InformVendor", "Procurement",new {id="querystring Mode = "Inform" }, FormMethod.Post))
{
<tr>
<td>
#Html.DropDownListFor(m=>m.VendorId,new MultiSelectList(Model.VendorDropdownlist, "CustomerId", "ContactName"))
</td>
<td>
#Html.CheckBoxFor(m => m.IsEmail)
</td>
</tr>
<tr>
<td>
<input type="submit" id="btnsubmit" value="Nominate Vendor" />
</td>
<td></td>
</tr>
}
The easiest way is to have your id field be hidden. This way the user doesn't see the ID field but your post back controller does.
#Html.HiddenFor(x => x.YourID)
If you add the Id to your view model and render it as a hidden field.
#Html.HiddenFor(m => m.Id)
You will be able to retrieve it like this instead of using the querystring.
public ActionResult InformVendor(AViewModel model)
{
var Id = model.Id;
}
#foreach (var item in Model.AllManagementActions)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
I want to pass all these values to my controller. The ID , the name, etc.
Can't i just pass the item itself?
This is my current code, but it doesn't work.
<a class="complete" title="My Action" data-url="#Url.Action("Submit", "Period", new { tpId = Model.Id ,it = item})" style="cursor: pointer;">
It works if I pass item.name, item.id,item.number,etc
Can i pass the model ?
Don't iterate in your views - use editor templates for the type.
Have a '~/shared/EditorTemplates/ManagementAction.ascx' (or .cshtml if using razor) that renders a single ManagementAction.
In the view, instead of iterating use:
#Html.EditorFor(model => model.AllManagementActions)
yes you can pass the entire model, but you should use a submit button instead of of an anchor tag and also put your code inside a
using(Html.BeginForm)
{
#foreach (var item in Model.AllManagementActions)
{
//your desire.
}
<input type="submit" value="Save" />
}
in your controller you will recieve the model on post
[HttpPost]
public ActionResult SaveViewData(YourModel model)
{
//your logic.
}
This question already has an answer here:
How to pass IEnumerable list to controller in MVC including checkbox state?
(1 answer)
Closed 6 years ago.
All, please clear up my confusion on how the model binding works with IEnumerables and Editor Templates.
I have a view, Approve.cshtml
#model IEnumerable<MvcWebsite.Models.Approve>
<table>
<tr>
<th>
Name
</th>
</tr>
#Html.EditorForModel()
</table>
A model, Approve.cs
public class Approve
{
public string Name { get;set;}
public string Role { get; set; }
}
And an editor template
#model MvcWebsite.Models.Approve
#using (Html.BeginForm("Approve", "Registration", FormMethod.Post))
{
<tr>
<td>
#Html.HiddenFor(m => m.Name)
#Html.EditorFor(m => m.Role)
</td>
<td>
<input type="submit" value="Approve" class="submit-button" />
</td>
</tr>
}
This is all fine and good. It renders the following output.
<input name="[0].Name" type="hidden" value="" />
....
However, in my Controller, I can't seem to receive values back for the Model (binding).
[HttpPost]
public ActionResult Approve(Approve approveModel)
{
.... approveModel has all default values
}
Can someone shed light on what I am doing wrong here? I abbreviated the code, I am using the Editor Template with other EditorFor and HiddenFor fields from my model...
Edited: I basically have a table layout, each with the User's Name, a textbox where I can enter their role (User or Admin) and then an Approve button which submits to my controller. Hence the reason I want to only return a single Approve object. I can return the entire IEnumerable to my Controller, but if I do that, how can I tell which of the items was the one I clicked the Approve button (submit) for?
EDIT:
So I have modified the code so that I have a single form surrounding my entire View Approve.cshtml
#model IEnumerable<MvcWebsite.Models.Approve>
#using (Html.BeginForm("Approve", "Program", FormMethod.Post))
{
<table>
<tr>
<th>
Name
</th>
</tr>
#Html.EditorForModel()
</table>
}
And then changed the controller to
[HttpPost]
public ActionResult Approve(IEnumerable<Approve> approvals)
{
// ???????????????????????
}
Now I'm still not clear on how to know which row I clicked Approve for. I know there are other ways to accomplish this task (create a checkbox for approve, and approve anything checked, etc.) However, I need the ability to click a button and only save 1 row back to the database, regardless if the user entered information into the other rows. Is it better practice to wrap my IEnumerable inside of it's own model (i.e. AllApprovals) and then add helper properties to that parent model (SelectedIndex, etc.)? If that is the approach to take, then how do I set the SelectedIndex after clicking an Approve button? Is that still jquery magic or is there a correct MVC way to accomplish this? Jquery magic seems very hackish to me?
EDIT: Based on Brian's response, here is my final. Still doesn't feel quite right, but it works!
View
#model IEnumerable<MvcWebsite.Models.Approve>
<table>
<tr>
<th>
Name
</th>
</tr>
#Html.EditorForModel()
</table>
Editor Template
#using (Html.BeginForm("Approve", "Registration", FormMethod.Post))
{
<tr>
<td>
#Html.HiddenFor(m => m.Name)
#Html.EditorFor(m => m.Role)
</td>
<td>
<input type="submit" value="Approve" class="submit-button" />
</td>
</tr>
}
Controller
[HttpPost]
public ActionResult Approve([Bind(Prefix="approval")]Approve approval) {
// WORKS!
}
Since you are only changing one at a time, I think the following is easier than trying to figure out at the controller which values changed, or adding a changed property and setting it via javascript.
Change Approve.cshtml to
#model IEnumerable<MvcWebsite.Models.Approve>
<table>
<tr>
<th colspan=2>
Name
</th>
</tr>
#foreach(var user in Model){
#using (Html.BeginForm("Approve", "Registration", FormMethod.Post)) {
<tr>
<td>
#Html.EditorFor(m => user)
</td>
<td>
<input type="submit" value="Approve" class="submit-button" />
</td>
</tr>
}
}
</table>
Change the Approve Editor Template to
#Html.HiddenFor(m => m.Name)
#Html.EditorFor(m => m.Role)
You're binding to the single Approve class, you should bind to IEnumerable<Approve>
Martin is correct I just want to add some more information. Your rendered HTML with the [0] is special syntax the model binder looks at and assumes you are working with a list if objects. Since your action method has a single Approve class and not a kist, you are experiencing this problem.