I have a model Course, a model Component and a model Evaluation:
public class Course
{
public virtual int CourseId { get; set; }
public virtual string Name { get; set; }
public virtual List<Component> Components { get; set; }
public virtual List<UserProfile> Users { get; set; }
public virtual List<Evaluation> Evaluation { get; set; }
}
public class Component
{
public virtual int ComponentId { get; set; }
public virtual int CourseId { get; set; }
public virtual Course Course { get; set; }
public virtual string NameComp { get; set; }
}
public class Evaluation
{
public virtual int EvaluationId { get; set; }
public virtual int CourseId { get; set; }
public virtual Course Course { get; set; }
public virtual int UserId { get; set; }
public virtual UserProfile User { get; set; }
public virtual int Grade { get; set; }
}
I need to show in a view a table with all the users, all the components created and the grade for each one.
I tried this way:
#model SGP.Models.Course
<table>
<tr>
<th>
Username
</th>
#foreach (var x in Model.Components)
{
<th>
#Html.DisplayFor(modelItem => x.NameComp)
</th>
}
<th></th>
</tr>
#foreach (var item in Model.Evaluation)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.User.UserName)
</td>
#foreach (var x in Model.Components)
{
<td>
#Html.DisplayFor(modelItem => item.Grade)
</td>
}
<td>
</td>
</tr>
}
But this gives me all the components in one column, and i need one column for each component, and a grade for each one:
The code i have gives this:
Username - Component1Component2Component3
Test - 12
Test - 13
Test - 10
(example of result with username and grades)
And i need:
Username - Component1 - Component2 - Component3
Test - 12 - 13 - 10
(example of result with username and grades)
How can i do this? Thanks
The definition of Evaluations in Course should be:
public virtual IGrouping<string, Evaluation> Evaluations { get; set; }
The code should be:
#model SGP.Models.Course
<table>
<tr>
<th>
Username
</th>
<th>
#foreach (var x in Model.Components)
{
#Html.DisplayFor(modelItem => x.NameComp)
}
</th>
<th></th>
</tr>
#foreach (var group in Model.Evaluations)
{
<tr>
<td>
#Html.DisplayFor(modelItem => group.Key)
</td>
#foreach (var item in group)
{
<td>
#Html.DisplayFor(modelItem => item.Grade)
</td>
}
<td>
</td>
}
</tr>
You need to make sure the evaluation is in reference to a course component:
public class Evaluation
{
public virtual int EvaluationId { get; set; }
public virtual int CourseId { get; set; }
public virtual Course Course { get; set; }
public virtual int ComponentId { get; set; }
public virtual Component Component { get; set; }
public virtual int UserId { get; set; }
public virtual UserProfile User { get; set; }
public virtual int Grade { get; set; }
}
Here are some test values:
var users = new List<UserProfile>
{
new UserProfile { UserId = 1, UserName = "Jim" },
new UserProfile { UserId = 2, UserName = "Sam" },
};
var components = new List<Component>
{
new Component { ComponentId = 1, NameComp = "Unit 1" },
new Component { ComponentId = 2, NameComp = "Unit 2" }
};
var course = new Course
{
Users = users,
Components = components.OrderBy(c => c.ComponentId).ToList(),
Evaluation = new List<Evaluation>
{
new Evaluation { User = users[0], ComponentId = components[0].ComponentId, Grade = 87 },
new Evaluation { User = users[0], ComponentId = components[1].ComponentId, Grade = 99 },
new Evaluation { User = users[1], ComponentId = components[0].ComponentId, Grade = 75 },
new Evaluation { User = users[1], ComponentId = components[1].ComponentId, Grade = 65 }
}
};
And following Razor view should do what you need:
#model SGP.Models.Course
<table>
<tr>
<th>
Username
</th>
#foreach (var x in Model.Components)
{
<th>
#Html.DisplayFor(modelItem => x.NameComp)
</th>
}
<th></th>
</tr>
#foreach (var userEvaluation in Model.Evaluation.GroupBy(e => e.User))
{
<tr>
<td>
#Html.DisplayFor(modelItem => userEvaluation.Key.UserName)
</td>
#foreach (var x in Model.Components)
{
<td>
#(userEvaluation.FirstOrDefault(e => e.ComponentId == x.ComponentId)?.Grade)
</td>
}
<td></td>
</tr>
}
Watch out for this model, it has a lot of circular references, which can be problematic if you're using something like Entity Framework.
Related
So I'm building a web app for tracking holidays across a company that has different sites. I need to display list of employees on my admin page but I only want Managers/Admins to view the employee list related to them based on their sites (they may have one or many).
On my Admin page, I have created a table of employees from my database, which displays all employees and links to their details on each row.
Controller :
public ActionResult Index(int? page, string searchString)
{
var employees = db.Employees.Include(e => e.Area).Include(e => e.Discipline).Include(e => e.Shift).Include(e => e.Site).Include(e => e.UserRole);
return View(employees.ToList().ToPagedList(page ?? 1, 10 ));
}
Model:
public partial class Employee
public Employee()
{
this.HolidayRequestForms = new HashSet<HolidayRequestForm>();
}
public int EmployeeID { get; set; }
public string FullName { get; set; }
[Required(ErrorMessage = "This Field is Required")]
public string EmailID { get; set; }
[Required(ErrorMessage = "This Field is Required")]
public string Password { get; set; }
public System.DateTime StartDate { get; set; }
public int RoleID { get; set; }
public int ShiftID { get; set; }
public int AreaID { get; set; }
public int DisciplineID { get; set; }
public int SiteID { get; set; }
public int ALCategory { get; set; }
public Nullable<int> HoursTaken { get; set; }
public Nullable<int> AwardedLeave { get; set; }
public Nullable<int> TotalHoursThisYear { get; set; }
public int HoursCarriedForward { get; set; }
public Nullable<int> EntitlementRemainingThisYear { get; set; }
public string Comments { get; set; }
public int SickLeaveTaken { get; set; }
public Nullable<int> SickLeaveEntitlement { get; set; }
public Nullable<int> SickLeaveEntitlementRemaining { get; set; }
public int StudyLeaveEntitlement { get; set; }
public int StudyLeaveTaken { get; set; }
public Nullable<int> StudyLeaveRemaining { get; set; }
public int ExamLeaveTaken { get; set; }
public int ForceMajeure { get; set; }
public int BereavementLeaveTaken { get; set; }
public int MaternityLeaveTaken { get; set; }
public int ParentalLeaveTaken { get; set; }
public int AdoptionLeaveTaken { get; set; }
public string LoginErrorMessage { get; set; }
public virtual Area Area { get; set; }
public virtual Discipline Discipline { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<HolidayRequestForm> HolidayRequestForms { get; set; }
public virtual Shift Shift { get; set; }
public virtual Site Site { get; set; }
public virtual UserRole UserRole { get; set; }
}
And View (just the table section):
<table class="table table-striped">
<tr>
<th>
Name
</th>
<th>
Start Date
</th>
<th>
Area
</th>
<th>
Discipline
</th>
<th>
Site Name
</th>
<th>
AL Category
</th>
<th>
Hours CF
</th>
<th>
Awarded Leave
</th>
<th>
Total Hours This Year
</th>
<th>
Hours Taken
</th>
<th>
Hours Remaining
</th>
<th>
Role
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FullName)
</td>
<td>
#Html.DisplayFor(modelItem => item.StartDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Area.Area1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Discipline.Discipline1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Site.SiteName)
<td>
#Html.DisplayFor(modelItem => item.ALCategory)
</td>
<td>
#Html.DisplayFor(modelItem => item.HoursCarriedForward)
</td>
<td>
#Html.DisplayFor(modelItem => item.AwardedLeave)
</td>
<td>
#Html.DisplayFor(modelItem => item.TotalHoursThisYear)
</td>
<td>
#Html.DisplayFor(modelItem => item.HoursTaken)
</td>
<td>
#Html.DisplayFor(modelItem => item.EntitlementRemainingThisYear)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserRole.RoleName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.EmployeeID }, new { #class = "btn-xs btn-warning glyphicon glyphicon-pencil" })
#Html.ActionLink("Details", "Details", new { id = item.EmployeeID }, new { #class = "btn-xs btn-info glyphicon glyphicon-info-sign" })
#Html.ActionLink("Delete", "Delete", new { id = item.EmployeeID }, new { #class = "btn-xs btn-danger glyphicon glyphicon-trash" })
</td>
</tr>
}
</table>
#Html.PagedListPager(Model, page => Url.Action("Index", new { page }))
I'd like it so that only managers/Admins assigned to certain sites could only view those sites and none else. Regular Users don't have access to this page.
Is this possible to do in mvc??
I'm trying to make my list to show data from another column instead of its ID, currently they are showing like so:
This is what I work out so far:
Controller:
public class CompanyAdmin
{
public string Id { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Company Name")]
public string CompanyName { get; set; }
public virtual List<Certificate> Certificates { get; set; }
public class Certificate
{
[Key]
[Display(Name = "Certificate No.")]
public string CertNo { get; set; }
[Display(Name = "Company")]
public IEnumerable<SelectListItem> Companies { get; set; }
[Required]
public string Company { get; set; }
[Display(Name = "User")]
public IEnumerable<SelectListItem> UserList { get; set; }
[Required]
public string User { get; set; }
public string UploadedBy { get; set; }
[Display(Name = "Description")]
public string CertDescription { get; set; }
[Display(Name = "File Location")]
public string CertFileLocation { get; set; }
[Display(Name = "Last Modified")]
public DateTime LastModifiedDate { get; set; }
}
public class CertificateViewModel
{
[Display(Name = "Certificate No.")]
public string CertNo { get; set; }
public string User { get; set; }
[Display(Name = "User")]
public string UserName { get; set; }
public string Company { get; set; }
[Display(Name = "Company")]
public string CompanyName { get; set; }
[Display(Name = "Description")]
public string CertDescription { get; set; }
[Display(Name = "Last Modified")]
public DateTime LastModifiedDate { get; set; }
}
Here is my controller:
public ActionResult Index()
{
if (User.IsInRole("Admin"))
{
IEnumerable<CertificateViewModel> model = null;
model = (from c in db.CertificateDB
join u in db.Users on c.User equals u.Id
join d in db.CompanyDB on c.Company equals d.Id
select new CertificateViewModel
{
CertNo = c.CertNo,
UserName = u.UserName,
CompanyName = d.CompanyName,
CertDescription = c.CertDescription,
LastModifiedDate = c.LastModifiedDate
}
);
return View(model);
}
else
{
return RedirectToAction("Index", "Home");
}
}
and the view:
#model IEnumerable<IdentitySample.Models.CertificateViewModel>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CertNo)
</th>
<th>
#Html.DisplayNameFor(model => model.Company)
</th>
<th>
#Html.DisplayNameFor(model => model.User)
</th>
<th>
#Html.DisplayNameFor(model => model.CertDescription)
</th>
<th>
#Html.DisplayNameFor(model => model.LastModifiedDate)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.CertNo)
</td>
<td>
#Html.DisplayFor(modelItem => item.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.User)
</td>
<td>
#Html.DisplayFor(modelItem => item.CertDescription)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastModifiedDate)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.CertNo }) |
#Html.ActionLink("Details", "Details", new { id = item.CertNo }) |
#Html.ActionLink("Delete", "Delete", new { id = item.CertNo })
</td>
</tr>
}
</table>
My Context:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
static ApplicationDbContext()
{
// Set the database intializer which is run once during application start
// This seeds the database with admin user credentials and admin role
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
string errorMessages = string.Join("; ", ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.PropertyName + ": " + x.ErrorMessage));
throw new DbEntityValidationException(errorMessages);
}
}
public DbSet<CompanyAdmin> CompanyDB { get; set; }
public DbSet<Certificate> CertificateDB { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
But the results keep coming up empty for the 2 join columns, please see screenshot here
Any idea what I've done wrong?
In your query you populate property CompanyName, but in your view you render property Company. Same goes for User in view vs UserName in a query.
Also I recommend adding .ToList() just before you pass the model to your view - this will make sure data is extracted from DB into objects before it is passed to your view:
model = (from c in db.CertificateDB
join u in db.Users on c.User equals u.Id
join d in db.CompanyDB on c.Company equals d.Id
select new CertificateViewModel
{
CertNo = c.CertNo,
UserName = u.UserName,
CompanyName = d.CompanyName,
CertDescription = c.CertDescription,
LastModifiedDate = c.LastModifiedDate
}
).ToList();
You are setting UserName and CompanyName property for your CertificateViewModel in controller action:
UserName = u.UserName,
CompanyName = d.CompanyName,
while in View you are using Company and User property:
<td>
#Html.DisplayFor(modelItem => item.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.User)
</td>
So you either need to set the Company and User property of your view model in controller or use CompanyName and UserName for rendering in View, if you change the view, change it to use the correct properties :
<td>
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
Controller:
List<EmployeeSkill> emp = new List<EmployeeSkill>();
public ActionResult viewskills(EmployeeSkill objskillset)
{
emp .Add(objskillset);
return PartialView("_SkillListPartial", objskillset);
}
Model:
public int EmployeeID { get; set; }
[Required(ErrorMessage = "Please Enter Skill")]
public string SkillName { get; set; }
[Display(Name = "Experience (In Months)")]
[Required(ErrorMessage = "Please Enter Experience(In Months)")]
public Nullable<byte> ExperienceInMonths { get; set; }
public string CreatedBy { get; set; }
public Nullable<System.DateTime> CreatedDate { get; set; }
public string ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public Nullable<bool> IsActive { get; set; }
public virtual EmployeeDetail EmployeeDetail { get; set; }
View:
model IEnumerable<CISSEC.Models.EmployeeSkill>
<table class="table">
<tr>
<th> #Html.DisplayNameFor(model => model.SkillName) </th>
<th> #Html.DisplayNameFor(model => model.ExperienceInMonths)</th>
</tr>
#foreach (var item in Model) {
<tr>
<td> #Html.DisplayFor(modelItem => item.SkillName) </td>
<td> #Html.DisplayFor(modelItem => item.ExperienceInMonths) </td>
</tr>
}
</table>
Your list is not posted back because you do not render <input> elements for the properties of the list items. Use HiddenFor to render the inputs. Use a for loop so the modelbinder receives the correct index of the list items.
#for (var i = 0; i < Model.Count(); i++) {
<tr>
<td> #Html.HiddenFor(m => m[i].SkillName) </td>
<td> #Html.HiddenFor(m=> m[i].ExperienceInMonths) </td>
</tr>
}
I have a model course, component, subcomponent and evaluation:
public class Course
{
public virtual int CourseId { get; set; }
(...)
public virtual List<Component> Components { get; set; }
public virtual List<UserCourse> Users { get; set; }
}
public class Component
{
public virtual int ComponentId { get; set; }
public virtual string Type { get; set; }
public virtual string Name { get; set; }
public virtual Course Course { get; set; }
public virtual List<Subcomponent> Subcomponents { get; set; }
}
public class Subcomponent
{
public virtual int SubcomponentId { get; set; }
public virtual int ComponentId { get; set; }
public virtual string TypeSub { get; set; }
public virtual string NameSub { get; set; }
(...)
public virtual int ParentId { get; set; }
public virtual Subcomponent Parent { get; set; }
public virtual List<Subcomponent> Childs { get; set; }
public virtual Component Component { get; set; }
}
public class Evaluation
{
public virtual int EvaluationId { get; set; }
public virtual int ComponentId { get; set; }
public virtual Component Component { get; set; }
public virtual int UserCourseId { get; set; }
public virtual UserCourse User { get; set; }
public virtual int Grade { get; set; }
}
I need to give a grade to each subcomponent (or component if the component doesn't have subcomponents) to each user.
First i have a view that shows the users and the grades already given, like an index:
#model SGP.Models.Course
<table>
<tr>
<th>
Users
</th>
<th>
Grades
</th>
</tr>
#foreach (var x in Model.Users)
{
<tr>
<td>
#Html.DisplayFor(modelItem => x.User.UserName)
</td>
#foreach (var y in x.Evaluation)
{
<td>
<table>
<tr>
<td>
<b>#Html.DisplayFor(modelItem => y.Component.NameComp)</b>
</td>
</tr>
<tr>
<td>
#Html.DisplayFor(modelItem => y.Grade)
</td>
</tr>
</table>
</td>
}
<td>
#Html.ActionLink("Give grade", "Comps", "Evaluation", new { id = x.UserCourseId }, null)
</td>
</tr>
}
</table>
Then when press "Give grade", i have a search box to select the component wich the grade will be given.
The search will give a list of subcomponents of that component. Each component can have one or more subcomponents so the final grade to the component is the average of the subcomponents grades.
The view of the subcomponents:
#model SGP.Models.Component
<table>
<tr>
<th>
#Html.DisplayFor(model => model.NameComp)
</th>
<th>
Grade
</th>
</tr>
#foreach (var item in Model.Subcomponents) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.NameSub)
</td>
<td>
???????
</td>
</tr>
}
</table>
But in this view i need to get the UserCourseId previously selected and give a grade to the user in the selected subcomponent. How can i do that?
Thanks
EDIT:
Give grade:
#using (Html.BeginForm("ListaSubs", "Evaluation", FormMethod.Post))
{
<table>
<tr>
<th>
#Html.TextBox("text")
</th>
<td>
<input type="submit" id="ButtonSearch" value="Search" />
</td>
</tr>
</table>
}
And controller:
[HttpPost, ActionName("ListaSubs")]
public ActionResult ListaSubs(string text)
{
var util = (db.Subcomponents.Where(m =>
m.ComponentId.Equals(text)));
return View("view", util);
}
What I do in this case, is to define a wrapper class and use it for view's model. Something like this:
public class MyWrapperClass
{
public Course _Course { get; set; }
public Component _Component { get; set; }
...
}
Then in your view, you define your model as:
#model MyWrapperClass
For some controls you can simply use the following format for the properties:
#Html.HiddenFor(model => model._Course.CourseId)
But in some cases, you probably have to create EditorTemplate or DisplayTemplate for each subclass and use the templates in the view.
To use Editor template, you need to use a UIHint attribute in the class definition.
Here is some links on how to use editor template in razor:
How to add and use a razor editor template
https://www.simple-talk.com/dotnet/asp-net/extending-editor-templates-for-asp-net-mvc/
I have an entity called Fixture, and basically I want to display some fixtures on the page.
My Model is as follows :-
public class Fixture
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FixtureId { get; set; }
[Display(Name = "Fixture Date")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? FixtureDate { get; set; }
public DateTime? FixtureTime { get; set; }
public int? CityId { get; set; }
public City City { get; set; }
public int CountryIdA { get; set; }
public int CountryIdB { get; set; }
public int? ScoreA { get; set; }
public int? ScoreB { get; set; }
public Round Round { get; set; }
public int? RoundId { get; set; }
public List<Scorer> Scorers { get; set; }
}
and my ViewModel is as follows :-
public class FixtureViewModel
{
public Fixture Fixture { get; set; }
public IEnumerable<Fixture> FixtureList { get; set; }
public IEnumerable<GroupCountry> GroupCountryList { get; set; }
public IEnumerable<Group> GroupList { get; set; }
public string CountryNameA { get; set; }
public string CountryNameB { get; set; }
}
and I am displaying them in this index page :-
<table>
<tr>
<th>
#Html.DisplayName("Date")
</th>
<th>
#Html.DisplayName("Country A")
</th>
<th>
#Html.DisplayName("Country A")
</th>
<th>
#Html.DisplayName("Score A")
</th>
<th>
#Html.DisplayName("Score B")
</th>
<th>
#Html.DisplayName("Round")
</th>
<th></th>
</tr>
#foreach (Fixture item in Model.FixtureList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FixtureDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.CountryIdA)
</td>
<td>
#Html.DisplayFor(modelItem => item.CountryIdB)
</td>
<td>
#Html.DisplayFor(modelItem => item.ScoreA)
</td>
<td>
#Html.DisplayFor(modelItem => item.ScoreB)
</td>
<td>
#Html.DisplayFor(modelItem => item.Round.RoundName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new {id = item.FixtureId}) |
#Html.ActionLink("Details", "Details", new {id = item.FixtureId}) |
#Html.ActionLink("Delete", "Delete", new {id = item.FixtureId})
</td>
</tr>
}
</table>
Now in the index page, I wish to display something like this :-
<td>
#Html.DisplayFor(modelItem => item.CountryIdA.Country.CountryName)
</td>
<td>
#Html.DisplayFor(modelItem => item.CountryIdB.Country.CountryName)
</td>
How can I achieve that?
I added a navigation item Country in the Model, however I still cannot display the correct CountryName.
Thanks for your help and time
Well CountryIdA and CountryIdB are just Integers. If you are trying to say...
Find a fixture with CountryIdA/B, then get it's Country, and then CountryName
then this is not appropriate to put in a view. Your view should not handle any data access, that is the job of your controller/model. I would suggest adding two Country properties to your view model, one for IdA and one for IdB. Then you can populate these in your controller action and have access to them in your view.
Your controller might look something like...
ViewModel.CountryA = Countries.Where(x => x.CountryID = ViewModel.CountryIdA)
You can extend Fixture in your model by creating a partial class Fixture in the same namespace, create two custom properties CountryIdA/B that does a lookup for the currect IDs.