I am looking for a way to send a table of data from view to action in .net core mvc. There are tones of explanations about sending data from action to a view using razor foreach and for loops but how to do the opposite using model binding?
I know/used ajax to send data of this sort but data binding is much easier to implement. Also due to lack of answers that I have found in few hours of searching I am starting to believe that I am missing something. I probably did not understand a basic concept of data binding.
Lets say I have a simple model like so:
public class myMode
{
public string item1 { get; set; }
public string item2 { get; set; }
}
Then the view model will be:
public class MyViewModel
{
public List<myMode> tableData { get; set; }
}
In the view I can use something like:
<table>
<tr>
<th>Item1</th>
<th>Item2</th>
</tr>
foreach (var d in Model.tableData)
{
<tr>
<td>#d.item1</td>
<td>#d.item2</td>
</tr>
}
</table>
This is how to get the data from view model to the table. How to do the opposite?
Lets say that in the same example the user is adding rows to the table. How can we bind the table rows to view model List<myMode> tableData { get; set; } when form is submited?
How can we bind the table rows to view model List tableData {
get; set; } when form is submited?
We can use name="tableData[#i].item1" to bind the table rows to view model List tableData
They generate the html like :
You can try the below code:
#model MyViewModel
<form asp-action="Index">
<table>
<tr>
<th>Item1</th>
<th>Item2</th>
</tr>
#{
int i = 0;
}
#foreach (var d in Model.tableData)
{
<tr>
<td><input name="tableData[#i].item1" /></td>
<td><input name="tableData[#i].item2" /></td>
</tr> #(i++)
}
</table>
<input type="submit" />
</form>
Controller:
[HttpPost]
public IActionResult Index(MyViewModel MyViewModel)
{
return View(MyViewModel);
}
result:
If you want to show the data at the table, then submit it, you can try:
<tr>
<td><input asp-for="#Model.tableData[i].item1" /></td>
<td><input asp-for="#Model.tableData[i].item2" /></td>
</tr>
<form asp-action="Privacy">
<table>
<tr>
<th>Item1</th>
<th>Item2</th>
</tr>
#{ int i = 0;}
#foreach (var d in Model.tableData)
{
<tr>
<td><input asp-for="#Model.tableData[i].item1" value="#d.item1"/></td>
<td><input asp-for="#Model.tableData[i].item2" value="#d.item2"/></td>
</tr>
#(i++)
}
</table>
<input type="submit" />
</form>
Related
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);
}
I am new to ASP.net MVC still in learning stage :) Thank you Advance
I have created the View Model to populate the table data based on the PName which I have selected.
Ex: for 1 project we have 10 related Items in , i am displaying all the rows on the screen, and making some updates to those rows to save it back to Database.
I am able to get the data but when I am trying to send the table object to controller to save, it always giving me null
Here is my ViewModel
namespace Application.ViewModel.Projects
{
public class PUpdates
{
public List<FItems> items{ get; set; }
}
}
CSHTML code
#using (Html.BeginForm("PFItems", "Projects", FormMethod.Post))
{
<table id="test">
<tr id="testtr">
<th id="testth"></th>
<th id="testth"></th>
</tr>
#foreach (var item in Model.FItems)
{
<tr id="testtr">
<td id="testtd">
#Html.Label(item.Ftype)
</td>
<td id="testtd">
#Html.TextBox("test", item.AC)
</td>
</tr>
}
<tr> <td><input type="submit" value="Submit" /></td></tr>
</table>
}
Controller Code
[HttpPost]
public PartialViewResult PFItems(PFUviewmodel)
{
return PartialView(viewmodel);
}
I am not able to get my table Object to make the update
Welcome to Stack overflow :)
For model binding to work you need to give you fields an index in the name property. Have a look at this question where I answered a very similar question and explained it.
In your case you need to do something like the following:
#model Application.ViewModel.Projects.ProjectfinanceUpdate
#using (Html.BeginForm("PartialprojectFinanceItem", "Projects", FormMethod.Post))
{
<table id="test">
<tr id="testtr">
<th id="testth">Financial Type</th>
<th id="testth">Actual Cost</th>
</tr>
#var i =0;
#foreach (var item in Model.tblFinanceItems)
{
<tr id="testtr">
<td id="testtd">
#Html.Label(item.FinancialType)
</td>
<td id="testtd">
<input name="ProjectfinanceUpdate.tblFinanceItems[#i].ActualCost" value="#item.ActualCost"/>
</td>
</tr>
#i++;
}
<tr> <td><input type="submit" value="Submit" /></td></tr>
</table>
}
The important part is that model binding works off of the name property, and because it's a list, it needs to have an index property.
Hope that helps.
In ASP.NET view, shown below, I'm getting the error that is specific to line <input asp-for="StateName" />
Error:
'List<GrantsViewModel>' does not contain a definition for 'StateName'
NOTE: View is supposed to display different State Names in an HTML table column.
Controller:
public class DbTestController : Controller
{
private MyProjContext _context;
public DbTestController(MyProjContext context)
{
_context = context;
}
public IActionResult GrantNumbers()
{
var qryVM = from s in _context.StateNames
join g in _context.AnnualGrants on s.StateNumber equals g.StateNumber into sg
from r in sg.DefaultIfEmpty()
select new GrantsViewModel() { StateNumber = s.StateNumber,StateName= s.State, GrantNo= (r == null ? String.Empty : r.GrantNo), FiscalYear = (r == null ? 1900 : r.FiscalYear) };
return View(qryVM.ToList());
}
}
ViewModel:
public class GrantsViewModel
{
public int GrantNo_Id { get; set; }
public string StateNumber { get; set; }
public string StateName { get; set; }
public string GrantNo { get; set; }
public int FiscalYear { get; set; }
}
View:
#model List<MyProjet.Models.GrantsViewModel>
<form asp-controller="DbTest" asp-action="GrantNumbers" asp-route-returnurl="#ViewData["ReturnUrl"]" method="post">
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.First().StateName)
</th>
<th>
#Html.DisplayNameFor(model => model.First().GrantNo)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
<input asp-for="StateName" />
</td>
<td>
#Html.DisplayFor(modelItem => item.GrantNo)
</td>
<td></td>
</tr>
}
</tbody>
</table>
<button type="submit" class="btn btn-default">Save</button>
</form>
#Html.DisplayNameFor(model => model.StateName)
In this line you are trying to access the property StateName from the object referenced by model. Except model references an object of type List<T>, which does not have a property StateName.
To access StateName, you need to provide which element of the list you are accessing, such as the following (assuming you don't need to iterate, since you are just getting the column titles).
#Html.DisplayNameFor(model => model[0].StateName)
To reference the element correctly in the asp-for helper, use
<input asp-for="#item.StateName" />
You want to use FirstOrDefault() when referencing names of properties. This will still be able to get the display names using reflection even if your IEnumerable/List Model is empty.
#Html.DisplayNameFor(model => model.FirstOrDefault().StateName)
Instead of the StateName asp-tag, here is the Razor Helper for an input:
#Html.EditorFor(modelItem => item.StateName)
I'm a bit confused as to why you have this table in a form if you're wanting to just display your list of information. If you clarify, I can better answer based on your intent.
If you just want this data displayed, then use a DisplayFor() like you did for GrantNo column:
#Html.DisplayFor(modelItem => item.StateName)
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 to display a collection in View of ASP.NET MVC Razor project?
My Model and View is below. For one person i've to display Many tests on the screen. Thanks in Advance
namespace UI.Models
{
public class SuperStudent
{
public string FullName { get; set; }
public ICollection<TestDetail> CandidateTestDetails { get; set; }
}
public class TestDetail
{
public DateTime TestDate { get; set; }
public string RegNum { get; set; }
}
}
View
#model IEnumerable<UI.Models.SuperStudent>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FullName)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
Print all test details
</td>
</tr>
}
</table>
For one person your view must be like :
#model UI.Models.SuperStudent
<h2>#Model.FullName</h2>
<table class="table">
<tr>
<th>
TestDate
</th>
<th>
RegNum
</th>
</tr>
#foreach (var item in Model.CandidateTestDetails ) {
<tr>
<td>
#item.TestDate
</td>
<td>
#item.RegNum
</td>
</tr>
}
</table>
The code works the same inside a loop as it does outside the loop. Just reference the loop item. For example:
#foreach (var student in Model) {
#foreach (var test in student.CandidateTestDetails) {
<tr>
<td>#test.RegNum</td>
</tr>
}
}
That would output just the RegNum value. You can also make use of things like DisplayNameFor by referencing an element in the collection:
#foreach (var student in Model) {
#foreach (var test in student.CandidateTestDetails) {
<tr>
<td>
#Html.DisplayNameFor(model => student.CandidateTestDetails.First().RegNum)<br />
#test.RegNum
</td>
</tr>
}
}
While intuitively it may seem that this could fail if Model is empty (since First() would throw an exception), it doesn't actually execute First() on the collection. The engine simply uses that to reflect into the model returned by First() to determine the display name.
Given this, you can construct the markup however you wish for those items. If it's complex enough, you might even create a partial view for just an item in that collection and render that partial view in the loop by passing the item value to the partial view.