I am using a razor view for showing the scored of an examination and it contains an enum value that holds 3 values "pass","Fail","absent" and i want to choose it accordingly. The model i used is
model
public class VerifyResultModel
{
[Display(Name = "StudnetId")]
public int StudentId { get; set; }
[Display(Name = "Student")]
[Required]
public string Student { get; set; }
[Display(Name = "Mark")]
[Required]
public int Mark { get; set; }
[Display(Name = "Score")]
[Required]
public string Score { get; set; }
[Display(Name = "Result")]
public App.EnumValues.ExamResultStatus Result { get; set; }
}
Controller
[HttpGet]
public ActionResult Verify(int Id)
{
List<VerifyResultModel> model_result = new List<VerifyResultModel>();
VerifyResultModel _resultItem;
foreach (exammark item in marks)
{
SchoolApp.EnumValues.ExamResultStatus result = SchoolApp.EnumValues.ExamResultStatus.Absent;
if(item.Mark >= MinMark)
{
result= SchoolApp.EnumValues.ExamResultStatus.Pass;
}
else
{
result = App.EnumValues.ExamResultStatus.Fail;
}
_resultItem = new VerifyResultModel { StudentId = (int)item.StudentId, Student = item.studentmaster.StudentName, Mark = (int)item.Mark, Score = item.Mark.ToString(), Result = result };
model_result.Add(_resultItem);
}
LoadResultsDropdown();
return View(model_result);
}
private void LoadResultsDropdown()
{
var types = (from App.EnumValues.ExamResultStatus type in Enum.GetValues(typeof(SchoolApp.EnumValues.ExamResultStatus))
select new { Id = type.ToString(), Name = type.ToString() }).ToList();
ViewBag.ResultList = new SelectList(types, "Id", "Name");
}
View
#model IList<SchoolApp.ViewModels.VerifyResultModel>
<tbody>
#for (int item = 0; item < Model.Count(); item++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model[item].Student)
</td>
<td>
#Html.DisplayFor(modelItem => Model[item].Mark)
</td>*#
<td>
#Html.DisplayFor(modelItem => Model[item].Score)
</td>
<td>
#Html.DropDownListFor(model => model[item].Result, (SelectList)ViewBag.ResultList) //Here All values are showing as Pass ( first item in dropdown)
</td>
</tr>
}
</tbody>
The problem is even if i pass values Fail / Absent to enum it shows as Pass in combobox . How can i show the correct value ?
Can you please try this:
private void LoadResultsDropdown()
{
var types = (from App.EnumValues.ExamResultStatus type in Enum.GetValues(typeof(SchoolApp.EnumValues.ExamResultStatus))
select new { Id = type.ToString(), Name = type.ToString() }).ToList();
var types=Enum.GetValues(typeof(ExamResultStatus)).Cast<ExamResultStatus>();
var EnumData= types.Select(c => new { c.Key, c.Value });
ViewBag.ResultList = new SelectList(types.AsEnumerable(), "key", "value");
}
Hope this helps..
Related
ASP.NET Core 6 MVC / EF model-first
Is there simple analogue of #Html.DropDownList and ViewBag to replace IDs from one model to values from another model?
I have got two models:
Model1
public int ID { get; set; } // PK
public int PersonID { get; set; } // FK
ID
PersonID
1
3
2
4
Model2
public int PersonID { get; set; } // PK
public int Name { get; set; }
PersonID
Name
3
Nick
4
James
Controller:
public async Task<IActionResult> Index()
{
return View(await _context.Model1.ToListAsync());
}
View:
#model IEnumerable<wpg.monster.Models.Model1>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.ID)
</td>
<td>
#Html.DisplayFor(modelItem => item.PersonID)
</td>
</tr>
}
So I'll get
TRHead
TRHead
1
3
2
4
Is there any trick to replace 3 and 4 with Names from Model2, like ViewBag with #Html.DropDownList to see something like this in View? Or there is only one way to create ViewModel?
TRHead
TRHead
1
Nick
2
James
Here a possible method for you:
First create a model3 to collect the information from model1 and model2:
public class Model1
{
public int ID { get; set; }
public int PersonId { get; set; }
}
public class Model2
{
public int PersonId { get; set; }
public string Name { get; set; }
}
public class Model3
{
public int ID { get; set; }
public string Name { get; set; }
}
Then in your controller, you can select the data you want and insert them to the new model which will be displayed on the viewer, here you can combine two elements whose PersonId are the same to the new model3:
public List<Model3> GetModel3()
{
List<Model1> model1 = new();
List<Model2> model2 = new();
List<Model3> model3 = new();
model1.Add(new Model1 { ID = 1, PersonId = 3 });
model1.Add(new Model1 { ID = 2, PersonId = 4 });
model2.Add(new Model2 { PersonId = 3, Name = "Nick" });
model2.Add(new Model2 { PersonId = 4, Name = "James" });
foreach (Model1 m1 in model1)
{
foreach (Model2 m2 in model2)
{
if(m1.PersonId == m2.PersonId)
{ model3.Add(new Model3 { ID = m1.ID, Name = m2.Name }); }
}
}
return model3;
}
Then you should use the following method in controller to pass the new model to the viewer:
public IActionResult Test()
{
ViewBag.Message = "This is test demo";
dynamic model = new ExpandoObject();
model.Model3 = GetModel3();
return View(model);
}
Last is the viewer page:
#using Test;
#using Test.Models;
#model dynamic
#{
ViewBag.Title = "Test";
ViewData["Title"] = "Test";
}
<h2>#ViewBag.Message</h2>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
#foreach (Model3 model3 in Model.Model3)
{
<tr>
<td>#model3.ID</td>
<td>#model3.Name</td>
</tr>
}
</table>
Now you may solve your issue, here is my test to it:
I am using Paging in my View and originally my #model was of type PagedList.IPagedList but I was having issues posting back the IPagedList interface back to controller, therefore, I found a post that gave a hint on how to deconstruct my IPagedList so I could present it in the View and be able to post back to controller for further processing as well. However, I ran into the following issue - I want to display only 50 records on each page. The search criteria I am testing returns 13000 records. I am expecting only 50 records on first page with page numbers 1,2,3 at bottom. I see the page numbers as expected at the bottom but all 13000 records get displayed on every page. I have debugged the code and found out that StaticPagedList function is returning 13000 records instead of 50 so I applied Skip and Take on the object before returning it to the View but the issue is still occurring. My code:
View Models -
public class PagedClientViewModel
{
public int? Page { get; set; }
public IEnumerable<SelectTimeTrackerEditorViewModel> Clients { get; set; }
public IPagedList PagingMetaData { get; set; }
}
public class SelectTimeTrackerEditorViewModel
{
public bool Selected { get; set; }
public int SID { get; set; }
public string PerNo { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
View -
#model EmergencyResponseTimeEntry.Models.PagedClientViewModel
#using PagedList;
#using PagedList.Mvc;
#foreach (var item in Model.Clients)
{
<tr>
<td>
#Html.CheckBoxFor(modelItem => item.Selected)
</td>
<td>
#Html.DisplayFor(modelItem => item.PerNo)
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
</tr>
}
Page #(Model.PagingMetaData.PageCount < Model.PagingMetaData.PageNumber ? 0 : Model.PagingMetaData.PageNumber) of #Model.PagingMetaData.PageCount
#Html.PagedListPager(new StaticPagedList<EmergencyResponseTimeEntry.Models.SelectTimeTrackerEditorViewModel>(Model.Clients, Model.PagingMetaData), page => Url.Action("Index", new { page }), new PagedListRenderOptions { DisplayEllipsesWhenNotShowingAllPageNumbers = false})
Controller -
var list = (from c in timeTrackers
select new SelectTimeTrackerEditorViewModel
{
Selected = false,
SID = c.SID,
PerNo = c.PerNo,
FirstName = c.FirstName,
LastName = c.LastName
});
var pagedClientViewModel = new PagedClientViewModel
{
Clients = list
};
int pageSize = 50;
int pageNumber = (page ?? 1);
int totalClients = list.Count();
// List of current page of 50 records (hits database again, pulls only 50 records, though)
var timeTrackerList = list.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
pagedClientViewModel.PagingMetaData = new StaticPagedList<SelectTimeTrackerEditorViewModel>
(timeTrackerList, pageNumber, pageSize, totalClients).GetMetaData();
return View(pagedClientViewModel);
I got it working after co-worker pointed out that I was displaying all records in my view - Model.Clients. So in my controller, I changed this
var pagedClientViewModel = new PagedClientViewModel
{
Clients = list
};
to this -
var pagedClientViewModel = new PagedClientViewModel
{
Clients = list.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList()
};
Now paging is working as expected!
I have a viewmodel to use in the Create view:
ViewModel
public class ReportViewModel
{
public int ID { get; set; }
[Display(Name = "Platform")]
public string Platform { get; set; }
[Display(Name = "Logo")]
public HttpPostedFileBase Logo { get; set; }
}
Create View
#model HPRWT.ViewModels.ReportViewModel
#using (Html.BeginForm("Create", "Report", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="editor-label">
#Html.LabelFor(model => model.Platform )
#Html.EditorFor(model => model.Platform )
#Html.ValidationMessageFor(model => model.Platform)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Logo)
<input type="file" id="Logo" name="Logo" />
</div>
}
This work perfect. But now I need an array of checkboxes (7x24) to get free hours (7 day, 24 hours). I have an array of ids (i need a defined id because I use jquery). In Create view:
#for(int i = 1; i < labels.Length; i++)
{
<tr>
<td>#labels[i][0]</td>#for(int j = 1; j < labels[i].Length; j++)
{
<td><div><input type="checkbox" id="#ids[i][j]" /><label for="#ids[i][j]"></label></div></td>
}
My ids are like R02C00 (R of row + numer of row with 2 digits + C (column) + number of column (2 digits). I generate them with:
for (int i = 1; i < 8; i++)
for (int j = 1; j < 25; j++)
ids[i][j] = "R" + i.ToString("00") + "C" + (j-1).ToString("00");
This also works well. Now my problem is how I get the checkboxes values.
Controller
[HttpPost]
public ActionResult Create(ReportViewModel rvm)
{
if (ModelState.IsValid)
{
rdb.Reports.Add(CreateReport(rvm));
rdb.SaveChanges();
return RedirectToAction("Index");
}
return View(rvm);
}
// Create a report from a reportviewmodel
private Report CreateReport(ReportViewModel rvm)
{
Report report = new Report();
// Platform
string platform = rvm.Platform;
report.Platform = platform ;
// Logo
HttpPostedFileBase inputFile = rvm.InputFile; // Some code to get the path
return report;
}
How can I get the checkboxes values? If I add in reportviewmodel a bool[][], is there is any way to do a #Html.Checkbox? (If I have to change ids names in jquery, I donĀ“t mind, is not mandatory to have a id like R01C01... only the ids in jquery be the same as html)
Why don't you use a view model?
public class FreeHourViewModel
{
public string Label { get; set; }
public bool Selected { get; set; }
}
public class ReportViewModel
{
public ReportViewModel()
{
this.FreeHours = Enumerable
.Range(1, 7)
.Select(day =>
Enumerable.Range(1, 24).Select(hour => new FreeHourViewModel
{
Label = string.Format("day: {0}, hour: {1}", day, hour)
}
).ToArray()
).ToArray();
}
public int ID { get; set; }
[Display(Name = "Platform")]
public string Platform { get; set; }
[Display(Name = "Logo")]
public HttpPostedFileBase Logo { get; set; }
public FreeHourViewModel[][] FreeHours { get; set; }
}
and then:
#for (int i = 0; i < Model.FreeHours.Length; i++)
{
for (int j = 0; j < Model.FreeHours[i].Length; j++)
{
#Html.LabelFor(x => x.FreeHours[i][j].Selected, Model.FreeHours[i][j].Label)
#Html.CheckBoxFor(x => x.FreeHours[i][j].Selected)
}
}
And when the form is submitted, since you have used a real view model, model binding will work as expected. Also you don't need any jQuery to generate those checkboxes. Strongly typed helpers such as Html.CheckBoxFor is the way to go.
How about having a strongly-typed view in relation to your checkboxes?
public class ReportViewModel
{
public ReportViewModel()
{
Days = new List<Day>();
}
public int ID { get; set; }
[Display(Name = "Platform")]
public string Platform { get; set; }
[Display(Name = "Logo")]
public HttpPostedFileBase Logo { get; set; }
public IList<Day> Days { get; set; }
}
public class Hour
{
public int Id { get; set; }
public bool Selected { get; set; }
}
public class Day
{
public Day()
{
Hours = new List<Hour>();
}
public int Id { get; set; }
public bool Selected { get; set; }
public IList<Hour> Hours { get; set; }
}
You then return a view like this:
foreach (var d in Enum.GetValues(typeof(DayOfWeek)))
{
var day = new Day { Id = (int)d };
for (var i = 0; i < 25; i++)
{
day.Hours.Add(new Hour { Id = i });
}
model.Days.Add(day);
}
return View(model);
And add this to your view:
for (var i = 0; i < 7; i++)
{
<div id="days">
<ul>
#for (var j = 0; j < 24; j++)
{
<li>#Html.CheckBoxFor(m=>Model.Days[i].Hours[j].Selected)</li>
}
</ul>
</div>
}
Now when you receive your input you have an array of bool values where you have a sort of Ids, that is 0-6 for days and 0-23 for hours. You can easily determine what hour in what particular day was selected.
Of course, you have to fill in the missing pieces with my suggestion like showing the labels for each checkboxes.
I have a ViewModel in: RolesMVC3.Area.Asesor.Models.ListNotesViewModel
is the following:
public class ListNotesViewModel
{
public decimal IdTime { get; set; }
public decimal IdArea { get; set; }
public decimal IdCriterion { get; set; }
public decimal Notes { get; set; }
public decimal IdEstudent { get; set; }
}
I have a controller that uses this ViewModel.
is the following:
public ActionResult EstudentsQualification()
{
var newItems = (from n in db.Qualification
join a in db.AREA on n.IdArea equals a.IdArea
join e in db.ESTUDENT on n.IdEstudent equals e.IdEstudent
join p in db.TIME on n.IdTime equals p.IdTime
join c in db.CRITERION on n.IdCriterion equals c.IdCriterion
where n.IdArea == 1
select new ListNotesViewModel { IdCriterion = c.IdCriterion, IdTime = p.IdTime, Notes=n.Note, IdEstudent==e.IdEstudent }).ToList();
var estu = (from n in db.Qualification
join e in db.ESTUDENT on n.IdEstudent equals e.IdEstudent
where n.IdArea == 1
select e).Distinct().ToList();
ViewBag.Estudents = estu;
ViewBag.Time = db.TIME;
ViewBag.Criterion = db.CRITERION;
ViewBag.Notes = newItems;
return View();
}
the associated view is:
#{
ViewBag.Title = "Index";
}
<table border="1">
#foreach (var item4 in ViewBag.Estudents)
{
<tr>
<td>
#item4.CodEstudents - #item4.NameEstudents
</td>
#foreach (var item2 in ViewBag.Time)
{
foreach (var item3 in ViewBag.Criterion)
{
<td>
#if (ViewBag.Notes.IdCriterion == item3.IdCriterion && ViewBag.Notes.IdTime == item2.IdTime && ViewBag.Notes.IdEstudent == item4.IdEstudent)
{
#ViewBag.Notes.Note
}
else
{
#:nothing
}
</td>
}
}
</tr>
}
</table>
I get the following error:
'System.Collections.Generic.List<RolesMVC3.Area.Asesor.Models.ListNotesViewModel>' does not contain a definition for 'IdCriterion'
I can't figure out what the problem is.
Think this line caused it ViewBag.Notes.IdCriterion
#if (ViewBag.Notes.IdCriterion == item3.IdCriterion && ViewBag.Notes.IdTime == item2.IdTime && ViewBag.Notes.IdEstudent == item4.IdEstudent)
ViewBag.Notes is a collection of Note. You need to access item in Notes e.g. ViewBag.Notes[0].IdCriterion or ViewBag.Notes[i].IdCriterion or foreach(noteItem in ViewBag.Notes)
I am new to MVC3, so apologies if this is basic, but I couldn't work it out.
I have a view model that includes 3 tables, an 'Albums' table with 2 foreign keys (Artist ID and Label ID).
I have a 'Labels' controller and a Details method where I display the Label table fields. I am trying to get the distinct 'Artists' from the collection of Albums related to the Label.
At the moment, I can get the name of the artists, but one is generated for each album - I have included the code for this scenario below.
I have tried a bunch of different things, like including Artists in the collection and using the Distinct and group by functions, but to no avail. Not sure if it is doable this way, or whether, due to the indirect relationship between the tables, I need to use a different approach.
Any helps is much appreciated.
Controller:
public ActionResult Details(int id)
{
var viewModel = new LabelsDetailsVM();
viewModel.Lables = db.Labels
.Include(a => a.Albums)
.SingleOrDefault(x => x.LabelID == id);
return View(viewModel);
View:
#foreach (var artist in Model.Lables.Albums)
{
<tr>
<td>
#Html.DisplayFor(model => artist.Artist.ArtistName)
</td>
</tr>
}
View Model
public class LabelsDetailsVM
{
public Label Lables { get; set; }
public IEnumerable<Album> Albums { get; set; }
public IEnumerable<Artist> Artists { get; set; }
}
}
Here's an example that might get you on the right track:
view model:
public class LabelViewModel
{
public Label Label { get; set; }
public IEnumerable<Artist> Artists { get; set; }
}
Controller:
public class LabelController : Controller
{
public ActionResult Details(int id)
{
var label = db.Labels
.Include(l => l.Albums)
.SingleOrDefault(l => l.LabelID == id);
if (label == null)
{
return HttpNotFound();
}
var distinctArtists = label
.Albums
.Select(a => a.Artist)
.Distinct(ArtistComparer.Default);
var model = new LabelViewModel
{
Label = label,
Artists = distinctArtists
};
return View(model);
}
}
and the equality comparer used to distinguish between 2 artists used in the controller that could of course be adapted to match your requirements. In this example it considers that 2 artists represent the same entity if they have the same ID. But you could work with some other properties such as the name and so on, all depends on what you need:
public class ArtistComparer : IEqualityComparer<Artist>
{
protected ArtistComparer()
{
}
private static readonly IEqualityComparer<Artist> _default = new ArtistComparer();
public static IEqualityComparer<Artist> Default
{
get
{
return _default;
}
}
public bool Equals(Artist x, Artist y)
{
if (x != null && y != null)
{
return x.ArtistID.Equals(y.ArtistID);
}
return false;
}
public int GetHashCode(Artist obj)
{
return obj.ArtistID.GetHashCode();
}
}
View:
#model LabelViewModel
<h3>#Html.DisplayFor(x => x.Label.LabelName)</h3>
<div>Artists</div>
<table>
<thead>
<tr>
<th>artist name</th>
</tr>
</thead>
<tbody>
#foreach (var artist in Model.Artists)
{
<tr>
<td>#artist.ArtistName</td>
</tr>
}
</tbody>
</table>