问题
I have a project to make an online shop between users (post a product, buy, etc.) using a database. In this project I have a view called "ShoppingCart":
@model IEnumerable<MyFirstProject.Models.Product>
@{
ViewBag.Title = "ShoppingCart";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Your Shopping Cart</h2>
@if (Model == null)
{
<div style="float:left">Your cart is empty.</div>
<div>
Total payment: 0
</div>
}
else
{
decimal tPrice = 0;
<div>
<table style="float:left">
@foreach (var product in Model)
{
tPrice = tPrice + product.Price;
{ Html.RenderPartial("ProductLine", product);}
}
</table>
</div>
<div>
Total payment: @tPrice
</div>
}
It receives a list of products which the user decided to buy and displays them (not the important part). I need to add a button which will send the list to an action result in the "ShoppingController":
[HttpPost]
public ActionResult ShoppingCart(List<Product> bought)
{
if (ModelState.IsValid)
{
foreach (var listP in bought.ToList())
{
foreach (var databaseP in db.Products.ToList())
{
if (listP.ProductID == databaseP.ProductID)
{
databaseP.State = 1;
db.SaveChanges();
break;
}
}
}
return RedirectToAction("Index");
}
else
{
return View(bought);
}
}
"State" indicates if the product was bought or not (0=not bought, 1=bought), db is the database
回答1:
If you wan't to post any data from a view to an action method, you should keep that data in form elements and keep that in a form. Since you want to post a collection of items, You may use Editor Templates.
Let's start by creating a view model.
public class ShoppingCartViewModel
{
public decimal TotalPrice { set; get; }
public List<Product> CartItems { set; get; }
}
public class Product
{
public int Id { set; get; }
public string Name { set; get; }
}
Now in your GET action, you will create an object of the ShoppingCartViewModel
, load the CartItems
property and send to the view.
public ActionResult Index()
{
var cart = new ShoppingCartViewModel
{
CartItems = new List<Product>
{
new Product { Id = 1, Name = "Iphone" },
new Product { Id = 3, Name = "MacBookPro" }
},
TotalPrice = 3234.95
};
return View(cart);
}
Now i will create an EditorTemplate. To do that, Go to your ~/Views/YourControllerName
folder, and Create a directory called EditorTemplates
and add a view with name Product.cshtml
The name of the file should match with the name of the type.
Open this new view and add the below code.
@model YourNamespace.Product
<div>
<h4>@Model.Name</h4>
@Html.HiddenFor(s=>s.Id)
</div>
You can keep the display however you want. But the important thing is, We need to keep a form field for the productId. We are keeping that in a hidden field here.
Now let's go back to our main view. We need to make this view strongly typed to our ShoppingCartViewModel
. We will use the EditorFor
html helper method in this view to call our editor template
@model ReplaceYourNamespaceHere.ShoppingCartViewModel
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.CartItems)
<p>Total : @Model.TotalPrice</p>
<input type="submit" />
}
And in your HttpPost action method, We will have a paramer of type ShoppingCartViewModel
. When the form is submitted, MVC Model binder will map the posted form values to an object of ShoppingCartViewModel
.
[HttpPost]
public ActionResult Index(ShoppingCartViewModel model)
{
foreach (var item in model.CartItems)
{
var productId = item.Id;
// to do : Use productId and do something
}
return RedirectToAction("OrderSucessful");
}
You can iterate through the CartItems
collection and get the Id of the Products and do whatever you want.
If you wan't to allow the user to edit the items (using a check box) in this page, Take a look at this answer. It is basically same, but you add a boolean property to Product class and use that for rendering a checkbox.
来源:https://stackoverflow.com/questions/34364990/calling-httppost-actionresult-from-inside-a-view-using-a-button