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>
}
Related
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);
}
}
I'm trying to access the properties of a class dynamically in ASP.NET Razor when generating an HTML Table. This problem is normally easily solved with reflection, but the #Html.DisplayFor method is giving me issues.
I am attempting to generate an HTML table that has 3 cells per row, with the title of the item in bold as the first line of the cell, and the value of the item in the second line of the cell. The contents of the table should not include cells which are on the 'Excluded Fields' list, and I do not want to have to statically reference each column.
<table class="blpSecurityTable">
<tr>
#{
int _rowCount = 0;
foreach (var property in item.GetType().GetProperties())
{
#if (!Model.ExcludedFields.Contains(#property.Name))
{
dynamic test = #property.GetValue(item);
<td><b>#Html.DisplayFor(m => #property.Name)</b><br />#Html.DisplayFor(m => #test)</td>
_rowCount++;
}
#if (_rowCount % numCols == 0)
{
#:</tr><tr>
}
}
}
</tr>
</table>
I've tried calling #Html.DisplayFor(m => #property.GetValue(item)) but that just creates a runtime error. I can simply call #property.GetValue(item) and the value displays, but this is not ideal because I use display templates to do things like set dates to the ShortDateString format.
I understand that DisplayFor is using reflection to determine the type of the property, and that is why I am trying to use the dynamic variable to facilitate reflection for the method. However, when I run the method, it throws errors indicating the variable is not a generic parameter, and therefore cannot share its attributes. The resulting page has mostly blank values, and some cells filled in with unexpected descriptive information.
I feel like I'm getting close, but I don't know how to proceed. The page won't look right if I don't pass the values into an HTML display method, and I cannot think of any other way to get the type of table I want to be generated. Thoughts?
The issue is solved by creating a Display Template for the Security object, which then allowed me to properly use the #Html.Display Method, because the Model for the Display Template has an entry for the property.
Here is what the page code looks like now:
Display Template
#model Interface.Models.Security
#{int numCols = 3;}
<table class="blpSecurityTable">
<tr>
#{
int _rowCount = 0;
foreach (var property in Model.GetType().GetProperties())
{
#if (!BLPDLModel.ExcludedFields.Contains(#property.Name))
{
<td><b>#Html.DisplayFor(m => #property.Name)</b><br />#Html.Display(property.Name)</td>
_rowCount++;
}
#if (_rowCount % numCols == 0)
{
#:</tr><tr>
}
}
}
</tr>
</table>
Razor Page
#foreach (var item in Model.Security)
{
<div class="blpSecurityItem">
<button type="button" class="collapsible">{button text}</button>
#{
<div class="collapsible-content">
<hr />
#Html.DisplayFor(m => item)
</div>
}
</div>
Replace #Html.DisplayFor(m => #test) with #Html.Display(property.Name)
and your model property for date should have [DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}")] or anything you like
I'm tinkering with a ASP.NET MVC 4 template, and I need guidance on how to design a complex view layout.
I already created a model that is getting data returned by a stored procedure in a SQL Server DB. A view is getting data from the model in an IEnumerable<> object. If you were to view the raw output of the stored procedure it would look something like this:
**Name** **Objects**
John Orange
John Banana
John Apple
I used a view template to create a simple table based on logic below, but as expected, it is rendered exactly like it is stored in the IEnumerable<> object. The name appears on each row of output in the table, like above.
#model IEnumerable<projectname.Models.ObjectsModel>
<table class="table">
...
...
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Objects)
</td>
...
Instead, I want to create a view layout that is not in table form, that would display the Name as a heading, the objects as a bulleted list, and other attributes elsewhere, with no redundant data. My problem is that if I display the items in the IEnumerable<> using #foreach (var item in Model) I end up getting three duplicate entries, since the values in the Name field are not unique.
What is the 'best practice' way to do this? Can someone please point me in the right direction?
Not sure what your controller looks like, or even your view model, but maybe i can point you in the right direction.
foreach is just that a loop of objects and should be used for just that, i have seen many people manipulate foreach loops with little or no success, You should be using a #for loop. besides The for-loop is approximately 2 times faster than the foreach-loop as it only has to call get_Item for every element in the list.
there are a few ways to acomplis what you want, one way would be what #colinD stated above by using LINQ in the controler or viewmodel to pass the proper data. the other way would be somthing similar to the following.
var models = ObjectsModel();
var grouped = models.GroupBy(s => s.ObjectsModel.Name)
.Select(x => x.Select(y => y))
.ToList();
return View(grouped);
#for(int i = 0; i < Model.Count; i++)
{
<h2>#Html.DisplayFor(model => model[i].First().ObjectsModel.Name)</h2>
<ul>
for(int j = 0; j < Model[i].Count; j++)
{
<li>#Html.DisplayFor(model => model[i][j].Object)</li>
}
</ul>
}
I haven't tested the code but I hope this helps you get the right solution you are looking for.
The main thing i'm trying to figure out is how to display this data
without having the #foreach (var item in Model) loop generate
duplicate entries for each row in the SP output.
Usually processing data is done in action, like groupping data, and then make some loop to display. As you want to avoid #foreach I thought using linq. This is just an idea but keep in mind you should separate concerns in MVC. I hope this helps ;)
IEnumerable<ObjectsModel> model = new List<ObjectsModel>()
{
new ObjectsModel(){ Name = "John", Objects = "Orange" },
new ObjectsModel(){ Name = "John", Objects = "Banana" },
new ObjectsModel(){ Name = "John", Objects = "Apple" }
};
var htmlModel = model
.GroupBy(a => a.Name)
.Select(a => new
{
Name = a.Key,
GroupObjects = string.Join("", a.Select(b => $"<li>{b.Objects}</li>"))
})
.Select(a => $"<h1>{a.Name}</h1><ul>{a.GroupObjects}</ul>")
.ToList();
var result = string.Join("", htmlModel); // <h1>John</h1><ul><li>Orange</li><li>Banana</li><li>Apple</li></ul>
Final result:
<h1>John</h1>
<ul>
<li>Orange</li>
<li>Banana</li>
<li>Apple</li>
</ul>
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.
I have this one in View:
<%
foreach (var item in (List<MyType>)ViewData["MyTypeArray"])
{
%><tr>
<td>
<%=Html.Encode(item.Name)%>
</td>
<td>
<%=Html.CheckBox("MyTypeFlags" + item.BitNumber),
/* Model goes here*/,
new {#value = (1 << item.BitNumber)})%> // html attr
</td>
</tr>
<%
}
%>
and I want do smth like this in Controller:
foreach (var item in MyDynamicallyCreatedArray)
{
//if (["MyTypeFlags" + item.BitNumber] != 0) // This shoud be changed
}
Question is how should I declare MyDynamicallyCreatedArray and go through the cycle?
You should look at using a "view model". You basically create "Models" just for your view that contain the data items you need in your view.
I use these quite often and they are really a great way of getting data in and out of your view.
For an example you can view here: http://stephenwalther.com/blog/archive/2009/04/13/asp.net-mvc-tip-50-ndash-create-view-models.aspx
Take a look at Phil Haack's post, it gets a bit tricker with checkboxes as if a box is unchecked then it doesnt submit a value.
Model Binding to a List
http://haacked.com/archive/0001/01/01/model-binding-to-a-list.aspx