display complex object properties in view asp.net - asp.net-mvc

If I have two models
class Person {
public string Name {get;set;}
public DateOfBirth DOB {get;set;}
}
class DateOfBirth {
public int Year {get;set;}
public int Month {get;set;}
public int Day {get;set;}
}
and a view that tries to display this information
#model Person
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.DOB.Year)
</td>
</tr>
What is the correct way of getting the values/information from the complex object "DateOfBirth"?
I'm currently trying this and the Year value is not being displayed, but the Name is being displayed because its just a string.
Thanks guys!

I think you're letting things get overly complicated with DisplayFor. You probably mean to do something like this:
#model IEnumerable<Person>
#foreach (var person in Model) {
<tr>
<td>
#person.Name
</td>
<td>
#person.DOB.Year
</td>
</tr>
When you start building a larger product, editor and display templates will allow you to get a lot of code reuse when you need to display basic information from your view models. But for now, that may be just a little more advanced than you need.

Related

Passing a partial model to a Display Template in ASP.Net MVC?

I have a view model derived from a base view model. The base VM has two properties, bool IsDisplayable & int MyValue.
I would like to create a display template that would take those two values and either display MyValue or not depending on the value of IsDisplayable.
This pair of values is part of all my view models and I would like to use one Display Template for all my views that use models derived from my base model.
My question is how would I pass this part of each view model to my Display Template?
#Html.DisplayFor(modelItem => item.MyValue,"MyDisplayTemplate")
My Display Template:
#model WhatModelGoesHere
#if (Model.IsDisplayable)
{
<td>
#Html.DisplayFor(model => model.MyValue)
</td>
}
The difficulty is trying to make the display template generic enough to work with any of my view models.
I suggest you create a common base class for other models to inherit.
Put the two fields IsDisplayable and MyValue that you need to display in the public template in the common base class, so that you only need to reference the common base class in the template.
And I suggest you use partial view to implement Display Template.
Common base class:
public class GenricModel
{
public bool IsDisplayable { get; set; }
public int MyValue { get; set; }
}
View Model to inhert common base class:
public class ViewModel1 : GenricModel
{
public int Id { get; set; }
public string Name { get; set; }
}
In the view to show ViewModel1 data, you can use #Html.Partial("_TemplateView", item) to call Display Template, here is an example based on ViewModel1:
#model IEnumerable<WebApplication_core.Models.ViewModel1>
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Index</h1>
<table class="table-bordered">
<tr>
<th>
Id
</th>
<th>
Name
</th>
<th>
MyValue
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#item.Id
</td>
<td>
#item.Name
</td>
#Html.Partial("_TemplateView", item)
</tr>
}
</table>
Then, create a partial view named _TemplateView.cshtml in Views folder.
Generally speaking, partial views are created under Views/Shared path in the project, you can refer to this.
#model WebApplication_core.Models.GenricModel
#if (Model.IsDisplayable)
{
<td>
#Html.DisplayFor(model => Model.MyValue)
</td>
}

How to display data from other tables in a view?

I'm using Database-First approach in MVC 4 web application using EF5. I have the following tables in my context:
**Employee** <br>
EmpId <br>
StatusId
One of the rows of the Employee table is :
EmpId=1<br>
StatusId=1
Options
OptionId<br>
OptionListName<br>
OptionName<br>
OptionValue<br>
One of the rows of the Options Table is as follows:
OptionId=1
OptionListName="Status"
OptionName="Active"
OptionValue=1
I created a view for my EmployeeController that displays some fields from Employee table as follows:
Employee
-----------
EmpId StatusId
1 1
However I need to view the table as follows:
Employee
-----------
EmpId Status
1 Active
How can I do this in MVC ?
You need a Viewmodel that takes IEnumerable<Employee> and if you only have one Options you need a Options. The Viewmodel will look like this:
public class YourNamedViewModel
{
public IEnumerable<Employee> Employees { get; set; }
public Options Options { get; set; }
}
Then in your view you change your model to
#using PathTo.ViewModel
#model YourNamedViewModel
<table>
<tr>
<th>
#Html.LabelFor(m => m.Employees.EmpId)
</th>
<th>
#Html.LabelFor(m => m.Options.OptionListName)
</th>
</tr>
#foreach (var item in Model.Employees)
{
<tr>
<td>
item.EmpId
</td>
#if (item.StatusId == 1)
{
<td>
<span>#Model.Options.OptionName</span>
</td>
}
else
{
<td>
<span>Inactive</span>
</td>
}
</tr>
}
</table>
If you have any other Status modes you need an IEnumerable<Options> in your View model and find the corresponding Options that has same OptionId as the Employee.
Haven't been able to test it, but it should work.

How to display a collection in View of ASP.NET MVC Razor project?

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.

View Models and Check Boxes in View

I have a newbie question, which I have tried to understand for the past few days. Hopefully someone can be kind enough to help me understand the programming flow.
Assuming I have a model, the information is stored in the database:
public class Student
{
public int studentID { get; set; }
public string studentName { get; set; }
public strin studentGrade {get; set; }
}
public class StudentDBContext : DbContext
{
public DbSet<Student> Students { get; set; }
}
and I want to display it into the view, with additional checkbox so I can select which students to be promoted into the next grade. I read that one way to do it is by putting into view model:
public class StudentViewModel
{
public bool promoted { get; set; }
public Student stu { get; set; }
}
But I am stuck on is this the way to do it? and if yes, how do you put into the view where it will display all the students with a checkbox next to it. Afterwards, I want to update all the grade for the students whose checkboxes are ticked. For example:
Student A, Student B, Student D promoted from Grade 1 to Grade 2. So I want to display the students, tick Student A, B and D and submit to update the Grade.
Step by step example will be much appreciated.
Update 1:
Controller:
[HttpGet]
public ViewResult CheckBox()
{
var studentViewModels = db.Students.Select(m => new StudentViewModel()
{
stu = m
}).ToList();
return View(studentViewModels);
}
[HttpPost]
public ActionResult CheckBox(IList<studentViewModel> list)
{
foreach (var stuUpdate in list.Where(m => m.promoted))
{
var stuRow = db.Students.Find(stuUpdate.stu.studentID);
stuRow.studentName = stuRow.studentName + "1";
db.Entry(stuRow).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("CheckBox");
}
return RedirectToAction("CheckBox");
}
View:
#model IList<School.ViewModels.StudentViewModel>
#using (Html.BeginForm())
{
<table>
<tr>
<th>
</th>
<th>
student ID
</th>
<th>
student name
</th>
<th>
student grade
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.CheckBoxFor(modelItem => item.promoted)
#Html.HiddenFor(modelItem => item.stu.studentID)
</td>
<td>
#Html.DisplayFor(modelItem => item.stu.studentID)
</td>
<td>
#Html.DisplayFor(modelItem => item.stu.studentName)
</td>
<td>
#Html.DisplayFor(modelItem => item.stu.studentGrade)
</td>
</tr>
}
</table>
<input type="submit" value="save" />
}
However currently hit by the following error:
Value cannot be null.
Parameter name: source
Source Error:
foreach (var stuUpdate in list.Where(m => m.promoted))
A very basic "step by step" (done in SO, so I probably did a few mistakes, but you've got the idea).
You have always a few ways to do these kind of things, so... just really take it as a sample, and find other examples to get other ideas.
well, first, in your controller, you will have a GET action (to see the list).
[HttpGet]
public ViewResult StudentList() {
//retrieve all students. With the Select, we create a new instance of StudentViewModel for each student.
//assuming StudentDbContext being a property of your controller, if it's not, you can instantiate a new one (in a using clause)
var studentViewModels = StudentDbContext.Students
.Select(m => new StudentViewModel() {
stu = m
//we don't say nothing about promoted :
//it will be there as "false" by default, which is probably what we want.
}).ToList();
//now we've got a list of StudentViewModel. This will be the model of our view
return View(studentViewModels);
}
Then we've got a view, StudentList.cshtml
in this view, we will display a table, with a line for each student : the studentId (hidden in this case), the name (display only), the grade (display only), and a checkbox.
We need a for loop (not a foreach) to get fine model binding.
#model IList<StudentViewModel>
#using (Html.BeginForm()) {
<table>
<tr>
<th>Student name</th>
<th>Student grade</th>
<th>Promote</th>
</tr>
#for (var i = 0; i < Model.Count(); i++) {
<tr>
<td>#Html.HiddenFor(m => Model[i].Student.studentID)
#Html.DisplayFor(m => Model[i].Student.studentName)
</td>
<td>#Html.DisplayFor(m => Model[i]Student.studentGrade)</td>
<td>#Html.CheckBoxFor(m => Model[i].promoted)</td>
</tr>
}
</table>
<input type="submit" value="save" />
}
This form will lead to another POST action (same name as the GET one, depending of what you have in your Html.BeginForm)
[HttpPost]
public ActionResult StudentList(IList<StudentViewModel> list) {
//we treat only the lines where checkbox has been checked
foreach (var studentViewModel in list.Where(m => m.promoted) {
var student = StudentDBContext.GetById(studentViewModel.Student.studentID);//get student entity instance from context
student.studentGrade ="<new value>";
StudentDBContext.SaveChanges();//save changes, you must have such a method somewhere.
}
return Action("StudentList");
}
Little detail :
Try to respect some really basic "usual" practices : for example in c#, Properties should begin by an uppercase letter (so StudentGrade, StudentName, Promoted, etc).

MVC 3 - Passing list of check boxes to and from a controller

I have a list of information sources in a database that I need to pass to a view in MVC. I need an end user to be able to tick the sources of information that apply to their course.
I am able to successfully pass the view a list of information sources alongside check boxes using the following code.
public ViewResult CreateUpdateInfoSource(int ProgrammeId)
{
List<ProgInfoSourceModel> viewmodel = new List<ProgInfoSourceModel>();
List<ProgInfoSourceDTO> myProgInfoDTOList = progInfoSourceService.AllInfoSources();
if (myProgInfoDTOList.Count != 0)
{
foreach (var x in myProgInfoDTOList)
{
ProgInfoSourceModel insert = new ProgInfoSourceModel();
insert.Selected = false;
insert.ProgrammeId = ProgrammeId;
insert.InfoSourceId = x.InfoSourceId;
insert.InfoSource = x.InfoSource;
insert.InfoReference = x.InfoReference;
insert.Rank = x.Rank;
viewmodel.Add(insert);
}
}
return View(viewmodel);
}
I am able to unpack this in the view just fine, however I am having real difficulty passing a the list back to my controller. I need to be able to loop through the list in my controller and see which ones do or don't apply so I can update the database.
My model looks like this:
namespace ProgrammeSpec.MVC.Models
{
public class ProgInfoSourceModel
{
[DisplayName("Selected")]
public bool Selected { get; set; }
[DisplayName("Programme Id")]
public int ProgrammeId { get; set; }
[DisplayName("Info Source Id")]
public int InfoSourceId { get; set; }
[DisplayName("Info Source")]
public String InfoSource { get; set; }
[DisplayName("Reference")]
public String InfoReference { get; set; }
[DisplayName("Rank")]
public int? Rank { get; set; }
}
}
My View looks like this:
<html>
<head>
<title>CreateUpdateInfoSource</title>
</head>
<body>
#using (Html.BeginForm())
{
<table>
<tr>
<th>
Selected
</th>
<th>
ProgrammeId
</th>
<th>
InfoSourceId
</th>
<th>
InfoSource
</th>
<th>
InfoReference
</th>
<th>
Rank
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.CheckBoxFor(modelItem => item.Selected)
</td>
<td>
#Html.DisplayFor(modelItem => item.ProgrammeId)
</td>
<td>
#Html.DisplayFor(modelItem => item.InfoSourceId)
</td>
<td>
#Html.DisplayFor(modelItem => item.InfoSource)
</td>
<td>
#Html.DisplayFor(modelItem => item.InfoReference)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rank)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
<input type="submit" value="Update" />
}
</body>
</html>
and the controller that the view gets passed to looks like this: (snippet)
[HttpPost]
public ActionResult CreateUpdateInfoSource(List<ProgInfoSourceModel> viewmodel)
{
if (ModelState.IsValid)
{
try
{
The problem is the viewmodel is null. I understand this is probably because I've unpacked the list in the view so it is no longer a list but how can I access the values of the check boxes then?
The added complication is that the number of info sources will vary so I can't use a static form or list and give each one an Id...
This must be a fairly common problem with a simple solution, but I'm an MVC novice and I don't know how to get round this.
Try using an editor template (Here's another SO that answers that question How to create custom editor/display templates in ASP.NET MVC 3?), for your ProgInfoSourceModel and then simply use Html.EditorFor(m => m.Model) on the View.
When you use the foreach loop, each checkbox is getting the same input name - and so is not actually submitting the correct data back.
If you go the editor template route, and making MVC do the hard work of iterating through the IEnumerable - it will create inputs with names like 'item[0].Selected' - which the model binder then correctly deserialized back into a list.
Phil Haack also blogged a fantastic walkthrough of exactly this scenario way back in 2008: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Resources