Proper way of binding a dictionary to a cshtml table - c#

My story is: I'm working in a asp.net MVC project. I have two Model's list, reprezented like this:
private Dictionary<string, List<Tuple<string, string>>> _foldersName_NoRecords = new Dictionary<string, List<Tuple<string, string>>>();
private List<string> _totalNoOfDates = new List<string>();
after I fill those lists(as I've seen after debug the fill goes perfectly), I pass them to a ViewBag for accessing them in the view. The problem is that the lists are not displayed in the order they are filled in and are rather overridden.
The code from the view is:
<thead>
<tr>
<th>
Folder Name
#foreach(var date in #ViewBag.dateList)
{
<td>#date</td>
}
</th>
</tr>
</thead>
<tbody>
#foreach(var tupleKey in #ViewBag.statisticsList.Keys)
{
<tr>
<td> #tupleKey </td>
#foreach (var tupleItem in #ViewBag.statisticsList[tupleKey])
{
foreach (var date in #ViewBag.dateList)
{
if (tupleItem.Item1 == date)
{
<td>#tupleItem.Item2</td>
}
}
}
</tr>
}
</tbody>
for example: #tupleItem.Item2 is not displayed to the date that it belongs when a new key is added(the data stored from the lists is extracted from txt files). Anyone have any idea of better displaying this data?

Since Tuple has many overloaded options, why not have a third DateTime parameter that you can use to link the contents of the Tuple to the contents of the date? If you want to keep the lists separate, ensure the ordering on loading the dictionary/list is the same; sounds like something is getting out of order.

Related

C# ASP.NET Core 5.0 MVC : validate a list of models created programmatically

Overview
I'm working on a project where I ingest a CSV file, where each record results in the creation of a model in the back end. After ingestion, I am able to create a list of models, and I can return the list of models back to the view with no issue.
However, I want to do custom validation on each model in the list and return the validation results to the view. In this example, I have custom model validation that checks if the email already exists via a database check. When I am creating a model one at a time through a Form on a view, everything goes well. All the validation is run, and there are no issues.
Problem
The issue is when I programmatically create List<Person>(), and then try to validate each Person model in the list. What ends up happening is all the errors are aggregated and when I return the list to the view and display in table via foreach(var p in Model.PersonList), with #Html.ValidationSummary(), then each table row shows all the errors of all the models, not just the errors that pertain to that PersonModel.
Within a foreach loop, I call TryValidateModel() and follow it with ModelState.Clear(), but this does not help. I've also tried using ModelState.Clear() at the beginning of the foreach loop, again no help.
Simplified code of what I'm doing and where I'm stuck.
Controller code - root of problem is here in the foreach loop:
[HttpPost]
public IActionResult BulkModelCreation(BulkUpload inputCSV)
{
// No issue here, I successfully convert CSV to data table. Each Record represents a model
// If there are 10 records in the CSV, then I will have a list of 10 People
DataTable dt = CSVHelper.CSVToDataTable(inputCSV.InputFile);
List<Person> people = new List<Person>();
if (dt != null)
{
// My issues is located here, I think
// In the foreach loop, I read each row from the DataTable, and create a new Person Model
// I want to validate each PersonModel and then add Person to people list
foreach (DataRow dr in dt.Rows)
{
// Create new person
Person p = new Person();
p.FirstName = dr["FirstName"].ToString();
p.LastName = dr["LastName"].ToString();
// More model attributes
p.Email = dr["Email"].ToString();
// Validate the model, and the validation works
TryValidateModel(p);
people.add(p);
// This is where I am unsure of what to do
//To try and reset for the next PersonModel in the List, but this does not work.
// If any person in the list has a duplicate email, then all items in the list will show
// as having a duplicate email.
ModelState.Clear();
}
}
// Other stuff, create model to be returned that has an attribute that is a List<People>
ModelToReturn ModelReturn = new ModelToReturn();
ModelReturn.Persons = people;
// Obj is returned, and data is populated. No issue here
return View(ModelReturn)
}
Simplified model class:
public class PersonModel
{
[Required]
public string FirstName {get;set;}
[Required]
public string LastName {get;set;}
// More stuff
//Custom Valiation
[EmailValidation]
public string Email {get;set;]
}
Custom validation:
public class EmailValidationAttribute: ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var PersonModel = (PersonModel)validationContext.ObjectInstance;
// Passing in Person Repository service so that I can check the db if email already exists
var service = (IPersonRepository)validationContext.GetService(typeof(IPersonRepository));
bool isDuplicate = service.CheckDuplicate(PersonModel.Email);
if (isDuplicate)
{
return new ValidationResult("Email Already Exists");
}
else
{
return ValidationResult.Success;
}
}
}
Simplified view:
<table class="table">
<thead>
<tr>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">More Stuff</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
#foreach(var p in Model.Persons)
{
<tr>
<td>
<input asp-for="FirstName" class="form-control" />
</td>
<td>
<input asp-for="LastName" class="form-control" />
</td>
<td>MORE STUFF</td>
<td>
<input asp-for="Email" class="form-control" />
#Html.ValidationSummary()<!-- if one person has a duplicate email, then this will say 'Duplicate Email' for every single record in Model.Persons-->
</td>
</tr>
}
</tbody>
</table>
Note, I have reviewed and tried what was suggested in this post: Validate list of models programmatically in ASP.NET MVC
But that does not work, either for the OP, and myself. Any help with this would be greatly appreciated.

How do I select a table row and grab data from it in razor?

I am new to coding, so sorry in advanced if this is simple.
I have a table that is populated through a foreach loop, iterating through a list of my model's type, which contains all of my properties. This is how my table body looks:
<tbody>
#foreach (var row in Model.myModel)
{
<tr>
<td>#row.Year</td>
<td>#row.ID</td>
<td>#row.User</td>
</tr>
}
</tbody>
There is a way that I want to do this but I am not sure how. I want to be able to click on the row and return the index number, so then I can just get the value straight from the list directly from the list. Is there a way to do this? Or should I be going about this another way?
I am working on a asp.net core web application btw. This is in my Index.cshtml file and so far I only have some code in my Index.cshtml.cs file to get the data from sql server into the list I am using to fill this table.
You can try this:
<tbody>
#foreach (var row in myModel)
{
<tr #onclick="#(() => Edit(#row.ID))">
<td>#row.Year</td>
<td>#row.ID</td>
<td>#row.User</td>
</tr>
}
</tbody>
and add this below:
#code {
private List<Model> myModel = new List<Model>();
private void Edit(int ID)
{
Console.WriteLine("You clicked" + ID);
}
}

One Column values will be changed when header DropdownListFor is getting changed - in mvc razor view c#

new to this type of work , need your help
in my view .cshtml ----
<table class>
<thead>
<tr>
<th >#Html.CheckBox("IsAllRowSelected")</th>
<th >
#Html.DropDownListFor(m => m._dropDownForcolumn2, new List<SelectListItem>
{ new SelectListItem{Text="option1", Value="option1"},
new SelectListItem{Text="option2", Value="option2"},
new SelectListItem{Text="option3", Value="option3"}
}, new {#id="dropDownForcolumn2" })
</th>
<th>#Html.Label(" column 3 ")</th>
<th>#Html.Label("column 4")</th>
<th>#Html.Label("column 5")</th>
</tr>
</thead>
<tbody>
#foreach (Models.MyModelClass item in Model._List)
{
<tr>
<td>#Html.CheckBox("IsEachRowSelected")</td>
<td>#item.Option1Values</td>
#*//#item.option2values;
#item.option3vlaues;*#
<td>#item.column3value</td>
<td>#item.column4value</td>
<td>#item.column5value</td>
</tr>
}
1 .cant post back to controller again to get only this column values . its just a small table in a huge page
2 i already have the other values in item
now only option1 values are coming in the column , the requirement is to bind 2nd colmn with header dropdown and 2nd option selected then this will show #item.option2values and 3rd option selected then #item.option3values will be shown
other columns will not be changed or touched .
somthing like this
<td>
if(dropdownvalue = option1)
#item.Option1Values
elseif(dropdownvalue == option2 )
#item.option2values
elseif(dropdownvalue == option2 )
#item.option3vlaues
</td>
ajax , jquery is allowed but whole page post or partial view post is not allowed
do it like this
put onload method in table tag
function load()
{
$('.MyProperty2').hide();
$('.MyProperty3').hide();
}
function Filldropdown(){
var value = $('#dropdownID :selected').text();
if (value == 'option1') {
$('.MyProperty1').show();
$('.MyProperty2').hide();
$('.MyProperty3').hide();
} else if (value == 'option2') {
$('.MyProperty2').show();
$('.MyProperty1').hide();
$('.MyProperty3').hide();
} else if (value == 'option3') {
$('.MyProperty3').show();
$('.MyProperty2').hide();
$('.MyProperty1').hide();
}
}

Get IQueryable object items in ASPNET MVC Razor View

Source is a not strongly type IQueryable returned by a Dynamic Linq Select.
As a result in View I have:
As they are not strongly typed I cannot manage to access (and correctly display) objects code and name_en (please note the reason I am using a dynamic linq select is column names vary from select to select).
I tried to access values by their index, item[0], item[1] but I get a Cannot apply indexing with [] to an expression of type 'object'error.
You could use property names if you already know them like so
item.GetType().GetProperty("code").GetValue(item);
Or just use more reflection and render all properties this object have.
#foreach (var item in Model)
{
<tr>
#{
var type = item.GetType();
var props = type.GetProperties();
foreach (var p in props)
{
<td>
#p.Name
</td>
<td>
#type.GetProperty(p.Name).GetValue(item);
</td>
}
}
</tr>
}
Of course you can loop through all properties and then just check and exclude some properties, like Id or other private data, which you would not like to render.
If you want to keep your Model class as is, you may just use dynamic objects in your view:
#foreach (dynamic item in Model.languageTable)
{
<tr>
<td>#item.code</td>
<td>#item.name_en</td>
</tr>
}

Displaying data from ViewBag into table?

I'm trying to retrieve a list of file info for .dll files from a specific directory to display on a ASP.NET webpage. The info I want displayed is the file name, the date it was last modified and the version.
So far I have the data being stored in a ViewBag and being displayed in the view, however it's messy and I want it to be displayed in a table.
Is there a way to take data from a ViewBag and place it in a table or is there better way than using the ViewBag?
This is the code I have for the View so far:
#using System.Diagnostics
#{
ViewBag.Title = "Versions";
}
<h2>Versions</h2></br>
<h3>File Name Last Modified Version</h3>
#ViewBag.FileList
#for(int i =0; i < ViewBag.FileList.Length;i++)
{
<p>
#ViewBag.FileList[i];
#{ FileInfo f = new FileInfo(ViewBag.FileList[i]);
<table>
<td><tr>#f.Name</tr></td>
<td><tr> #f.LastAccessTime</tr></td>
</table>
FileVersionInfo currentVersion = FileVersionInfo.GetVersionInfo(ViewBag.FileList[i]);
#currentVersion.FileVersion
}
</p>
}
ViewBag should not be exploited this way. Use a View Model
In your controller's action pass the data like this,
public ActionResult Files()
{
List<FileInfo> fileNames = ...get file names
return View(fileNames);
}
In your view,
Right at the top, define the type of object
#model IEnumerable<System.IO.FileInfo>
Your table should be layed out in a way similar to the below.
<table>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>#item.Name</td>
<td>#item.LastAccessTime</td>
</tr>
}
</tbody>
</table>

Categories