ASP.NET MVC Complex Object property stays null on form submit - c#

I'm getting myself acquainted with ASP.NET MVC but i'm running into something probably trivial. I have a model called ToDoList, this is a complex type with a list of ToDoItems:
public class ToDoList
{
public Guid Id {get;set;}
public string Name { get; set; }
public virtual ICollection<ToDoItem> Items {get;set;}
}
public class ToDoItem
{
public int Id { get; set; }
public string Task { get; set; }
public bool IsDone { get; set; }
public virtual ToDoList ToDoList { get; set; }
}
My Details page with form looks like this:
#model DataLayer.TomTest.Entities.ToDoList
<h2>#Model.Name</h2>
#using (#Html.BeginForm())
{
#Html.AntiForgeryToken()
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Items.First().Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().Task)
</th>
<th>
#Html.DisplayNameFor(model => model.Items.First().IsDone)
</th>
</tr>
#foreach (var toDoItem in Model.Items)
{
<tr>
<td>
#toDoItem.Id
</td>
<td>
#Html.EditorFor(model => toDoItem.Task)
</td>
<td>
#Html.EditorFor(model => toDoItem.IsDone, new {htmlAttributes = new {#Style = "margin-left: 10px;"}})
</td>
</tr>
}
</table>
<input type="submit" value="Save" class="btn btn-default"/>
}
And this is the method it posts to:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Details([Bind(Include = "Id,Name,Items")] ToDoList todoList)
{
if (ModelState.IsValid)
{
_context.Entry(todoList).State = EntityState.Modified;
await _context.SaveChangesAsync();
return View();
}
return View();
}
As you can see I included the [Bind] attribute as I read somewhere that would ensure i get the correct properties passed. When I debug this however, only the Id property is filled the rest remains null.
What can I do to fix this? Is it a mistake in the View? Or is it possible Entity Framework isn't setup correctly?
Thanks in advance for your help.

Model binding to a list doesn't work with a foreach; you need to use a for loop instead.
You'll also need hidden inputs for any properties which don't have editors within the loop.
#for (int index = 0; index < Model.Items.Count; index++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Items[index].Id)
#Model.Items[index].Id
</td>
<td>
#Html.EditorFor(m => m.Items[index].Task)
</td>
<td>
#Html.EditorFor(m => m.Items[index].IsDone, new { htmlAttributes = new { #Style = "margin-left: 10px;" } })
</td>
</tr>
}
ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries - Scott Hanselman's Blog

Related

How do I display a list from SQL database when my table is not enumerable? I can use details view fine, but not an index view

I am having difficulty getting my Index view to display the values I have from the SQL database.
The main issue is that I cannot use #foreach (var item in Model) {... because my table is not created as Enumerable (I think). I run into the error message System.NullReferenceException: 'Object reference not set to an instance of an object.' pointing at Model in the foreach statement expression.
I am wondering if I need to set my table up as an Enumerable list, then display each item. Or if I am missing something with regards to my Index view. Or maybe I need to pass something through the Index return View()?
Here is the Image Model:
namespace Uploadimage.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web;
public partial class Image
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string Phone { get; set; }
[Required]
[RegularExpression(#"[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
ErrorMessage = "Email doesn't look like a valid email address.")]
public string Email { get; set; }
[Compare("Email", ErrorMessage = "Emails do not match.")]
public string ConfirmEmail { get; set; }
[DisplayName("Upload File")]
public string ImagePath { get; set; }
public HttpPostedFileBase ImageFile { get; set; }
}
}
Here is the controller:
public class ImageController : Controller
{
[HttpGet]
public ActionResult Add()
{
return View();
}
[HttpPost]
public ActionResult Add(Image imageModel)
{
string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
string extension = Path.GetExtension(imageModel.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
imageModel.ImagePath = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
imageModel.ImageFile.SaveAs(fileName);
using(LoginDBEntities db = new LoginDBEntities())
{
db.Images.Add(imageModel);
db.SaveChanges();
}
ModelState.Clear();
return View();
}
[HttpGet]
public ActionResult View(int id)
{
Image imageModel = new Image();
using (LoginDBEntities db = new LoginDBEntities())
{
imageModel = db.Images.Where(x => x.Id == id).FirstOrDefault();
}
return View(imageModel);
}
public ActionResult Index()
{
return View();
}
}
And lastly, my Index View:
#model IEnumerable<Uploadimage.Models.Image>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.Province)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.ImagePath)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.Province)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.ImagePath)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
I have tried thoroughly looking this up but have found things that don't specifically apply to me.
Or I don't know what to look up.
Two things:
Your Index controller action will need to load the images to pass to the view. At a minimum to serve a collection of images as the view's "model":
public ActionResult Index()
{
using (LoginDBEntities db = new LoginDBEntities())
{
var images = db.Images.ToList();
return View(images);
}
}
Then in the view you will need to check whether you actually get any results, then extract your labels from the first result if there are any, or display a suitable message if there are no images:
<!-- ... -->
#if (Model.Any())
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).LastName)
</th>
...
</tr>
#for (int count = 0; count < Model.Count; count++) {
<tr>
<td>
#Html.DisplayFor(model => model.ElementAt(count).FirstName)
</td>
<td>
#Html.DisplayFor(model => model.ElementAt(count).LastName)
</td>
...
<td>
#Html.ActionLink("Edit", "Edit", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Details", "Details", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Delete", "Delete", new { id=#Model.ElementAt(count).Id })
</td>
</tr>
}
</table>
}
else
{
<p> No Images. </p>
}
The above is written from memory so it will likely have some syntactic issues but it should point you in the right direction.
Typically when working with collections like search results I define a view model class for the results page that itself contains the collection of results. The page's model becomes that wrapper view model which can contain details about the page (such as things like current search criteria, lookup values for searches, etc.) along with the collection of results. (typically a PagedList to support pagination)
I.e.
[Serializable]
public class ImageIndexViewModel
{
public string NameSearchString { get; set; }
public ICollection<ImageViewModel> Results { get; set; } = new List<ImageViewModel>();
}
Where ImageViewModel is a serializable POCO view model to represent just the details about images that the view will display. Often the views don't need everything about a data row, and in cases where the data row has navigation properties and extra fields we don't need to display, serializing entities results in lazy load calls or simply sending a lot of extra data that isn't needed.
You don't have anything in your Index method which gets a list of entities.
Your Index method in your controller should look something like:
public ActionResult Index()
{
var model = db.Images.ToList();
return View(model);
}

Can't pass an enumerable model to a controller?

I'm a bit confused because I thought this a very straight-forward thing, it's possibly something simple tripping me up.
I have a view:
#model IEnumerable<CarViewModel>
#using (Html.BeginForm("SummarySaveAll", "VroomVroom", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
#Html.DisplayNameFor(model => model.Driver)
</th>
<th width="1">
#Html.DisplayNameFor(model => model.Colour.Name)
</th>
</tr>
</thead>
<tbody>
#foreach (var element in Model)
{
<tr>
<td width="1">
#Html.DisplayFor(m => element.Driver)
</td>
<td width="1">
#Html.DropDownListFor(m => element.Colour, element.Colours, "Unknown")
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
and the list/enumerable of CarViewModel is supposed to bounce back to the VroomVroom controller, action SummarySaveAll which it does - but the viewmodel on the page doesn't get passed back to it:
[HttpPost]
public ActionResult SummarySaveAll(IEnumerable<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View();
}
I tried to encapsulate the List in another ViewModel and cycle through elements using a for i loop but that wouldn't pass back to the controller either.
Surely it's possible to send a List or IEnumerable of models back to a controller?
My CarVM:
public class CarViewModel
{
[MaxLength(150)]
[Display(AutoGenerateField = true, Name = "Entered By")]
public string Driver { get; set; }
[Display(AutoGenerateField = true)]
public Colour Colour { get; set; }
[Key]
[Display(AutoGenerateField = false)]
public int Id { get; set; }
[Display(AutoGenerateField = false)]
public bool IsDeleted { get; set; } = false;
public IEnumerable<SelectListItem> Colours { get; set; }
public CarViewModel() { }
public CarViewModel(Model CarModel summaryModel, CarPropertyCollection propertyCollection)
{
Driver = summaryModel.Driver;
Id = summaryModel.Id;
IsDeleted = summaryModel.IsDeleted;
Colour = summaryModel.Colour == null ? null :
propertyCollection.Colours.Where(x => x.Id == summaryModel.Colour.Id).FirstOrDefault();
Colours = propertyCollection.Colours.Select(x => new SelectListItem { Value = x.Id.ToString(), Text = x.Name });
}
}
}
Must stress that Colour is a custom class but only has Id and Name properties
Colours doesn't relate to a specific car, it relates to cars in general, so rather than using a collection as your view model, create a wrapper:
class EditCarsViewModel
{
public IEnumerable<SelectListItem> Colours { get; set; }
public IList<CarViewModel> Cars { get; set; }
}
Then your view:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour, Model.Colours, "Unknown")
</td>
}
Any other CarViewModel properties will need their own input as well. HiddenFor can be used if they should be readonly:
#model EditCarsViewModel
#for (int i = 0; i < Model.Cars.Length; i++)
{
#Html.HiddenFor(m => Model.Cars[i].Id)
#Html.HiddenFor(m => Model.Cars[i].Driver)
<!-- etc. -->
<td>
#Html.DropDownListFor(m => Model.Cars[i].Colour.Id, Model.Colours, "Unknown")
</td>
}
And your controller:
[HttpPost]
public ActionResult SummarySaveAll(EditCarViewModel model)
{
// model.Cars should be populated
return View();
}
Note that an indexable collection, such as IList<T> should be used, as the form field names need to include the index to differentiate the items.
Edit by OP
The Colour class consists of a [Key] int Id property and a string Name property. For DropDownList items I had to make sure the Id property was specified on the m => Model.Cars[i].Colour.Id line otherwise that particular prop was coming back as null even though other items were coming through fine.
try
[HttpPost]
public ActionResult SummarySaveAll(IList<CarViewModel> summaries)
{
// Want to do stuff with summaries but it's always null
return View(summaries);
}
I've also added this model as a param for your view
This how you do it:
First my View which posts back to a controller named Home and an action named ListView:
#model List<MyModel>
#{
ViewData["Title"] = "Using a list as model";
}
<h1>#ViewData["Title"]</h1>
#using (Html.BeginForm("ListView", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
<table>
<thead>
<tr>
<th width="1">
Name
</th>
<th width="1">
Description
</th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td width="1">
#Html.DisplayFor(m => Model[i].Name)
</td>
<td width="1">
#Html.TextBoxFor(m => Model[i].Description)
</td>
</tr>
}
</tbody>
</table>
<div>
<input type="submit" value="Save Changes" class="btn" />
#Html.ActionLink("Cancel Changes", "Index", null, new { #class = "btn" })
</div>
</div>
}
Notice how I used an indexer to render the controls [i]
This is my model:
public class MyModel
{
public string Name { get; set; }
public string Description { get; set; }
}
This is my controller action:
[HttpPost]
public IActionResult ListView(IEnumerable<MyModel> model)
{
return View(model);
}
And this is the result:

One field in posted array not binding to model

I have the following, abridged for clarity and confidentiality, viewmodels:
public class BoqReviewViewModel
{
public List<BoqReviewItemViewModel> Items { get; set; } = new List<BoqReviewItemViewModel>();
}
public static BoqReviewItemViewModel FromEntity(GTT_BOQ_IMPORT import)
{
var ret = new BoqReviewItemViewModel();
ret.BOQ_IMPORT_ID = import.BOQ_IMPORT_ID;
ret.ITEM_CODE = import.ITEM_CODE;
ret.ITEM_DESC = import.ITEM_DESC;
ret.UNIT = import.UNIT;
ret.QTY = import.QTY;
ret.RATE = import.RATE;
ret.AMOUNT = import.AMOUNT;
ret.ITEM_PAYS = import.ITEM_PAYS;
ret.ITEM_CPA = import.ITEM_CPA;
return ret;
}
public GTT_BOQ_IMPORT ToEntity()
{
var ret = new GTT_BOQ_IMPORT();
ret.BOQ_IMPORT_ID = this.BOQ_IMPORT_ID;
ret.ITEM_CODE = this.ITEM_CODE;
ret.ITEM_DESC = this.ITEM_DESC;
ret.UNIT = this.UNIT;
ret.QTY = this.QTY;
ret.RATE = this.RATE;
ret.AMOUNT = this.AMOUNT;
ret.ITEM_PAYS = this.ITEM_PAYS;
ret.ITEM_CPA = this.ITEM_CPA;
return ret;
}
[Key]
public Guid BOQ_IMPORT_ID;
public string ITEM_CODE { get; set; }
public string ITEM_DESC { get; set; }
public string UNIT { get; set; }
public decimal? QTY { get; set; }
public decimal? RATE { get; set; }
public decimal? AMOUNT { get; set; }
public bool? ITEM_PAYS { get; set; }
public bool? ITEM_CPA { get; set; }
public bool IsFlagged { get; set; }
}
Then I have the following GET action for an index type view:
[HttpGet]
public ActionResult BoqReview()
{
var model = new BoqReviewViewModel();
model.CONTRACT_ID = Guid.NewGuid();
model.PROJECT_ID = Guid.NewGuid();
model.Items = GetItems().ToList();
return View(model);
}
Where GetItems is a method that returns an IEnumerable with three fully populated item level viewmodels. When I startup the app, I see a properly populated table with 3 rows with all columns properly populated.
This is the view:
#using (Html.BeginForm("BoqReview", "Boq", FormMethod.Post))
{
<table class="table">
<tr>
<th>
</th>
<th class="col-header">
Item Code
</th>
<th class="col-header">
Item Description
</th>
<th>
Unit
</th>
<th>
Quantity
</th>
<th>
Rate
</th>
<th>
Amount
</th>
<th>
Item Pays
</th>
<th>
Item CPA
</th>
<th>
</th>
</tr>
#for (var i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>
#Html.TextBoxFor(model => model.Items[i].BOQ_IMPORT_ID)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].ITEM_CODE)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_CODE)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].ITEM_DESC)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_DESC)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].UNIT, new {style = "width: 120px;"})
#Html.ValidationMessageFor(model => Model.Items[i].UNIT)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].QTY)
#Html.ValidationMessageFor(model => Model.Items[i].QTY)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].RATE)
#Html.ValidationMessageFor(model => Model.Items[i].RATE)
</td>
<td>
#Html.TextBoxFor(model => Model.Items[i].AMOUNT)
#Html.ValidationMessageFor(model => Model.Items[i].AMOUNT)
</td>
<td>
#Html.DropDownListFor(model => Model.Items[i].ITEM_PAYS, Model.YesNoSelectList)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_PAYS)
</td>
<td>
#Html.DropDownListFor(model => Model.Items[i].ITEM_CPA, Model.YesNoSelectList)
#Html.ValidationMessageFor(model => Model.Items[i].ITEM_CPA)
</td>
<td>
#Html.CheckBoxFor(model => Model.Items[i].IsFlagged)
</td>
</tr>
}
<tr>
<td colspan="4">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Everything looks perfect, yet when I submit the form:
[HttpPost]
public ActionResult BoqReview(BoqReviewViewModel model)
{
using (var db = new AppDbContext())
{
foreach (var modelItem in model.Items)
{
var updated = modelItem.ToEntity();
db.GttBoqImports.AddOrUpdate(updated);
}
db.SaveChanges();
}
return RedirectToAction("BoqReview");
}
The BOQ_IMPORT_ID field on each item viewmodel is an empty GUID, yet the displayed table shows a proper GUID. All other properties of the item level viewmodels are still populated.
What could be wrong that just this BOQ_IMPORT_ID is not properly model bound? When I debug the network call in Chrome tools, it shows a proper GUID value for each BOQ_IMPORT_ID in the form data posted:
Items[0].BOQ_IMPORT_ID: 610d9a12-920f-43aa-89bc-08b68067a462
Items[0].ITEM_CODE: AA1
Items[0].ITEM_DESC: Some A's and a 1
Items[0].UNIT: kg
Items[0].QTY: 200
...
JUST IN: Renaming the VM property in that class and in the view solved the whole problem. Maybe, despite several VS restarts, a machine restart mighty have somehow helped. This smells of temp files or some caching somewhere.

Querying related entities with Eager & Explicit loading it doesn't work in EF for ASP.NET MVC CORE

I tried this in VS 2015 .net MVC Application and it works perfectly without any trouble.
Instead, trying to test the same with DOTNET CORE 1.x, I can't get the 2^ level navigation property . You've got a situation like that or you know how solve?
DATABASE CLASS
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Person Person { get; set; }
public Blog Blog { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string Username { get; set; } // I need to show this in the partial view
public string Action { get; set; }
}
CONTROLLER
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
Blog blog = await _context.Blogs
.Include(b=>b.Posts)
.Include(b=>b.Posts.Select(x=>x.Person))
.FirstOrDefaultAsync(b => b.BlogId == id);
//Then.Include don't show property!
if (blog == null)
{
return NotFound();
}
return View(blog);
}
VIEW
#model MyProj.Models.Blog
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>Blog</h4>
<hr />
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.Url)
</dt>
<dd>
#Html.DisplayFor(model => model.Url)
</dd>
</dl>
</div>
<p>
#Html.ActionLink("Edit", "Edit", new { id = Model.BlogId }) |
#Html.ActionLink("Back to List", "Index")
</p>
<br />
<div>
#Html.Partial("_posts", this.Model.Posts)
</div>
PARTIAL VIEW
#model IEnumerable<MyProj.Models.Post>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Content)
</th>
<th>
Person
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Content)
</td>
<td>
#Html.DisplayFor(modelItem => item.Person.Username)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PostId }) |
#Html.ActionLink("Details", "Details", new { id=item.PostId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PostId })
</td>
</tr>
}
</table>
In my case I cannot get the property Person.Username in a partial that show post list. ThenIclude (with intellisense) don't show the table
Error code :
InvalidOperationException: The property expression 'b => {from Post x in [b].Posts select [x].Person}' is not valid. The expression should represent a property access: 't => t.MyProperty'. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
Thanks in advance for troubleshooting, I can't figure it out.
I tried to solve this for about 20 hours to understand why it doesn't work..... At end it was egg of Columbus. Thanks to Ivan Stoev for the comment.
Though intellisense doesn't show the related entities, when the app is builded it works.
Blog blog = await _context.Blogs
.Include(b=>b.Posts)
.ThenInclude(p=>p.Person)
.FirstOrDefaultAsync(b => b.BlogId == id);

MVC passing the selected object from a List view page

I have a little serach box that returns results from a database. That works fine. The results are in a List page and display correctly. However, I need to take the selected object and pass it to my controller. I am getting NULL values when I debug it, and an empty results page.
Here is the model:
public class CodeSnip
{
public short Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Code { get; set; }
public LangType Language { get; set; }
public string Creator { get; set; }
}
public class ListOfCodeSnips : List<CodeSnip>
{
public CodeSnip CodeSnip { get; set; }
}
public enum LangType
{
CSharp,
css,
HTML,
JavaScript,
Perl,
PHP,
Python,
Ruby,
SQL,
VB,
XML,
Other
}
Here is the controller method (which does nothing atm):
[HttpPost]
public ActionResult Display(CodeSnip snip)
{
return View(snip);
}
Here is what I have for a view. Again, it posts only NULLS for the object values:
#model Models.ListOfCodeSnips
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Language)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Creator)
</th>
</tr>
#using (Html.BeginForm("Display", "Home", FormMethod.Post))
{
foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Language)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Creator)
</td>
<td>
<input type="submit" value ="Display"/>
</td>
</tr>
}
}
</table>
so, in short, what I want to do is take the selected model-item from the view and pass it into my controllers Display method, but as I said, all I get are nulls.
I have looked around and all i find are examples f how to pass a List of objects. I tried monkeying with those, but got nothing.
Thanks for your time.
Since you said that you have tried with ActionLink and it did not work, here is how it would work.. instead of passing the type that you are looking for as a parameter for the Display action, pass the ID of the record.
So it would look something like this:
Controller Action
[HttpGet]
public ActionResult Display(int id)
{
var snip = db/* connection string */.TableName.Find(id);
return View(snip);
}
ActionLink
#Html.ActionLink("Display", "Display", "ControllerName", new { id = item.Id }, null )
// Or if you want a button that acts as a link, and not just a plain link
<input type="button" class="btn" value="Display" onclick="location.href='#Url.Action("Display", "ControllerName", new { id = item.Id })'" />
Let me know if this helps!
There are two problems with your code:
1) You do not render any <input> elements in which you send the selected values back to the controller. Use Html.HiddenFor or Html.EditorFor in addition to Html.DisplayFor.
2) In order for the MVC Modelbinder to be able to bind your list, use a for loop instead of foreach.
See also MVC Form submit not transferring the updated model back to action method for List
for (var i = 0; i < Model.Count(); i++) {
<tr>
<td>
#Html.DisplayFor(m => m[i].Title)
#Html.HiddenFor(m => m[i].Title)
</td>
#* etc ... *#
<tr>
}
PS: this loop would post all displayed CodeSnips, i.e. the receiving action would have the signature public ActionResult Display(ListOfCodeSnips postData).

Categories

Resources