Model :
public IEnumerable<Role> Roles { get; set; }
Index :
Roles = securityServiceClient.GetAllRoles()
View :
#foreach (var role in Model.Roles)
{
<tr>
#if (role.Name == "User")
{
<td><input type="checkbox" checked="checked"/></td>
}
else
{
<td><input type="checkbox"/></td>
}
<td>#Html.Label(role.Name)</td>
</tr>
}
[HttpPost]
CreateSomething :
How can I get the selected checkbox(s) from the view?
You must give your checkboxes names:
#if (role.Name == "User")
{
<td><input type="checkbox" checked="checked" name="roles"/></td>
}
else
{
<td><input type="checkbox" name="roles"/></td>
}
and then:
[HttpPost]
public ActionResult Index(IEnumerable<string> roles)
{
...
}
Also you should be using view models, strongly typed views and helpers and not hardcode checkboxes as you did in your veiws.
Here's what I mean:
Model:
public class MyViewModel
{
public RoleViewModel[] Roles { get; set; }
}
public class RoleViewModel
{
public string RoleName { get; set; }
public bool IsSelected { get; set; }
}
and then:
public class HomeController: Controller
{
public ActionResult Index()
{
var roles = securityServiceClient.GetAllRoles().Select(r => new RoleViewModel
{
RoleName = r.Name
});
var model = new MyViewModel
{
Roles = roles
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<RolesViewModel> roles)
{
...
}
}
and in the view:
#model MyViewModel
#using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>role</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Roles.Length; i++)
{
<tr>
<td>
#Html.CheckBoxFor(x => x.Roles[i].IsSelected)
#Html.LabelFor(x => x.Roles[i].IsSelected, Model.Roles[i].RoleName)
#Html.HiddenFor(x => x.Roles[i].RoleName)
</td>
</tr>
}
</tbody>
</table>
}
Related
I'm trying to make a simple shopping cart app in Asp.Net Core 2.0 MVC. I'm not doing any Ajax-ing. I have three models:
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
}
public class Cart
{
public int Id { get; set; }
public int CartItemId { get; set; }
public int CustomerId { get; set; } // not in use yet
}
public class CartItem
{
public int Id { get; set; }
public int ProductId { get; set; }
public int NumEach { get; set; } // not in use yet
}
From either one of the two views below, I want to update Cart and CartItem, and then get redirected back to the view where I clicked the Add to cart-button:
1) Index-view:
#model IEnumerable<simpleShop.Models.Product>
<table class="table">
<thead>
<tr>
<th>#Html.DisplayNameFor(model => model.Title)</th>
<th>#Html.DisplayNameFor(model => model.Info)</th>
<th>#Html.DisplayNameFor(model => model.Price)</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
<a asp-action="Details" asp-route-id="#item.Id">
#Html.DisplayFor(modelItem => item.Title)
</a>
</td>
<td>#Html.DisplayFor(modelItem => item.Info)</td>
<td>#Html.DisplayFor(modelItem => item.Price)</td>
<td>
<form asp-action="AddToCart">
<button type="submit" value="#item.Id">Add to cart</button>
</form>
</td>
</tr>
}
</tbody>
</table>
2) Details-view:
#model simpleShop.Models.Product
#{
ViewData["Title"] = Html.DisplayFor(model => model.Title);
}
<h2>#Html.DisplayFor(model => model.Title)</h2>
<h4>#Html.DisplayFor(model => model.Info)</h4>
<h1>#Html.DisplayFor(model => model.Price)</h1>
<form asp-action="AddToCart">
<input type="hidden" asp-for="Id" />
<p>
<input type="submit" value="Add to cart" />
</p>
</form>
<div>
<a asp-action="Index">Return to list</a>
</div>
Below is my faulty AddToCart-method in the home controller, which at the moment certainly isn't doing anything to save data to the Cart or CartItem tables. How can I get it to?
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddToCart([Bind("Id")] Product product)
{
if (ModelState.IsValid)
{
_context.Add(product);
await _context.SaveChangesAsync();
if (product.Id > 0) // added to cart via the details-view
{
return RedirectToAction("Details", "Home", product.Id);
}
else // added to cart via the index-view
{
return RedirectToAction(nameof(Index));
}
}
return View(product);
}
for one thing Bind() requires way more than just "id", remember what ever you wanted to bind has to include all properties in question of the object you wanted to pass to new action.
reference comments in line -->>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddToCart([Bind("Id")] Product product)
{
if (ModelState.IsValid)
{
_context.Add(product); //**BAD this will probably try to
// add product to the product table again.**
// With error to follow about already exists
// exception at the SaveChangesAsync call.
await _context.SaveChangesAsync();
if (product.Id > 0)
{
return RedirectToAction("Details", "Home", product.Id);
}
else // added to cart via the index-view
{
return RedirectToAction(nameof(Index));
}
}
return View(product);
}
My suggestion goes along these lines where you will have ProductId and Qty of the Product (your NumEach)
[HttpPost]
[ValidateAntiForgeryToken]
public asyync Task<IActionResult> AddToCart([Bind("ProductId", "NumEach", "CartId")] CartItem model, string ReturnUrl){
if(ModelState.IsValid){
_context.CartItem.Add(model);
await _context.SaveChangesAsync();
//model.Id greater than 0 indicates a save occurred.
if(model.Id > 0)
return RedirectToAction(ReturnUrl); // forced return for further shopping?
else
return RedirectToAction("Index");
}
//this assumes bad model state with no error unless model is
//annotated accordingly.
return View(model);
}
problem with Cart though is right now you can only store 1 item...
public class Cart{
public Cart(){}
public int Id {get;set;} //cart's integer id
public List<CartItem> CartItems {get;set;} //collection of cartitems
//FK
public int CustomerId {get;set;} //customer's id
//NAV
public Customer CustomerId {get;set;} //customer nav
...
}
public class CartItem{
public CartItem(){}
public int Id {get;set;} //cart item id
public int ProductId {get;set;} //productid
public Product Product {get;set;} //nav property
public int NumEach {get;set;} //quantity of each product
//FK
public int CartId {get;set;} //Foreign Key
//NAV
public Cart Cart {get;set;} //nav property
}
One other thing your index view wouldn't just have the model be a collection of Products it would probably be a viewmdodel that had a collection of products in it but it would also contain the cartid that would have been generated with the visitor. That CartId would follow that visitor until the transaction was completed and you then have an OrderId or it dies once the session is closed.
#model simpleShop.Models.IndexViewModel
<table class="table">
<thead>
<tr>
<th>#Html.DisplayNameFor(model => model.Title)</th>
<th>#Html.DisplayNameFor(model => model.Info)</th>
<th>#Html.DisplayNameFor(model => model.Price)</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Products)
{
<tr>
<td>
<a asp-action="Details" asp-route-id="#item.Id">
#Html.DisplayFor(modelItem => item.Title)
</a>
</td>
<td>#Html.DisplayFor(modelItem => item.Info)</td>
<td>#Html.DisplayFor(modelItem => item.Price)</td>
<td>
<form asp-action="AddToCart">
<input type="Hidden" asp-for="#Model.CartId" />
<button type="submit" value="#item.Id">Add to cart</button>
</form>
</td>
</tr>
}
</tbody>
</table>
public class IndexViewModel
{
public IndexViewModel(){}
public int CartId{get;set;}
public List<Product> Products{get;set;}
}
I think you get the idea from there.
ModelState.IsValid is always returning true.
Code:
user.cs
public class User
{
public int UserID { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
HomeController.cs
public ActionResult SaveUser(User user)
{
if (ModelState.IsValid)
{
//create DBContext object
using (var dbCtx = new UsersDbEntities())
{
dbCtx.Entry(user).State = EntityState.Modified;
dbCtx.SaveChanges();
}
return View("ShowUser", user);
}
return View("EditUser", user);
}
Register.cshtml
#model TrainingWebsite.Models.User
<div id="myForm">
#using (Html.BeginForm("RegisterUser", "Home", FormMethod.Post))
{
if (#ViewBag.Message != null)
{
<div style="border: 1px solid red">
#ViewBag.Message
</div>
}
<table>
<tr>
<td>#Html.LabelFor(a => a.Username)</td>
<td>#Html.TextBoxFor(a => a.Username, new { id = "id_username" })</td>
<td>#Html.ValidationMessageFor(a => a.Username)</td>
</tr>
<tr>
<td>#Html.LabelFor(a => a.FirstName)</td>
<td>#Html.TextBoxFor(a => a.FirstName, new { id = "id_firstName" })</td>
<td>#Html.ValidationMessageFor(a => a.FirstName)</td>
</tr>
<tr>
<td>#Html.LabelFor(a => a.LastName)</td>
<td>#Html.TextBoxFor(a => a.LastName, new { id = "id_lastName" })</td>
<td>#Html.ValidationMessageFor(a => a.LastName)</td>
</tr>
</table>
ModelState.IsValid is true even though Last Name field is empty yet is a required field
Any help would be appreciated. Thanks in advance
David
MVC4 How to get the data in partial view when parent view click submit
I create a edit page for user update the data, the parent view show some of book details and partial view is a loop show the status of each book. Also, the parent view have a submit button for update both of partial view and parent view, but when I click the submit only parent view data can update and partial view still not update. The code below:
Model:
public class LibraryInventory
{
public decimal LibraryID { get; set; }
public string Title { get; set; }
public List<LibraryItem> Entities { get; set; }
}
public class LibraryItem
{ public decimal StatusID { get; set; }
public string Location { get; set; }
public decimal BorrowedBy { get; set; }
}
Controller :
public ActionResult EditRecord(string ID)
{
DataTable dt = (DataTable)Session["EditGridData"];
LibraryInventory record = new LibraryInventory(dt.Rows[0]);
dt = LibraryEditBLL.GetEditItems(ID);
if (results.ToList().Count > 0)
{
record.Entities = LibraryItem.ConvertToLibraryEntity(dt).ToList();
}
return View(record);
}
[HttpPost]
public ActionResult EditRecord(string FormButton, LibraryInventory model)
{
switch (FormButton)
{
case "Submit":
if (ModelState.IsValid)
{
LibraryEditBLL.UpdateInventoryLibrary(model);
}
return View("EditRecord",record);
default:
return RedirectToAction("Edit"); //other page not need check
}
View :
#model XXX.Models.LibraryModels.LibraryInventory
#using (Html.BeginForm("EditRecord", "Library", FormMethod.Post, new { enctype = "multipart/form-data" })){
#Html.HiddenFor(m =>m.LibraryID)
<table>
<tr>
<td>#Html.TextBoxFor(m => m.Title)</td>
#for (var i = 0; i < #Model.Entities.Count; i++)
{
#Html.Partial("EditItem", #Model.Entities[i])
}
</tr>
</table>
}
#model XXX.Models.LibraryModels.LibraryItem
<tr>
<td> #Html.DropDownListFor(m => m.StatusID) </td>
<td> #Html.DropDownListFor(m => m.Location)</td>
<td> #Html.DropDownListFor(m => m.BorrowedBy) </td>
</tr>
Try this code for your view. And no need of partialview for this simple scenario.
#model XXX.Models.LibraryModels.LibraryInventory
#using (Html.BeginForm("EditRecord", "Library", FormMethod.Post, new { enctype = "multipart/form-data" })){
#Html.HiddenFor(m =>m.LibraryID)
<table>
<tr>
<td>#Html.TextBoxFor(m => m.Title)</td>
</tr>
#for (var i = 0; i < Model.Entities.Count; i++)
{
<tr>
<td> #Html.DropDownListFor(m => Model.Entities[i].StatusID) </td>
<td> #Html.DropDownListFor(m => Model.Entities[i].Location)</td>
<td> #Html.DropDownListFor(m => Model.Entities[i].BorrowedBy) </td>
</tr>
}
</table>
}
I am having issues with a view model that constantly return null properties after a post. Below is my code (it could be a syntax issue or two calling a class or property the same name as i saw in other posts but i could not see any such issue in code):
VIEW MODEL:
public class ProductItem
{
public int ProductID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string LongDescription { get; set; }
public int SupplierID { get; set; }
public string Dimensions { get; set; }
public double Price { get; set; }
public bool On_Sale { get; set; }
public double DiscountedPrice { get; set; }
public string Thumbnail { get; set; }
public string LargeImage { get; set; }
public string LargeImage2 { get; set; }
public string LargeImage3 { get; set; }
public string CrossRef { get; set; }
public byte Available { get; set; }
public double Weight { get; set; }
public byte Important { get; set; }
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
// this is required on the page to allow products to be marked for deletion
public bool IsForDelete { get; set; }
}
public class ProductListViewModel
{
public IEnumerable<ProductItem> ProductItems { get; set; }
public IEnumerable<Category> CategoryItems { get; set; }
}
CONTROLLER:
public ActionResult ProductList()
{
var productList = new ProductListViewModel();
productList.ProductItems = productRepository.GetProductsWithDeleteOption().ToList();
productList.CategoryItems = categoryRepository.GetCategories().ToList();
return View(productList);
}
[HttpPost]
public ActionResult ProductList(ProductListViewModel productViewModel, FormCollection formCollection, string submit)
{
if (ModelState.IsValid)
{
// Check for submit action
if (submit == "Change Sort")
{
if (formCollection["Sortby"] == "ProductID")
{
OrderBy(productViewModel, formCollection, "m.ProductID");
}
else if (formCollection["Sortby"] == "Code")
{
OrderBy(productViewModel, formCollection, "m.Code");
}
else if (formCollection["Sortby"] == "Name")
{
OrderBy(productViewModel, formCollection, "m.Name");
}
else if (formCollection["Sortby"] == "Price")
{
OrderBy(productViewModel, formCollection, "m.Price");
}
}
else if (submit == "Delete all selected")
{
}
else if (submit == "Update All")
{
}
else if (submit == "Restrict Display")
{
}
}
return View(productViewModel);
}
VIEW:
#model Admin.Models.ViewModels.ProductListViewModel
#{
ViewBag.Title = "View Products";
}
#using (Html.BeginForm())
{
<h2>Product List as at #DateTime.Now.ToString("dd/MM/yyyy")</h2>
<table>
<tr>
<td>Sort by:</td>
<td>
<select name="Sortby">
<option value="ProductID">ProductID</option>
<option value="Code">Code</option>
<option value="Name">Name</option>
<option value="Price">Price</option>
</select>
</td>
<td>
<input type="radio" name="sortDirection" checked="checked" value="Asc" /> Ascending
<input type="radio" name="sortDirection" value="Desc" /> Descending
</td>
<td>
<input type="submit" name="submit" value="Change Sort" />
</td>
</tr>
<tr>
<td>Display only : (category)</td>
<td>#Html.DropDownList("CategoryID", new SelectList(Model.CategoryItems, "CategoryID", "Name"), "All Categories")</td>
<td colspan="2"><input type="submit" name="submit" value="Restrict Display" /></td>
</tr>
<tr>
<td colspan="4"><br />Total Number of products: #Model.ProductItems.Count()</td>
</tr>
</table>
<table>
<tr>
<th>
Edit
</th>
<th>
Code
</th>
<th>
Name
</th>
<th>
Price
</th>
<th>
On_Sale
</th>
<th>
DiscountedPrice
</th>
<th>
Weight
</th>
<th>
Delete
</th>
<th></th>
</tr>
#for (var i = 0; i < Model.ProductItems.ToList().Count; i++)
{
<tr>
<td>
#Html.HiddenFor(m => m.ProductItems.ToList()[i].ProductID)
#Html.ActionLink(Model.ProductItems.ToList()[i].ProductID.ToString(), "ProductEdit", new { id = Model.ProductItems.ToList()[i].ProductID })
</td>
<td>
#Html.DisplayFor(m => m.ProductItems.ToList()[i].Code)
</td>
<td>
#Html.DisplayFor(m => m.ProductItems.ToList()[i].Name)
</td>
<td>
#Html.EditorFor(m => m.ProductItems.ToList()[i].Price)
</td>
<td>
#Html.CheckBoxFor(m => m.ProductItems.ToList()[i].On_Sale, new { id = "On_Sale_" + Model.ProductItems.ToList()[i].ProductID })
</td>
<td>
#Html.EditorFor(m => m.ProductItems.ToList()[i].DiscountedPrice)
</td>
<td>
#Html.EditorFor(m => m.ProductItems.ToList()[i].Weight)
</td>
<td>
#Html.CheckBoxFor(m => m.ProductItems.ToList()[i].IsForDelete, new { id = Model.ProductItems.ToList()[i].ProductID })
</td>
<td>
#Html.ActionLink("Edit", "ProductEdit", new { id = Model.ProductItems.ToList()[i].ProductID }) |
#Html.ActionLink("Details", "Details", new { id = Model.ProductItems.ToList()[i].ProductID }) |
#Html.ActionLink("Delete", "Delete", new { id = Model.ProductItems.ToList()[i].ProductID })
</td>
</tr>
}
</table>
<p>
<input name="submit" type="submit" value="Delete all selected" />
</p>
<p>
<input name="submit" type="submit" value="Update All" />
</p>
<p>
#Html.ActionLink("Add a new product", "ProductAdd")
</p>
}
In the post action, the productViewModel argument has the ProductItems and CategoryItems properties as null.
Ok, so there are two problems.
I don't understand why you want to post list of the CategoryItems You should only expect the selected category and not the list
The problem with ProductItems is the name generated for <input> tags. Currently, the name being generated is name="[0].Price" whereas it should have been name="ProductItems[0].Price"
I changed the following code
#Html.EditorFor(m => m.ProductItems.ToList()[i].Price)
to
#Html.EditorFor(m => m.ProductItems[i].Price)
and it worked.
Note: I changed IEnumerable<ProductItem> ProductItems to List<ProductItem> ProductItems in ProductListViewModel
Yes it will be null on post back. I have a similar table in one of my projects and this is what I would have done given my situation.
You could change your view model to look like this then you don't have to do so many converting to lists in your view:
public class ProductListViewModel
{
public List<ProductItem> ProductItems { get; set; }
public List<Category> CategoryItems { get; set; }
}
Now in you view it could look something like this (this is just part of it, then rest you can just go and add):
#for (int i = 0; i < Model.ProductItems.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(m => m.ProductItems[i].Name)
#Html.HiddenFor(m => m.ProductItems[i].Name)
</td>
</tr>
<tr>
<td>
#Html.CheckBoxFor(m => m.ProductItems[i].IsForDelete)
</td>
</tr>
}
Add the code and do some debugging to see how the values are returned on submit
I hope this helps.
How to find the values of checkboxes(i.e., whether checked or not) from a list of dynamically created check boxes in razor view engine? the code runs as follows...
#foreach (var item in Model))
{
<tr>
<td class="Viewtd">
#Html.ActionLink(item.Title, "Edit", new { id = item.id})
</td>
<td>
#Html.CheckBox("ChkBox"+item.ThresholdID , false, new { id = item.id})
</td>
</tr>
}
How to get those check boxes values in the controller?
Do it the proper way: using view models and editor templates.
As always start by defining a view model:
public class MyViewModel
{
public string Title { get; set; }
public string Id { get; set; }
public bool IsThreshold { get; set; }
}
then a controller to populate this view model :
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new MyViewModel
{
Id = "1",
Title = "title 1",
IsThreshold = false,
},
new MyViewModel
{
Id = "2",
Title = "title 2",
IsThreshold = true,
},
new MyViewModel
{
Id = "3",
Title = "title 3",
IsThreshold = false,
},
};
return View(model);
}
[HttpPost]
public ActionResult Edit(MyViewModel model)
{
// This action will be responsible for editing a single row
// it will be passed the id of the model and the value of the checkbox
// So here you can process them and return some view
return Content("thanks for updating", "text/plain");
}
}
and then the Index view (~/Views/Home/Index.cshtml):
#model IEnumerable<MyViewModel>
<table>
<thead>
<tr>
<th></th>
<th>Threshold</th>
</tr>
</thead>
<tbody>
#Html.EditorForModel()
</tbody>
</table>
and finally the editor template (~/Views/Home/EditorTemplates/MyViewModel.cshtml):
#model MyViewModel
#{
ViewData.TemplateInfo.HtmlFieldPrefix = "";
}
<tr>
#using (Html.BeginForm("Edit", "Home"))
{
#Html.HiddenFor(x => x.Id)
#Html.HiddenFor(x => x.Title)
<td><input type="submit" value="#Model.Title" /></td>
<td>#Html.CheckBoxFor(x => x.IsThreshold)</td>
}
</tr>