How to bind a List of ViewModel in ASP MVC? - asp.net-mvc

Does anyone know how to get POST values for MODELVIEW Pattern below. I can display the MenuItem as Checkboxes and Radio buttons,
but when user submits the form i.e. POST, ModelViewTest is null. I'm expecting List of MenuItems that user have selected.
public class ModelViewTest
{
public IEnumerable<MenuItem> MenuItemList { get; set; } //Will be displayed as listboxes and checkboxes
public Restaurant restaurant {get;set;}
}
ACTIONS:
public ActionResult Edit()
{
//some code here
}
return View(new ModelViewTest());
}
[HttpPost]
public ActionResult Edit(ModelViewTest model)
{
//I'm not getting List of MenuItems
return View();
}
MenuItem Class:
public class MenuItem
{
public string MenuItemCode{get;set;}
public string MenuItemDescription{get;set;}
public string UIType {get;set;} //This determines whether it's radio or checkbox
public string UIGroupType {get;set;} //Determines the Group for radio/checkbox.
}
public class Restaurant
{
public string restaurantName{get;set;}
public MenuItem MenuItem{get;set;}
}
Update
Please see my View code snippet below:
<table>
#foreach (var menu in Model.MenuList)
{
if (menu.UIType == "Radio")
{
<tr>
<td align="left">
<input id="MenuCheckboxRadio" name="#Menus.UIGroup" value="#Menu.MenuItemCode" type="radio" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
else
{
<tr>
<td align="left">
<input id="MenuCheckbox" name="#Menus.UIGroup" value="#Menus.#MenuItem" type="checkbox" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
i++;
}
</table>

In order to get the list of menu items in the POST action you need their corresponding values must be included in the html <form> and because this is a collection follow the standard naming convention so that the default model binder can parse them.

First you should show us your view to know how you render your ViewModel.
However try this:
make partial view to be editor template for your MenyItem
<%# Control Inherits="ViewUserControl<MenyItem>" %>
<%: Html.TextBoxFor(m => m.MenuItemCode) %>
<%: Html.TextBoxFor(m => m.MenuItemDescription) %>
.......
then in your view make for loop NOT foreach:
<%# Page Inherits="ViewPage<ModelViewTest>" %>
<% using (Html.BeginForm()) {%>
<% for (int i = 0; i < 3; i++) { %>
<%: Html.EditorFor(m => m.MenuItemList[i]) %>
<% } %>
<% } %>
And please see this answer

Please see my View code snippet below:
<table>
#foreach (var menu in Model.MenuList)
{
if (menu.UIType == "Radio")
{
<tr>
<td align="left">
<input id="MenuCheckboxRadio" name="#Menus.UIGroup" value="#Menu.MenuItemCode" type="radio" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
else
{
<tr>
<td align="left">
<input id="MenuCheckbox" name="#Menus.UIGroup" value="#Menus.#MenuItem" type="checkbox" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
i++;
}
</table>

Related

How do I select an Id from a SelectList using asp.net core 3?

I'm iterating through a list and populating a table with the last column having a edit button to edit that Id specific request.
Right now no matter what button i click it always takes me to the edit page of the first Id in the list, also the url has each Id listed in it like this.
/EditRequest?SelectedId=127&SelectedId=128
why is the SelectedId set to all values in the list? and how do I only pass the one Id of the one selected?
Here's my model
public class MyRequestsViewModel
{
public MyRequestsViewModel()
{
this.MyRequests = new List<SelectListItem>();
}
public List<SelectListItem> MyRequests;
public int SelectedId { get; set; }
}
I'm iterating through MyRequests and want to send SelectedId to the controller
<form method="get" asp-controller="Home" asp-action="EditRequest">
<table id="SortRequestsTable" class="table table-striped">
<thead>
<tr>
<th>SortID</th>
<th>SortCriteria</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.MyRequests)
{
<tr>
<td>#item.Value</td>
<td>#item.Text</td>
<td>
<input asp-for="SelectedId" type="hidden" value="#item.Value" />
<button>#item.Value<span class="sap-icon"></span></button>
</td>
</tr>
}
</tbody>
</table>
</form>
And my controller keeps saying that SelectedId is 0
public IActionResult EditRequest(MyRequestsViewModel requests)
I got it working with this. But I don't want to display the Id value in the button.
<input asp-for="SelectedId" type="submit" value="#item.Value" /><span class="sap-icon icon-16"></span>
I've also tried using asp-route-SelectedId tag helper but I'm not entirely sure how to implement that.
Try code below to achieve Edit from Index Page.
<a asp-action="Edit" asp-route-id="#item.ID">Edit</a> |
And Controller for accept request
// GET: Tickets/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var ticket = await _context.Tickets.FindAsync(id);
if (ticket == null)
{
return NotFound();
}
return View(ticket);
}

Posted model is null

I'm not sure what's wrong since I'm very new with MVC. This is a shopping cart. The customer is able to review their cart and edit quantity.
On the HttpPost ViewCart method, the cart is always empty, and number of lines is zero.
Controller:
public ActionResult ViewCart() {
var cart = (CartViewModel)Session["Cart"];
return View(cart);
}
[HttpPost]
public ActionResult ViewCart(CartViewModel cart) {
Session["Cart"] = cart;
return RedirectToAction("Order", "Checkout");
}
View:
#model CartViewModel
using (Html.BeginForm()) {
<h2>Your cart</h2>
<table>
<thead> ... </thead>
<tbody>
#foreach (var item in Model.Lines) {
<tr>
<td>#Html.DisplayFor(modelItem => item.Article.Description)</td>
<td>#Html.EditorFor(modelItem => item.Quantity)</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="Checkout">
}
ViewModel:
public class CartViewModel {
public List<Line> Lines { get; set; }
public CartViewModel() {
Lines = new List<Line>();
}
}
Try changing the view to use indexes:
#model CartViewModel
using (Html.BeginForm()) {
<h2>Your cart</h2>
<table>
<thead> ... </thead>
<tbody>
#for (int i = 0; i < Model.Lines.Count; i++) {
<tr>
<td>#Html.DisplayFor(m => Model.Lines[i].Article.Description) #Html.HiddenFor(m => Model.Lines[i].Article.Id)</td>
<td>#Html.EditorFor(m => Model.Lines[i].Quantity)</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="Checkout">
}

Post collection of selected values to server action

I have a form with a product-warranty list.
Each list item has checkbox.
How can I post a list of SelectecSources (warranties) to the server?
What do I have to change in that code?
The WarrantyPlusViewModel object is posted to the server. It contains a list SelectedSources which should contain the selected warranty articles.
Is it possible at all to use a complex object for the selected list?
Consider I have to post the WarrantyPlusViewModel to the server which includes
the SelectedSources property with the selected warranty objects.
#model WarrantyPlusViewModel
<div class="row">
<div class="col-md-10 col-md-offset-1">
#using (...)
{
<table class="table table-striped">
<tr>
<th></th>
<th>#Html.DisplayFor(m => m.ProductSelected.Name)</th>
<th>#Html.DisplayFor(m => m.ProductSelected.Description,l)</th>
<th>#Html.DisplayFor(m => m.ProductSelected.Price)</th>
</tr>
#foreach (var product in Model.ProductList)
{
<tr>
<td><input type="checkbox" name="SelectedSources" value="#product" /></td>
<td>#product.Name</td>
<td>#product.Description</td>
<td>#product.Price</td>
</tr>
}
</table>
<input type="submit" value="Save" />
#Html.HiddenFor(p => p.SerialNumber)
}
</div>
[HttpPost]
public virtual async Task<ActionResult> Save(WarrantyPlusViewModel viewModel)
{
return View(MVC.WarrantyPlus.WarrantyPlus.Views.OverviewWarrentyPlus, viewModel);
}
public class WarrantyPlusViewModel
{
// other properties
public List<WarrantyPlusProductViewModel> ProductList { get; set; }
public IEnumerable<WarrantyPlusProductViewModel> SelectedSources { get; set; }
}
It's really hard to bind this way. If you want to do it you should override Model Binder.
If i were on your place i will just use ProductId with some knowlege of list binding and come up with this solution:
#for (var i = 0; i < Model.ProductList.Count(); i++)
{
<tr>
<td><input type="checkbox" name="ProductList[" #i "].Id" value="#Model.ProductList[i].Id" /></td>
<td>#Model.ProductList[i].Name</td>
<td>#Model.ProductList[i].Description</td>
<td>#Model.ProductList[i].Price</td>
</tr>
}

How do I return List or Collection to Controller from View in MVC 3?

Someone please help me return this list properly from my view. I don't see why I'm returning null for my fieldModelList I try to pass to the controller...
Here is my view:
#model List<Regions.SOA.UI.CopyBookSchemaCreator.Models.FieldModel>
<script type="text/javascript" src="~/Scripts/jquery-ui-1.8.11.min.js"></script>
#using (Html.BeginForm("GetResponse", "TestMethods", FormMethod.Post))
{
<table id="tblMethods">
<tr>
<th>
Property Name
</th>
<th>
Request
</th>
</tr>
#foreach (FieldModel fieldModel in Model)
{
<tr>
<td>
#Html.DisplayFor(m => fieldModel.PropertyName)
</td>
<td>
#Html.TextBoxFor(m => fieldModel.PropertyValue)
</td>
</tr>
}
</table>
<div>
<input type="submit"/>
</div>
and here is my controller:
[HttpPost]
public ActionResult GetResponse(List<FieldModel> fieldModelList)
{
return GetResponse(fieldModelList);
}
I am hitting the HttpPost method but if I place a breakpoint just inside it, I am returning null for the fieldModelList right off the bat, which I was hoping would be a list of the values I entered into the texboxes on the view that is of model FieldModel...
I think something is wrong with my logic versus my syntax, or as maybe as well as my syntax, but basically what I want to do is return back a list of type FieldModel with each corresponding PropertyName and PropertyValue to the controller. I noticed I am not passing any kind of id parameter in my BeginForm statement in the view. Do I need one here?
Just in case, here is my model class for FieldModel:
namespace Regions.SOA.UI.CopyBookSchemaCreator.Models
{
public class FieldModel
{
[Display(Name = "Property")]
public string PropertyName { get; set; }
[Display(Name = "Value")]
public string PropertyValue { get; set; }
}
}
Phil Haack wrote an article some time ago explaining how to bind collections (ICollection) to view models. It goes into additional detail about creating an editor template, which you could certainly do as well.
Basically, you need to prefix the HTML elements' name attributes with an index.
<input type="text" name="[0].PropertyName" value="Curious George" />
<input type="text" name="[0].PropertyValue" value="H.A. Rey" />
<input type="text" name="[1].PropertyName" value="Ender's Game" />
<input type="text" name="[1].PropertyValue" value="Orson Scott Card" />
Then, your controller could bind the collection of FieldModel
[HttpPost]
public ActionResult GetResponse(List<FieldModel> fieldModelList)
{
return GetResponse(fieldModelList);
}
I'm not 100% sure the following would name the attributes correctly (I'd recommend using the editor template) but you could easily use the htmlAttributes argument and give it a name using the index.
#for(int i = 0;i < Model.Count;i++)
{
<tr>
<td>
#Html.DisplayFor(m => m[i].PropertyName)
</td>
<td>
#Html.TextBoxFor(m => m[i].PropertyValue)
</td>
</tr>
}
Editor Template
If you wanted to go as far as adding an editor template, add a partial view named FieldModel.ascx to /Views/Shared that is strongly typed to a FieldModel
#model Regions.SOA.UI.CopyBookSchemaCreator.Models.FieldModel
#Html.TextBoxFor(m => m.PropertyName) #* This might be a label? *#
#Html.TextBoxFor(m => m.PropertyValue)
And, then the part of your view responsible for rendering the collection would look like:
#for (int i = 0; i < Model.Count; i++) {
#Html.EditorFor(m => m[i]);
}

delete rows of table on checking of checkboxes

I have table containing data . In every row there is a checkbox plus a checkbox to select all checkbox at the headers.
Upon checking this checkboxes,corresponoding rows are to be deleted from database table.Plus,on chiecking the checkbox at the header,all rows will be deleted from the database table.How can i achieve this asp.net mvc.
As always start with a model:
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Then a controller:
public class HomeController : Controller
{
// TODO: Fetch this from a repository
private static List<ProductViewModel> _products = new List<ProductViewModel>
{
new ProductViewModel { Id = 1, Name = "Product 1" },
new ProductViewModel { Id = 2, Name = "Product 2" },
new ProductViewModel { Id = 3, Name = "Product 3" },
new ProductViewModel { Id = 4, Name = "Product 4" },
new ProductViewModel { Id = 5, Name = "Product 5" },
};
public ActionResult Index()
{
return View(_products);
}
[HttpPost]
public ActionResult Delete(IEnumerable<int> productIdsToDelete)
{
// TODO: Perform the delete from a repository
_products.RemoveAll(p => productIdsToDelete.Contains(p.Id));
return RedirectToAction("index");
}
}
And finally the Index.aspx view:
<% using (Html.BeginForm("delete", "home", FormMethod.Post)) { %>
<table>
<thead>
<tr>
<th>Name</th>
<th>Select</th>
</tr>
</thead>
<tbody>
<%= Html.EditorForModel()%>
</tbody>
</table>
<input type="submit" value="Delete selected products" />
<% } %>
And the product editor template (~/Views/Home/EditorTemplates/ProductViewModel.ascx):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ToDD.Controllers.ProductViewModel>" %>
<tr>
<td>
<%: Model.Name %>
</td>
<td>
<input type="checkbox" name="productIdsToDelete" value="<%: Model.Id %>" />
</td>
</tr>
I would use AJAX. On changing the checked state, I would submit a request to delete all the selected IDs and refresh the table data.
Use jQuery, some other javascript library, or just hand code an AJAX request on check of checkbox. Then alter the DOM on success.
Using jQuery you could do something like:
<table>
<tr>
<td><input type="checkbox" class="check" id="1" /></td>
</tr>
<tr>
<td><input type="checkbox" class="check" id="2" /></td>
</tr>
<tr>
<td><input type="checkbox" class="check" id="3" /></td>
</tr>
</table>
$('.check').click(function() {
var tableRow = $(this).parent().parent();
var id = $(this).attr('id');
$.ajax({
url: 'http://www.YOURDOMAIN.com/Controller/Action/' + id,
success: function(data) {
$(tableRow).remove();
}
});
)};
This is very basic implementation, you could dress it up with some animation in the removal of the row. You also need to pass data and return data with some error handling. Check out here for a jQuery AJAX tutorial.

Resources