Getting index value on razor foreach - asp.net-mvc

I'm iterating a List<T> in a razor foreach loop in my view which renders a partial. In the partial I'm rendering a single record for which I want to have 4 in a row in my view. I have a css class for the two end columns so need to determine in the partial whether the call is the 1st or the 4th record. What is the best way of identifying this in my partial to output the correct code?
This is my main page which contains the loop:
#foreach (var myItem in Model.Members){
//if i = 1
<div class="grid_20">
<!-- Start Row -->
//is there someway to get in for i = 1 to 4 and pass to partial?
#Html.Partial("nameOfPartial", Model)
//if i = 4 then output below and reset i to 1
<div class="clear"></div>
<!-- End Row -->
</div>
}
I figure I can create a int that I can update on each pass and render the text no problem here but it's passing the integer value into my partial I'm more concerned about. Unless there's a better way.
Here is my partial:
#{
switch()
case 1:
<text>
<div class="grid_4 alpha">
</text>
break;
case 4:
<text>
<div class="grid_4 omega">
</text>
break;
default:
<text>
<div class="grid_4">
</text>
break;
}
<img src="Content/960-grid/spacer.gif" style="width:130px; height:160px; background-color:#fff; border:10px solid #d3d3d3;" />
<p>#Model.Name<br/>
#Model.Job<br/>
#Model.Location</p>
</div>
Not sure if I'm having a blonde day today and this is frightfully easy but I just can't think of the best way to pass the int value in. Hope someone can help.

#{int i = 0;}
#foreach(var myItem in Model.Members)
{
<span>#i</span>
i++;
}

//this gets you both the item (myItem.value) and its index (myItem.i)
#foreach (var myItem in Model.Members.Select((value,i) => new {i, value}))
{
<li>The index is #myItem.i and a value is #myItem.value.Name</li>
}
More info on my blog post
http://jimfrenette.com/2012/11/razor-foreach-loop-with-index/

Or you could simply do this:
#foreach(var myItem in Model.Members)
{
<span>#Model.Members.IndexOf(myItem)</span>
}

Take a look at this solution using Linq. His example is similar in that he needed different markup for every 3rd item.
foreach( var myItem in Model.Members.Select(x,i) => new {Member = x, Index = i){
...
}

You could also use deconstruction and tuples and try something like this:
#foreach (var (index, member) in #Model.Members.Select((member, i) => (i, member)))
{
<div>#index - #member.anyProperty</div>
if(index > 0 && index % 4 == 0) { // display clear div every 4 elements
#: <div class="clear"></div>
}
}
For more info you can have a look at this link

Is there a reason you're not using CSS selectors to style the first and last elements instead of trying to attach a custom class to them? Instead of styling based on alpha or omega, use first-child and last-child.
http://www.quirksmode.org/css/firstchild.html

IndexOf seems to be useful here.
#foreach (myItemClass ts in Model.ItemList.Where(x => x.Type == "something"))
{
int currentIndex = Model.ItemList.IndexOf(ts);
#Html.HiddenFor(x=>Model.ItemList[currentIndex].Type)
...

All of the above answers require logic in the view. Views should be dumb and contain as little logic as possible. Why not create properties in your view model that correspond to position in the list eg:
public int Position {get; set}
In your view model builder you set the position 1 through 4.
BUT .. there is even a cleaner way. Why not make the CSS class a property of your view model? So instead of the switch statement in your partial, you would just do this:
<div class="#Model.GridCSS">
Move the switch statement to your view model builder and populate the CSS class there.

Very Simple:
#{
int i = 0;
foreach (var item in Model)
{
<tr>
<td>#(i = i + 1)</td>`
</tr>
}
}`

In case you want to count the references from your model( ie: Client has Address as reference so you wanna count how many address would exists for a client) in a foreach loop at your view such as:
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DtCadastro)
</td>
<td style="width:50%">
#Html.DisplayFor(modelItem => item.DsLembrete)
</td>
<td>
#Html.DisplayFor(modelItem => item.DtLembrete)
</td>
<td>
#{
var contador = item.LembreteEnvolvido.Where(w => w.IdLembrete == item.IdLembrete).Count();
}
<button class="btn-link associado" data-id="#item.IdLembrete" data-path="/LembreteEnvolvido/Index/#item.IdLembrete"><i class="fas fa-search"></i> #contador</button>
<button class="btn-link associar" data-id="#item.IdLembrete" data-path="/LembreteEnvolvido/Create/#item.IdLembrete"><i class="fas fa-plus"></i></button>
</td>
<td class="text-right">
<button class="btn-link delete" data-id="#item.IdLembrete" data-path="/Lembretes/Delete/#item.IdLembrete">Excluir</button>
</td>
</tr>
}
do as coded:
#{ var contador = item.LembreteEnvolvido.Where(w => w.IdLembrete == item.IdLembrete).Count();}
and use it like this:
<button class="btn-link associado" data-id="#item.IdLembrete" data-path="/LembreteEnvolvido/Index/#item.IdLembrete"><i class="fas fa-search"></i> #contador</button>
ps: don't forget to add INCLUDE to that reference at you DbContext inside, for example, your Index action controller, in case this is an IEnumerable model.

I prefer to use this extension method:
public static class Extensions
{
public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)
=> self.Select((item, index) => (item, index));
}
Source:
https://stackoverflow.com/a/39997157/3850405
Razor:
#using Project.Shared.Helpers
#foreach (var (item, index) in collection.WithIndex())
{
<p>
Name: #item.Name Index: #index
</p>
}

Related

if condition in the razor syntax for HTML table- MVC

I have a HTML table with 2 rows and 4 columns. The data in each cell is coming from the stored proc. Currently, the last column displays number, I have an image to display, but it has to be displayed only if(rejected_question>0) else it should not display anything. How do I accomplish this in razor.
I have tried this so far, the below code doesn't work:
#foreach (var item in Model)
{
//logic for first three columns..
//fourth column here.
<td align="center">
if(#Html.DisplayFor(modelItem => item.RejectedQuestion) >0)
{
return Html.Raw(string.Format("<text><img height='3' width='3' src="\{0}\" alt="\Image\" /></text>", Url.Content("../../content/images/icon_red_questions_returnedbyreviewer.gif")));
}
</td>
}
This is what your looking for, probably:
#foreach (var item in Model)
{
//logic for first three columns..
//fourth column here.
<td align="center">
#if(item.RejectedQuestion > 0)
{
<img height="3" width="3" alt="Image"
src="#Url.Content("~/content/images/icon_red_questions_returnedbyreviewer.gif")" />
}
</td>
}
Make the line inside the if statement
#Html.Raw(string.Format("<text><img height='3' width='3' src="\{0}\" alt="\Image\" /></text>", Url.Content("../../content/images/icon_red_questions_returnedbyreviewer.gif")))
to display it. view.Execute() returns void, so you cannot include a return statement.
I tried this and it worked, just in case if someone needs it in future
:
#if(item.RejectedQuestion > 0)
{
<img height="20" width="20" alt="Image"
src="#Url.Content("~/content/images/icon_red_questions_returnedbyreviewer.gif")" />
}

Html Agility Pack asp.net mvc Xpath

I have this html:
Picture with html code
I need to show in my View : Бавария Майнц -2.25
I try write this:
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(
"https://www.parimatch.com/sport/futbol/germanija-bundesliga");
foreach (HtmlNode table in htmlDoc.DocumentNode.SelectNodes("//div[#id='oddsNote']/table/tbody"))
{
foreach (HtmlNode row in table.SelectNodes("tr"))
{
HtmlNodeCollection cells = row.SelectNodes("td");
if (cells == null)
{
continue;
}
foreach (HtmlNode cell in cells)
{
ViewBag.Results += cell.InnerText;
}
}
}
but my table always null. Where I have a mistake?
and what are the other options to output in View except Viewbag?
My HTML:
<div id ="z_container">
<div id = "Z_contentw">
<div id = "OddList">
<form name ="f1" id = "f1">
<div class = "container_grey">
<div class = "wrapper">
<table id = "4529899" class ="dt_twp">
<tbody class ="row1 processed">
<tr class ="bk">
<td> "02/03" <br> "21:00"</td>
<td class ="l"> <a class ="om" id ="a738">Bavaria Mainc</a></td>
<td> <b 3.5></td>
</tr>
</tbody>
<tbody class ="row2 processed">
<tr class ="bk">
<td> "03/03" <br> "19:00"</td>
<td class ="l"> <a class ="om" id ="a739">Roma Milan</a></td>
<td> <b 2.5></td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
</div>
</div>
I need to show: 02/03 21:00 Bavaria Mainc 03/03 19:00 Roma Milan
Your xpath has an id that is not in the html you provided other than that
If you want the text in one line as you showed then it can be
var text = string.Join(" ", doc.DocumentNode.SelectNodes("//tr[#class='bk']//text()[normalize-space()]").Select(t=>t.InnerText));
If you want to model the data then use the indices of the td, the first one has the time the second has the teams so create a simple model
class FootballMatch
{
public string Time;
public string Teams;
}
and get the data using the following
var matches = doc.DocumentNode.SelectNodes("//tr[#class='bk']").
Select(tr => new FootballMatch() {
Time = string.Join(" ", tr.SelectNodes("./td[1]//text()").Select(t => t.InnerText)),
Teams = tr.SelectSingleNode("./td[2]//text()[normalize-space()]").InnerText,
});

MVC foreach statement

Im new with MVC.
I have a model called UAV.
│Callsign│NumDeliveries│Mileage│MaxVelocity│MinVelocity│
 Hawk61   37    96    20     10
 BURL14   2047     57     30     15
 OTTO93   82    72    25     10
in cshtml file, i made a table only using Callsign, NumDeliveries, Mileage.
<table class="UAV_table" id="UAV_table">
<tr>
<th>Callsign</th>
<th>NumDeliveries</th>
<th>Mileage</th>
</tr>
#foreach (UAV uav in Model.UAVs)
{
<tr onclick="click_row()">
<td onclick="click_row()">
#Html.DisplayFor(modelItem => uav.Callsign)
</td>
<td>
#Html.DisplayFor(modelItem => uav.NumDeliveries)
</td>
<td>
#Html.DisplayFor(modelItem => uav.Mileage)
</td>
</tr>
}
</table>
 so the table shows all datas for Callsign, NumDeliveries, Mileage.
what i want to do is, when i click the row of the table, i want to see only that correspond information.
#foreach (UAVs uavid in Model.uavs)
{
<p class="detail_title" id="detail_title">
UAV: # (#Html.DisplayFor(modelItem => uavid.MaxVelocity))
</p>
}
for example, using above line of code, if i click first row of that table(callsign = Hawk61), i want to see like UAV: # 20 (MaxVelocity for Hawk61). MaxVelocity is not in the table, so i need to get it from database.
But I have problem with showing data. If i use right above code, it has #foreach statement, it shows all the Hawk61, BURL14, OTTO93's MaxVelocity.
it shows me like
UAV:# 20
UAV:# 30
UAV:# 25
I need to see only what i selected. (just shows what i click, in this example, only need to show UAV:# 20 which is first row, Hawk61's MaxVelocity).
is there any way to get the data from database not using foreach statement?
Thank you.
Since the values of MaxVelocityand MinVelocity are populated, you can make use of data- attributes to store the values in the DOM and use jquery to display them. For example
#foreach (UAV uav in Model.UAVs)
{
<tr class="uavrow" data-maxvelocity="#uav.MaxVelocity" data-minvelocity="#MinVelocity">
<td>#Html.DisplayFor(modelItem => uav.Callsign)</td>
<td>#Html.DisplayFor(modelItem => uav.NumDeliveries)</td>
<td>#Html.DisplayFor(modelItem => uav.Mileage)</td>
</tr>
}
And include some elements to display the associated data when you click on the row
<div>
<div><span>Call Sign: </span><span id="callsign"></span>
<div><span>Max Velocity: </span><span id="maxvelocity"></span>
<div><span>Min Velocity: </span><span id="minvelocity"></span>
</div>
And the script
$('.uavrow').click(function) {
// Get the call sign for the td element
$('#callsign').text($(this).children('td').eq(0).text());
// Get the velocity from the data attributes
$('#maxvelocity').text($(this).data('maxvelocity'));
$('#minvelocity').text($(this).data('minvelocity'));
});
If however the value were not populated, or you have a large number of properties to display, then it may be better to make an ajax call to a method (passing the callsign) which returns a partial view containing the details
<div id="uavdetails"></div>
$('.uavrow').click(function) {
var callSign = $('#callsign').text($(this).children('td').eq(0).text());
var url = '#Url.Action("Details", "YourController")';
$('#uavdetails').load(url, { CallSign: callsign });
});
Controller
public ActionResult Details(string CallSign)
{
UAV uav = // Get the UAV base on the CallSign value
return PartialView(uav);
}
Actually you have all data that you need in there.
The only thing that you need is to show proper item by using JavaScript.
You need to add parameter to your function call here:
<tr onclick="click_row('#uav.Callsign')">
And also add css class here:
#foreach (UAVs uavid in Model.uavs)
{
<p class="detail_title #uavid.Callsign" id="detail_title" style="display=none;">
UAV: # (#Html.DisplayFor(modelItem => uavid.MaxVelocity))
</p>
}
And then add a bit of javascript:
<script>
function click_row(elClass){
var elements = document.getElementsByClassName("detail_title");
for (i = 0; i < x.length; i++) {
if(x[i].className.contains(elClass)){
x[i].style.display = 'block';
} else{
x[i].style.display = 'none';
}
}
};
<script/>

How can I save the order a list of hidden form inputs which represent an array in ASP.NET MVC?

I have a sortable list of items which are generated from an array property on a model. I want to use jquery UI sortable to reorder the items and save this order in my Action method.
Here is the code I am using to output the items:
#using(#Html.BeginForm("Save", "MyController", FormMethod.Post))
{
<input type="submit" value="Save"/>
#Html.HiddenFor(x=>x.Id)
<ul id="sortable2" class="connectedSortable">
#{
int count = 0;
}
#foreach(var item in Model.CollectionOfThings)
{
<li class="ui-state-default">
#Html.Hidden("Things[" + count + "].Id", item.Id)
#Html.Hidden("Things[" + count + "].Title", item.Title)
#item.Title
</li>
count++;
}
</ul>
}
When I receive the model in my action method the order is the same as when I started. I understand that this is because of the index but I can't figure out a way (without writing some custom javascript to update the name attribute of each hidden input) to have the items bound to the array in correct order.
Can anyone suggest a way this could be achieved? I can probably write a bit of Javascript to do it but I hoped I could do this purely with model binding.
I tried outputting the inputs like this:
#Html.Hidden("Things[].Id", item.Id)
However this just resulted in my collection being null.
I have discovered a blog post from Phil Haack which contained the answer.
By using arbitrary indexes in a seperate hidden input with the name of my collection + .Index, the model binder seems to handle it and maintain the order.
#using(#Html.BeginForm("Save", "MyController", FormMethod.Post))
{
<input type="submit" value="Save"/>
#Html.HiddenFor(x=>x.Id)
<ul id="sortable2" class="connectedSortable">
#{
int count = 0;
}
#foreach(var item in Model.CollectionOfThings)
{
<li class="ui-state-default">
#Html.Hidden("Things.Index", "item" + count)
#Html.Hidden("Things[item" + count + "].Id", item.Id)
#Html.Hidden("Things[item" + count + "].Title", item.Title)
#item.Title
</li>
count++;
}
</ul>
}
Easier solution for you can be to collect only Ids. I assume titles can be recreated on server side. In this case you can just define:
#Html.Hidden("ThingIds", item.Id)
and bind to
List<int> thingsIds

How to change the separator for group data in formCollection in MVC view?

Suppose I have a form in view with dynamic row with fields bound to viewmodel like:
<%for (int i = 1; i <= 5; i++)
{ %>
<tr>
<td style="width: 100%">
<%= Html.TextBox("Link", Model.Websites[i.ToString()].Links)%>
</td>
.....
</tr>
<%} %>
When the form is posted, in FormCollection, all data coming from TextBox "Link" is a string separated by comma,. like, Yahoo.com, youtube.com, google.com
Then I can use comma to extract the data to array like:
string[] links = formdata["Link"].Split(',');
But question is: if the data use entered include comma, like "mysite, go.com", the I can get the right data for each item.
How to resolve this issue? is it possible to set special separator for string in FormCollection?
Use this
public ActionResult MyActionToHandleForm(string[] Link)
{
}
You should automatically get correct array. Read here for more.
Notice that you may also be able to do (name textbox "Link[i]")
<%for (int i = 1; i <= 5; i++)
{ %>
<tr>
<td style="width: 100%">
<%= Html.TextBox("Link[" + i + "]", Model.Websites[i.ToString()].Links)%>
</td>
.....
</tr>
<%} %>
with the same string[] Link action input parameter, but in this case you'll be sure to get correct strings for correct indices (even if, for example, you hide some of them).

Resources