I have a model like as follows:
public class Category
{
[Key]
public int Id { get; set; }
[StringLength(200), Required, DataType(DataType.Text)]
public string Title { get; set; }
[Display(Name = "Parent Category")]
public int? ParentId { get; set; }
[DisplayFormat(NullDisplayText = "Root")]
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Category> ChildCategories { get; set; }
}
Which is hierarchical in nature, 'ParentId' reference to 'Id' as self referencing. With this, I'm trying to display the Data as follows:
+--------+-----------------+
| S. No | Name |
+--------+-----------------+
| 1 | Parent One |
+--------+-----------------+
| 1.1 | Child-1 of One |
+--------+-----------------+
| 1.2 | Child-2 of One |
+--------+-----------------+
| 2 | Parent Two |
+--------+-----------------+
| 2.1 | Child-1 of Two |
+--------+-----------------+
| 3 | Parent Three |
+--------+-----------------+
Please help me on this.
You can count the Total Child Numbers using an group by parentId and then count
Here is my solution
Define an ViewModel
public class CategoryViewMoel
{
public IEnumerable<ParentCategoryInfo> parentCategoryInfo { get; set; }
public int SN { get; set; }
public int ChildSN { get; set; }
}
public class ParentCategoryInfo
{
public int? ParentId { get; set; }
public int TotalChild { get; set; }
}
In The Controller
var model = new CategoryViewMoel();
var parentCategory = from r in db.Categories
where r.ParentCategory != null
group r by r.ParentId into newGroup
select new ParentCategoryInfo
{
TotalChild = newGroup.Count(),
ParentId = newGroup.Key,
};
model.SN = 1;
model.ChildSN = 1;
model.parentCategoryInfo = parentCategory;
Lastly In your View
<table>
<thead>
<tr>
<th>S. No</th>
<th>Name</th>
</tr>
</thead>
<tbody>
#foreach (var parent in Model.parentCategoryInfo)
{
<tr>
<td>#Model.SN </td>
<td>Parent #Model.SN</td>
</tr>
for (var i = Model.ChildSN; i <= parent.TotalChild; i++)
{
<tr>
<td>#Model.SN.#i</td>
<td>Child - #i of #Model.SN</td>
</tr>
Model.ChildSN = 1;
Model.SN++;
}
</tbody>
The Mapping form numerical to words can be done by building an customized helper function to achieve this, there are plenty of examples in SO
converting numbers in to words C#
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 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..
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)
A collegaue of mine created a model and here it is.
Model
[Serializable]
public class ModifyCollegeListModel
{
public List<SchoolModel> CollegeList { get; set; }
public List<SchoolListModel> SchoolList { get; set; }
public string Notes { get; set; }
public int QuestionnaireId { get; set; }
}
[Serializable]
public class SchoolModel
{
public Guid SchoolId { get; set; }
public string SchoolName { get; set; }
public string StateName { get; set; }
public int DisplayIndex { get; set; }
public int DetailId { get; set; }
public int CategoryId { get; set; }
public int? ApplicationStatusId { get; set; }
}
I intend to create a loop that will generate the radiobutton list for the ApplicationStatusId , something like this...
Razor Code
#foreach (SchoolModel justright in Model.CollegeList.Where(m => m.CategoryId == 3).OrderBy(m => m.SchoolName).ToList<SchoolModel>())
{
<tr class="#HtmlHelpers.WriteIf(eventCounter % 2 == 0, "even", "odd")">
<td class="school"><b>#justright.SchoolName</b></td>
<td class="location"><b>#justright.StateName</b></td>
<td><label>#Html.RadioButtonFor(x => justright.SchoolId, (int)BrightHorizons.CC.BusinessLogic.CollegeListApplicationStatusEnum.DidNotApply)</label></td>
<td><label>#Html.RadioButtonFor(x => justright.SchoolId, (int)BrightHorizons.CC.BusinessLogic.CollegeListApplicationStatusEnum.Accepted)</label></td>
<td><label>#Html.RadioButtonFor(x => justright.SchoolId, (int)BrightHorizons.CC.BusinessLogic.CollegeListApplicationStatusEnum.NotAccepted)</label></td>
</tr>
}
but what happens is that ALL radiobhuttons created has the same name so they are grouped as one giant radiobutton collection. not via the schoolID... scratches head
Can someone help me here and point me to the right direction on how i will be able to create radio buttons that are grouped per row?
I would do two things.
First up, I would remove the filtering logic from the view. What I mean is this part:
Model.CollegeList.Where(m => m.CategoryId == 3).OrderBy(m => m.SchoolName).ToList<SchoolModel>()
That sort of logic belongs in a service. Also it will make the view much cleaner.
Secondly, I think you'll need to use a for-loop so MVC binds everything back how you want:
for (int i = 0; i < Model.CollegeList.Count; i++) {
<tr class="#HtmlHelpers.WriteIf(eventCounter % 2 == 0, "even", "odd")">
<td class="school"><b>#CollegeList[i].SchoolName</b></td>
<td class="location"><b>#CollegeList[i].StateName</b></td>
<td><label>#Html.RadioButtonFor(x => x.CollegeList[i].SchoolId, (int)BrightHorizons.CC.BusinessLogic.CollegeListApplicationStatusEnum.DidNotApply)</label></td>
<td><label>#Html.RadioButtonFor(x => x.CollegeList[i].SchoolId, (int)BrightHorizons.CC.BusinessLogic.CollegeListApplicationStatusEnum.Accepted)</label></td>
<td><label>#Html.RadioButtonFor(x => x.CollegeList[i].SchoolId, (int)BrightHorizons.CC.BusinessLogic.CollegeListApplicationStatusEnum.NotAccepted)</label></td>
</tr>
}
You'll notice after using the for-loop, that the radiobutton names and ID's also contain their index in the CollegeList. For example:
<input id="CollegeList_0__SchoolId" name="CollegeList[0].SchoolId" type="radio" value="2">
I’m building MVC3 “code first” application and to display data on my page I’m using the following:
Controller Code:
public ViewResult Index()
{
dynamic assignedT = from t in db.AssignedTasks
join a in db.Approvers on t.ApproverID equals a.ID
join r in db.Requestors on t.RequestorID equals r.ID
select new {Approver =a.FirstName + " " +a.LastName,
Requestor=r.FirstName + " " + r.LastName,
Title = t.Title,
t.RequestedDate,
t.CompletedDate, t.Description,
Status =(int)t.InternalStatut
};
return View(assignedT);
}
and on the page:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<table>
#foreach (var item in Model) {
<tr>
<td>#item.GetType().GetProperty("Title").GetValue(item, null)
</td>
</tr>
}
</table>
I know that using dynamic is not the best way.
How to achieve same functionality using strongly-typed view?
Thank you
You can define the following class:
public class MyViewModel
{
string Approver { get; set; }
string Requestor { get; set; }
string Title { get; set; }
DateTime RequestedDate { get; set; }
DateTime CompletedDate { get; set; }
string Description { get; set; }
int Status { get; set; }
}
If you then change select new in your Linq query to select new MyViewModel then you have a strongly typed viewmodel (and dynamic to var).