MVC 4 How to display foreign key related data in view? - asp.net-mvc

I have two classes generated from my database by EF 6:
class Person {
public int PID { set; get; }
public string Name { set; get; }
public int CountryID { set; get; }
public virtual ICollection<Country> Country { set; get;}
}
class Country {
public int CID { set; get; }
public string Name { set; get; }
}
I would like to show data in a table as follows:
Person Name ^v Countries ^v Operation
----------------------------- ----------------------------- ---------------
[___________________________] [________dropdown_________|v] [Edit] [Delete]
...
[___________________________] [________dropdown_________|v] [Edit] [Delete]
[Pager]
In my database CountryID is a foreign key to Country.CID. I would like to show data and allow to in line editing and/or deleting. The columns have to be disabled but in edit mode of course enabled instead.
I googled a lot and read stackoverflow too but didn't found a comprehensive help for this.
Does anybody has an example how to do this gridview?
Thanks a lot

You can try this:
Controller
public ActionResult Index()
{
var persons = db.Person.Include(c => c.Country);
return View(persons.ToList());
}
View
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.PersonName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Country.CountryName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PersonID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PersonID })
</td>
</tr>

http://www.jtable.org/demo/PagingAndSorting
You can also find some good nuget packages for inline editing grid.

Related

ASP MVC Nested Models

I need a view that displays rows of header-detail info. For a trivial case I set up a Course-Student tiny database with 4 simple tables. The Courses and Students tables are linked with a Section table which also has a student grade in it. Also, the Student table is owned by a Student Type Table. There are helper classes to hold the list of "header" data for the course and a "detail" list for each student who takes that class.
namespace SchoolA.Models
{
public class CourseInfo2
{
public int CourseID { get; set; }
public int CourseNumber { get; set; }
public string CourseName { get; set; }
public IEnumerable<CourseSectionList> CourseStudentList { get; set; }
}
}
namespace SchoolA.Models
{
public class CourseSectionList
{
public string Student { get; set; }
public string Type { get; set; }
public string Grade { get; set; }
}
}
The controller is:
public ActionResult Courselisting()
{
List<CourseInfo2> courseInfo2 = new List<CourseInfo2>();
List<CourseSectionList> courseSectionList = new List<CourseSectionList>();
var Cquery = from c in db.Courses select c;
foreach (var item in Cquery)
{
Course course = db.Courses.Find(item.CourseID); // first find the selected course
// get the sections
foreach (var s in query) // go through each section
{
// get the section data
courseSectionList.Add(new CourseSectionList
{
Student = student.StudentName,
Type = studentType.StudentTypeName,
Grade = s.SectionStudentGrade
});
} // end of section loop
courseInfo2.Add(new CourseInfo2
{
CourseID = course.CourseID,
CourseNumber = course.CourseNumber,
CourseName = course.CourseName,
CourseStudentList = courseSectionList
});
} // end of Course loop
return View(courseInfo2); // Course List and Section list for each course
}
The View is:
#model SchoolA.Models.CourseInfo2
#* doesn't work either: model List<SchoolA.Models.CourseInfo2>*#
<div>
<table>
<tr><th>ID</th> <th>Number</th> <th>Course</th></tr>
#foreach (var CourseInfo2 in Model)
{
<tr>
<td>
#CourseInfo2.CourseID
</td>
<td>
#CourseInfo2.CourseNumber
</td>
<td>
#CourseInfo2.CourseName
</td>
<td>
<table class="table">
<tr><th>Student</th> <th>Type</th><th>Grade</th></tr>
#foreach (var s in Model.CourseStudentList)
{
<tr>
<td>
#s.Student
</td>
<td>
#s.Type
</td>
<td>
#s.Grade
</td>
</tr>
}
</table>
</td>
</tr>
}
</table>
</div>
The problem is that I get a variety of errors as I try to pass these two models to the view. In the code as shown above I get this error:
CS1579: foreach statement cannot operate on variables of type 'SchoolA.Models.CourseInfo2' because 'SchoolA.Models.CourseInfo2' does not contain a public definition for 'GetEnumerator'
I've tried a number of variations for passing the models but always run into one error other the other that prevents both models from working in the view. I should note that I tested each part of the view independently and the controller code works fine to deliver the correct data to the view. However, I can't combine the two.
The problem seems to be the way I create instances of the models an how they are passed to the view. What am I doing wrong?
You're passing your view a List<SchoolA.Models.CourseInfo2>, but the view expects a single SchoolA.Models.CourseInfo2.
Change your #model declaration to IEnumerable<SchoolA.Models.CourseInfo2>.
Then change your foreach loops.
Change the first loop to foreach (var courseInfo in Model)
Change the inner loop to foreach (var s in courseInfo.CourseStudentList)
You are passing a model which is list of object, while in view you have single object.
Change your view model bind as below:
#model List<SchoolA.Models.CourseInfo2>
Problem: I have to edit Level2Name (by UI it's a textbox inside the child grid).
I'm able to edit Level1name (parent grid text field) and able to get value in my controller.
Question: How do I able to edit the nested textbox.
What I've tried.
Code:
Model
public class Level0
{
public Level0()
{
Level1= new List<Level1>();
}
public int? ID{ get; set; }
public int? NameText{ get; set; }
public List<Level1> lev1{ get; set; }
}
public class Level1
{
public Level1()
{
Level2= new List<Level2>();
}
public int Lev1ID {get;set;}
public string Level1name{ get; set; }
public List<Level2> level2{ get; set; }
}
public class Level2
{
public string Level2_ID { get; set; }
public string Level2Name{ get; set; }
}
UI Code
#for (int i = 0; i < Model.Level1.Count; i++)
{
#Html.HiddenFor(modelItem => Model.Floors[i].Lev1ID )
#for (int j = 0; j < Model.Level1[i].Level2.Count; j++)
{
#Html.HiddenFor(m => m.Level1[i].Level2[j].OP)
#Html.TextBoxFor(m => m.Level1[i].Level2[j].Level2Name, new { #class = "form-control" })
}
}

Using 2 models in one view in ASP.NET MVC 5

I am new to ASP.NET MVC and I am kindly asking you to help me with this problem:
I have 2 models - Delegation:
public int idDelegation { get; set; }
public string Delegation_Name { get; set; }
public string Employee_Name { get; set; }
and Project:
public int idProject { get; set; }
public string Project_System_Name { get; set; }
public string Project_System_ID { get; set; }
I want to display in a view a list of current delegations and projects, with the Edit/Details/Delete options for each one.
I created a ViewModel DelegationProject:
public class DelegationProject
{
public IEnumerable<Delegation> delegations { get; set; }
public IEnumerable<Project> projects { get; set; }
}
In my Index view I have:
#model IEnumerable<....ViewModel.DelegationProject>
<table class="table">
<tr>
<th>#Html.DisplayNameFor(model => model.delegations.Employee_Name</th>
<th>#Html.DisplayNameFor(model => model.delegations.Delegation_Name</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(itemModel => item.delegations.Employee_Name)</td>
<td>#Html.DisplayFor(itemModel => item.delegations.Delegation_Name</td>
<td>
#Html.ActionLink("Edit", "Edit", new { x => x.idDelegation }) |
#Html.ActionLink("Details", "Details", new { x => x.idDelegation }) |
#Html.ActionLink("Delete", "Delete", new { x => x.idDelegation })
</td>
</tr>
}
</table>
When I run the application, I get these error messages:
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1061: 'System.Collections.Generic.IEnumerable' does not contain a definition for 'Employee_Name' and no extension method 'Employee_Name' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)
I tried different things (like List<> or Tuple) from stackoverflow topics and other sites posts, but I wasn't able to use to models in the same view and iterate through them.
I managed to find a solution for this problem.
The ViewModel DelegationProject remained the same, in my controller for Index action I have:
public ActionResult Index()
{
DelegationProject viewModel = new DelegationProject();
viewModel.delegations = GetDelegations();
viewModel.projects = GetProjects();
return View(viewModel);
}
And I created two methods in the respective controller - GetDelegations() and GetProjects():
public List<Delegation> GetDelegations()
{
var deleg = db.Delegation.Include(d => d.Project);
var list = new List<Delegation>(deleg).ToList();
return list;
}
public List<Project> GetProjects()
{
var projects = db.Project.Include(p => p.Country);
var list = new List<Project>(projects);
return list;
}
My View looks like this:
#model .......ViewModel.DelegationProject
#foreach (var item in Model.delegations)
{
<tr>
<td>
#Html.DisplayFor(itemModel => item.Employee_Name)
</td>
<td>
#Html.DisplayFor(itemModel => item.Delegation_Name)
</td>
</tr>
}
Hope this could be of any help to somebody.

ASP MVC - How use two models in one single view

I have a web page that have two task
1) add new products
2) display existing products in a table (refer below image)
also i have two classes for each above task
for display existing details and save new product
public class VMProduct
{
public List<Product>ProductList { get; set; }
}
public class Product
{
public String ProductID { get; set; }
public String ProductName { get; set; }
public String Uom1 { get; set; }
public String Uom2 { get; set; }
public String ProductCategoryID { get; set; }
}
my problem is i can display data by using VMProduct.ProductList model but how i can save new product items using "product" model ?
how can i use both Models in One view?
if i use "product" model's properties in VMProduct model it is duplication codes right?
can anyone have a Solution for this?
thanks in advance
It looks like VMProduct is already a custom view model (I'm mostly guessing by its name), so just add a property to it of the type you need:
public class VMProduct
{
public List<Product> ProductList { get; set; }
public Product NewProduct { get; set; }
}
Though, unless I'm misunderstanding something about your UX, this might not even be necessary. The model used to render a view doesn't necessarily need to be the same model received from any given form on that view. So you might continue to render the view using the VMProduct object you have now, and the form for adding a product can still post an instance of Product to the controller action which invokes the add operation. Something like:
public ActionResult Add()
{
// create your view model
return View(someVMProduct);
}
[HttpPost]
public ActionResult Add(Product newProduct)
{
// add the product to the backing data store
return RedirectToAction("Add");
}
Here explained how to implement 2 model in 1 view in MVC 4. Sometimes its required to implement a page where 2 forms exist in a single view (page) like Login & Registration in a same view.
You can get complete tutorial here : http://dotnetawesome.blogspot.com/2013/09/2-model-in-1-view-in-mvc-4.html
Here is the sample code :
A ViewModel Created :
public class UsersLoginRegister
{
public User User { get; set; }
public Login Login { get; set; }
}
User Entity
public partial class User
{
public int UserID { get; set; }
[Required]
public string Username { get; set; }
[Required]
[DataType(System.ComponentModel.DataAnnotations.DataType.Password)]
public string Password { get; set; }
[Required]
public string FullName { get; set; }
}
Login Entity
public class Login
{
[Required]
public string UserName { get; set; }
[Required]
[DataType( System.ComponentModel.DataAnnotations.DataType.Password)]
public string Password { get; set; }
}
In the view (Html) code :
<table>
<tr>
<td><b>Login</b></td>
<td><b>Register</b></td>
</tr>
<tr>
<td>
#using (Html.BeginForm("Login", "Home", FormMethod.Post))
{
<table>
<tr>
<td>Username : </td>
<td>#Html.TextBoxFor(a => a.Login.Username)</td>
</tr>
<tr>
<td>Password : </td>
<td>#Html.EditorFor(a => a.Login.Password)</td>
</tr>
<tr>
<td></td>
<td> <input type="submit" value="Submit" /></td>
</tr>
</table>
}
</td>
<td>
#using (Html.BeginForm("Register", "Home", FormMethod.Post))
{
<table>
<tr>
<td>Fullname : </td>
<td>#Html.TextBoxFor(a => a.User.FullName)</td>
</tr>
<tr>
<td>Username : </td>
<td>#Html.TextBoxFor(a => a.User.Username)</td>
</tr>
<tr>
<td>Password : </td>
<td>#Html.EditorFor(a => a.User.Password)</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
</td>
</tr>

How to use two IENumerable models in one view

I am trying to use two models in one view, but from what I understand my program just don't see any objects within models.
Here is my code.
Models:
public class Album
{
[Key]
public int ThreadId { get; set; }
public int GenreId { get; set; }
public string Title { get; set; }
public string ThreadByUser { get; set; }
public string ThreadCreationDate { get; set; }
public string ThreadContent { get; set; }
public Genre Genre { get; set; }
public List<Posts> Posty { get; set; }
}
public class Posts
{
[Key]
public int PostId { get; set; }
public int ThreadId { get; set; }
public string PostTitle { get; set; }
public string PostContent { get; set; }
public string PostDate { get; set; }
public string PosterName { get; set; }
public Album Album { get; set; }
}
public class ModelMix
{
public IEnumerable<Posts> PostsObject { get; set; }
public IEnumerable<Album> ThreadsObject { get; set; }
}
Index controller code:
public ActionResult Index(int id)
{
ViewBag.ThreadId = id;
var posts = db.Posts.Include(p => p.Album).ToList();
var albums = db.Albums.Include(a => a.Genre).ToList();
var mixmodel = new ModelMix
{
PostsObject = posts,
ThreadsObject = albums
};
return View(mixmodel);
}
View code:
#model MvcMusicStore.Models.ModelMix
<h2>Index</h2>
#Html.DisplayNameFor(model => model.PostsObject.PostContent)
And when I try to execute my program I am getting this error:
CS1061: The "
System.Collections.Generic.IEnumerable 'does not contain a definition
of" PostContent "not found method of expanding" PostContent ", which
takes a first argument of type' System.Collections.Generic.IEnumerable
"
How I can make it work as intended? There are a lot of questions like mine on the internet but I couldn't find any matching my case.
Looping over models can be a little confusing to begin with in MVC, only because the templated helpers (i.e. Html.DisplayFor and Html.EditorFor) can be provided templates which the helper will automatically invoke for every element in a collection. That means if you're new to MVC, and you don't realise a DisplayTemplate or an EditorTemplate has not been provided for the collection already, it looks as though a simple:
#Html.DisplayFor(m => m.SomePropertyThatHoldsACollection)
is all you need. So if you've seen something like that already, that might be why you made the assumption it would work. However, let's assume for a moment that a template has not been provided. You have two options.
Firstly, and most simply, would be to use foreach over the collection:
#foreach (var post in Model.PostsObject)
{
#Html.DisplayFor(m => post.PostTitle)
// display other properties
}
You could also use a for loop, but with IEnumerable<T>, there is no indexer, so this won't work:
#for (int i = 0; i < Model.PostsObject.Count(); i++)
{
// This generates a compile-time error because
// the index post[i] does not exist.
// This syntax would work for a List<T> though.
#Html.DisplayFor(m => post[i].PostTitle)
// display other properties
}
If you did want to use the for loop still, you can use it like so:
#for (int i = 0; i < Model.PostsObject.Count(); i++)
{
// This works correctly
#Html.DisplayFor(m => post.ElementAt(i).PostTitle)
// display other properties
}
So use whichever you prefer. However, at some point it would be a good idea to look into providing templates for your types. (Note: Although this article was written for MVC 2, the advice still applies.) They allow you to remove looping logic from your views, keeping them cleaner. When combined with Html.DisplayFor, or Html.EditorFor, they will also generate correct element naming for model binding (which is great). They also allow you to reuse presentation for a type.
One final comment I'd make is that the naming of your properties is a little verbose:
public class ModelMix
{
public IEnumerable<Posts> PostsObject { get; set; }
public IEnumerable<Album> ThreadsObject { get; set; }
}
We already know they're objects, so there's no need to add that on the end. This is more readable:
public class ModelMix
{
public IEnumerable<Posts> Posts { get; set; }
public IEnumerable<Album> Threads { get; set; }
}
You need to iterate them like this:
#model MvcMusicStore.Models.ModelMix
<h2>Index</h2>
#for(var i=0; i<model.PostsObject.Count(); i++)
{
#Html.DisplayNameFor(model => model.PostsObject[i].PostContent)
}
And also it's better to save IList instead of IEnumerable, as it will have Count property, instead of using Count() method
Typically you'll need to iterate through each item if you're passing in any type of IEnumerable<>. Since you built a semi-complex model, you'll want to display foreach item in each list. Here is an example based from the ASP.NET MVC tutorials that I think would help you a bit:
#model IEnumerable<ContosoUniversity.Models.Course>
#{
ViewBag.Title = "Courses";
}
<h2>Courses</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CourseID)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Credits)
</th>
<th>
Department
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.CourseID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Credits)
</td>
<td>
#Html.DisplayFor(modelItem => item.Department.Name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) |
#Html.ActionLink("Details", "Details", new { id=item.CourseID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CourseID })
</td>
</tr>
}
</table>
Generally most people use an ICollection for their lists, where an example might be in an object:
public virtual ICollection<Post> Posts { get; set; }
Source:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
I would recommend starting at the beginning as it'll help you understand why you need to do this:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc

ASP MVC3 Error - There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key

I've researched this a bit and haven't found an answer that quite deals with a similar situation or MVC3. In the ViewModel I'm using I have a Lists of a separate model (List<AgentId> which is a list of the AgentId model).
In the Create page for this controller, I need an input section for 5 items to be added to this list. However, before the page even load, I receive this error message:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'BankListAgentId[0].StateCode'.
Here is the ViewModel I am using:
public class BankListViewModel
{
public int ID { get; set; }
public string ContentTypeID1 { get; set; }
public string CreatedBy { get; set; }
public string MANonresBizNY { get; set; }
public string LastChangeOperator { get; set; }
public Nullable<System.DateTime> LastChangeDate { get; set; }
public List<BankListAgentId> BankListAgentId { get; set; }
public List<BankListStateCode> BankListStateCode { get; set; }
}
And here is the section of the view that's having the issues:
<fieldset>
<legend>Stat(s) Fixed</legend>
<table>
<th>State Code</th>
<th>Agent ID</th>
<th></th>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[0].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[0].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[0].AgentId)
</td>
</tr>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[1].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[1].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[1].AgentId)
</td>
<td id="plus2" class="more" onclick="MoreCompanies('3');">+</td>
</tr>
</table>
</fieldset>
I believe #Html.DropDownListFor() is expecting an IEnumerable<SelectListItem>, you can bind it the following way:
In your ViewModel:
public class BankListViewModel
{
public string StateCode { get; set; }
[Display(Name = "State Code")]
public IEnumerable<SelectListItem> BankListStateCode { get; set; }
// ... other properties here
}
In your Controller load the data:
[HttpGet]
public ActionResult Create()
{
var model = new BankListViewModel()
{
// load the values from a datasource of your choice, this one here is manual ...
BankListStateCode = new List<SelectListItem>
{
new SelectListItem
{
Selected = false,
Text ="Oh well...",
Value="1"
}
}
};
return View("Create", model);
}
And then in the View bind it:
#Html.LabelFor(model => model.BankListStateCode)
#Html.DropDownListFor(model => model.StateCode, Model.BankListStateCode)
I hope this helps. Let me know if you nee clarifications.
This error wound up being thrown because the ViewBag element I was using had the same name as one of the list item properties.
The solution was to change ViewBag.StateCode to ViewBag.StateCodeList.

Resources