I am pulling data from a Warehouse database and displaying on a webpage.
This pulls all data.
#foreach (WarehouseCategoryModel categoryModel in Model.Categories)
{
<tr>
<td>#Html.ActionLink(categoryModel.Name, "Category", "-WarehouseCatalog", new { ide = categoryModel.ID }, null)</td>
<td>#categoryModel.Count</td>
</tr>
}
I need to display each category where the Count is greater than 0.
I tried:
if(#categoryModel.Count > 0)
{
<td>#categoryModel.Count</td>
}
You could explicitly filter out the items that you are iterating through using a Where() clause :
<!-- Only iterate through those with Counts greater than 0 -->
#foreach (WarehouseCategoryModel categoryModel in Model.Categories.Where(c => c.Count > 0))
{
<!-- Do work here -->
}
If you wanted to handle scenarios where your Model or Categories were possibly null, you could add the following explicit check :
<!-- Ensure you have categories to loop through (avoids null exceptions) -->
#if(Model?.Categories != null)
{
<!-- Only iterate through those with Counts greater than 0 -->
foreach (WarehouseCategoryModel categoryModel in Model.Categories.Where(c => c.Count > 0))
{
<!-- Do work here -->
}
}
else
{
<tr>
<td>No Categories Available</td>
</tr>
}
If you were planning on handling this, you would likely want to include this logic within your ViewModel itself as opposed to in the View, but the general idea is the same.
You have got the # in the wrong place. It should come before the if, like this
#if(categoryModel.Count > 0)
{
<td>#categoryModel.Count</td>
}
else
{
<td></td>
}
As mentioned in the comments. This should work:
<td>
if(#categoryModel.Count > 0)
{
#categoryModel.Count
}
</td>
Remove # from IF statement. You have already used in foreach loop.
if(categoryModel.Count > 0)
{
<td>#categoryModel.Count</td>
}
else
{
<td></td>
}
You can do this a couple of different approaches, the approach your taking please keep in mind will be slow with complex iterations. I'll point out the potential code errors:
#if will trigger proper Razor, unless you have no Html inbetween your foreach and the if.
Sample:
// Valid
#foreach(var .... model.Categories)
{
if(...)
{
}
}
// Invalid:
#foreach(var .... model.Categories)
{
<td>Content</td>
if(...)
{
}
}
Your model should be fully qualified, Example.Models.Warehouse.
You should show how you're actually declaring your #model for the View. Which could help us distinguish further, also are you sure that you have more than one? I would structure like this to hold the table row data better:
#if(Model != null)
{
foreach(Example.Models.Warehouse model in Model.Categories)
{
<tr>
<td>#model.Something</td>
</tr>
}
}
There isn't a need for a conditional, the loop won't execute if it is empty.
Got it, thanks for all the feedback. Here is my final solution.
#if(categoryModel.Count > 0)
{
<td>#Html.ActionLink(categoryModel.Name, "Category","~WarehouseCatalog", new { id = categoryModel.Id }, null)</td>
<td>#categoryModel.Count</td>
}
You can try the following. I first checked if there are items in the list and if there are create the HTML table, loop through the items and create a table row for each item:
#if (Model.Categories.Count > 0)
{
<table>
foreach (WarehouseCategoryModel categoryModel in Model.Categories)
{
<tr>
<td>#Html.ActionLink(categoryModel.Name, "Category", "-WarehouseCatalog", new { ide = categoryModel.ID }, null)</td>
#if(categoryModel.Count > 0)
{
<td>#categoryModel.Count</td>
}
else
{
<td> </td>
}
</tr>
}
</table>
}
I hope this helps.
Related
I'm not sure I quite have the vocabulary to asking this question properly, I hope the title of this question is worded correctly ... either way, I'm trying to make a table on a .cshtml Razor page using .Net Core 5.0 (Visual Studio 16.10.0). I'm using Entity Framework Core 5.0.11, though I'm not sure that's relevant to this.
I'm pulling data from the database, and what I want to do is build a table with two columns. So, I want the first item I pull to be in the first column, the second item I pull to be in the Second column, the third item I pull to be in the first column (now underneath item one), the fourth item I pull to be in the second column (now under item 2) and so on and so forth.
I'm trying to produce a two column table with an arbitrary amount of items.
(EDIT: Sorry if this table looks all messed up. When I'm in the edit page it looks like I want it to, but when I'm looking at my question on the final page it looks just like the raw text. Just wanted to include that I'm aware of this but not sure what to do about it. Or, maybe it's just my screen and it looks great on yours. Fingers crossed.)
Like this:
|Header (but ignore headers in the actual code)|Another Header (it just says a header row is required here)|
|---------------------------------------------|---------------|
|Item 1|Item 2|
|Item 3|Item 4|
Now, here's the code that I'm trying to use, but it doesn't seem to be working:
#if (Model.MyListOfItems is not null) // I can verify that it is not null
{
<table>
<tbody>
#{ int count = 0; }
#foreach (var item in Model.MyListOfItems)
{
count++;
#if (count % 2 == 1)
{
<tr>
<td>
#Html.DisplayFor(i => item.Text)
</td>
}
else
{
<td>
#Html.DisplayFor(i => item.Text)
</td>
</tr>
}
}
#if (count % 2 == 1)
{
</tr>
}
</tbody>
</table>
}
Now, you can see what I'm doing, right? For odd numbered items, open up a new table row. For even numbered items, close it. If we end on an odd numbered item, we'll have a row open, so that last if statement there closes it up. This way, we get a table like I described above.
However, this doesn't work. It won't compile. I get errors like ; expected for the very first line of the file. It starts complaining about code that comes before this code. I start getting a bunch of such and such doesn't exist in this current context
But here's the thing... I know the rest of the code is good, because if I change it to this
#if (Model.MyListOfItems is not null) // I can verify that it is not null
{
<table>
<tbody>
#{ int count = 0; }
#foreach (var item in Model.MyListOfItems)
{
count++;
#if (count % 2 == 1)
{
<tr>
<td>
#Html.DisplayFor(i => item.Text)
</td>
</tr> <!-- Added this -->
}
else
{
<tr> <!-- Added this -->
<td>
#Html.DisplayFor(i => item.Text)
</td>
</tr>
}
}
#if (count % 2 == 1)
{
<!-- </tr> --> <!-- Removed This -->
}
</tbody>
</table>
}
then it works and compiles just fine. The table obviously doesn't look how I want it to look, but I'm including this to illustrate that I know it's not a problem with my code, or even with the rest of my code, it's the compiler not seeing the end of that <tr> tag in the opening if statement and then not recognizing the } else { as C# code (and because of that is throwing errors all over the page).
I know the compiler is trying to save me from myself, but if I can just force it to compile I'm... at least fairly confident that the table will be properly formed HTML. Even if it's not, if I can get it to compile then I can see what kind of table this code does produce and fix it from there. But, the compiler clearly doesn't appreciate me opening a tag within an if block and then not closing it.
I've run into similar problems to this before, which is also why I'm confident that it's the compiler and not me, but usually I just find some other way to do it. And I'm sure I'll do that this time, I'm going to start trying as soon as I send this, but I figured I'd check with y'all anyway; I figure there must be something I'm missing.
Also, yes, I tried changing else to #else, but that didn't solve the problem, just started throwing errors saying that #else wasn't allowed there.
So, I think, that's a long way around asking what I'm trying to ask, but I think all the exposition was necessary. Anyway, I suppose here's the question:
How do I force the compiler to recognize my closing brackets as code when I open an HTML tag within that block, but don't close the HTML tag?
(EDIT: Added an explicit <tbody> to the table as per response from #Dai)
You can chunk your list of items so that you can iterate through your list of sublists like this:
<table>
<tbody>
#{
var listOfSubLists = Model.MyListOfItems
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / 2)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
}
#foreach (var subList in listOfSubLists)
{
<tr>
#foreach (var item in subList)
{
<td>
#Html.DisplayFor(i => item.Text)
</td>
}
</tr>
}
</tbody>
</table>
In this way, you can then use the <table> tag. I think this is a much proper approach than creating the table with the Bootstrap col- class in UI.
And also .NET6 LINQ will support a built-in chunk method that allows you to chunk your list into a single line of code:
var listOfSubLists = Model.MyListOfItems.Chunk(2);
Alright, well, if anyone knows the answer to my question I would still really appreciate that, because like I mentioned, I've run into stuff like this before and always have to find a workaround. But, in case anyone is wondering, here's my workaround.
I'm using the Bootstrap framework, and it has this column organization to it. 12 columns per page, and within each container, 12 more columns. Basically, you can keep dividing the page into smaller and smaller groups of twelve. That's not the best explanation but... here's their webpage on it https://getbootstrap.com/docs/4.0/layout/grid/
Anyway, instead of using a table, I'm making use of the grid system, like this:
<div class="row">
#if (Model.MyListOfItems is not null) // I can verify that it is not null
{
int count = 0;
#foreach (var item in Model.MyListOfItems)
{
count++;
<div class="col-6">
#Html.DisplayFor(i => item.Text)
</div>
#if (count % 2 == 0)
{
<br />
}
}
#if (count % 2 ==0)
{
<br />
}
}
</div>
I know that those last two if (count % 2 == 0) blocks probably aren't necessary at all since Bootstrap's grid system is responsive, but I just like being explicit. For the sake of everyone's sanity, though, this should yield the same result:
<div class="row">
#if (Model.MyListOfItems is not null) // I can verify that it is not null
{
#foreach (var item in Model.MyListOfItems)
{
<div class="col-6">
#Html.DisplayFor(i => item.Text)
</div>
}
}
</div>
I have a panel that is created and filled via a vacancy page I have created. Im doing it as follows:
#{
var root = CurrentPage.AncestorOrSelf(1);
var newsNode = root.Descendants("News").First();
var vacanciesNode = root.Descendants("Vacancies").First();
string shortenedSummary = string.Empty;
}
<ul>
#foreach (var vacancyItem in vacanciesNode.Descendants("Vacancy").Take(3).OrderBy("postDate desc"))
{
<p>here we are 2</p>
#vacanciesNode.Count().ToString()
<li>
<h4>#vacancyItem.jobTitle</h4> <span>Posted on #vacancyItem.postDate.ToString("dd/MM/yyyy")</span>
<p>
#if (vacancyItem.jobSummary.Length <= 182)
{
#vacancyItem.jobSummary
}
else
{
shortenedSummary = vacancyItem.jobSummary.Substring(0, 182) + "...";
#shortenedSummary
}
</p>
Read More..
</li>
}
</ul>
However, when there are no vacancy items, my list is empty. Should this be the case, I'm wanting it to read "sorry no vacancies just now" but I don't know how to check if my vacanciesNode has any items in it.
Could someone show me how I could achieve this?
Since the .Descendants() method returns a DynamicContentList (a collection) you can simply do a .Count() on the collection and check whether it's more than or equal to 1.
If there's more than 0 items in the collection, it's not empty.
So, what you need to do is surround your #foreach with an #if statement which checks on this, and an else statement after that prints whatever html you want to show if there's no vacancies
#if( vacanciesNode.Descendants("Vacancy").Take(3).OrderBy("postDate desc").Count() > 0) {
//Do foreach
}
else
{
//Write message about missing vacancies
}
I have a collection of items and would like to print them out with Razor. For every 12 elements it should create a new div. However, opening and closing tags seems to cause a lot of problems. This is my code.
<div class='grid_7'>
#foreach (var property in properties)
{
#if (counter % 12 == 0)
{
</div>
<div class='grid_7'>
}
#if (property.ShowInEditor)
{
<span> one property! #(property.Name) </span>
}
counter++
}
However, it is telling me that the first if and the first foreach are missing a closing {. I think that's because of the unclosed tags but, as you can see, I can't close the tag there.
How can I do this?
edit: removing # before the if cause even more problems making Razor belive it is text and not code.
Try adding "#:" without the quotes before the lines where you get this error and remove the # before if statements.
<div class='grid_7'>
#foreach (var property in properties)
{
if (counter % 12 == 0)
{
#:</div>
#:<div class='grid_7'>
}
if (property.ShowInEditor)
{
#:<span> one property! #property.Name </span>
}
counter++;
}
You'll need to modify your conditions a little to take into account the counter being zero at the start otherwise you'll get an empty div on first interation:
<div class='grid_7'>
#{
var counter = 0;
foreach (var property in properties)
{
if (counter > 0 && counter % 12 == 0)
{
#Html.Raw("</div><div class='grid_7'>")
}
if (property.ShowInEditor)
{
<span> one property! #(property.Name) </span>
}
counter++;
}
</div>
<div class='grid_7'>
#foreach (var property in properties)
{
if (count%7==0)
{
#Html.Raw("</div>");
#Html.Raw("<div class='grid_7'>");
}
else
{
//do something
}
I need to print this html in a view:
#foreach (string indices in Model.Indices)
{
if (counter == 1)
{
Response.Write("<tr>");
}
Response.Write("<td><span class='select'>#Html.CheckBox('nome',false)</span>)"); #indices Response.Write("</td>");
if (counter > 4)
{
Response.Write("</tr>");
}
}
This will print the html markup which I want to create.
I know I could only write the html but Razor is complaining that I am not closing the foreach.
This was my first try:
#foreach (string indices in Model.Indices)
{
if (counter == 1)
{
<tr>
}
<td><span class='select'>#Html.CheckBox('nome',false)</span> #indices </td>
#if (counter > 4)
{
</tr>
}
}
Razor requires that HTML tags be well-formed; otherwise, it wouldn't know when to go back to code context.
You can bypass this restriction by prefixing the line with #:.
However, the correct way to do this is to group your collection into a collection of groups of 4 items, and use a nested foreach.
If i right understand what do you want (Do you want to display first four elements from Model.Indices?), you should move your and tags out from your foreach. Something like this:
<tr>
#{var counter = 1;}
#foreach (string indice in indices)
{
if(counter>4)
{break;}
<td><span class='select'>#Html.CheckBox("nome", false)</span> #indice </td>
counter++;
}
</tr>
How can i close <tr> and open <tr> after 3 loop iterations? I have MVC 3 in .NET 4.0. How can I count loop iterations in MVC 3?
Current Code:
#foreach (var articleOnFirstPage in Model.ArticlesOnFirstSite)
{
<tr>
<td><div class="productsFrame"></div></td>
</tr>
}
I want to get this:
<tr>
<td><div class="productsFrame"></div></td>
<td><div class="productsFrame"></div></td>
<td><div class="productsFrame"></div></td>
</tr>
<tr>
<td><div class="productsFrame"></div></td>
<td><div class="productsFrame"></div></td>
<td><div class="productsFrame"></div></td>
</tr>
<tr>
<td><div class="productsFrame"></div></td>
<td><div class="productsFrame"></div></td>
<td><div class="productsFrame"></div></td>
</tr>
You could perform the following pornography in your view:
#model IEnumerable<Foo>
<table>
#foreach (var item in from i in Model.Select((value, index) => new { value, index }) group i.value by i.index / 3 into g select g)
{
<tr>
#foreach (var x in item)
{
<td><div class="productsFrame">#x.SomeProperty</div></td>
}
</tr>
}
</table>
or simply use view models and do the grouping in your controller action which obviously is what I would recommend you. The sole fact that you need to do this means that your view model is not adapted to your view's requirements which is to group results by 3. So adapt it. Don't pass IEnumerable<Foo> to your view. Pass IEnumerable<MyViewModel> where obviously MyViewModel will contain the necessary grouping so that in your views you could simply loop or since I hate writing for and foreach loops in views simply use display templates. They will take care of everything and your view will simply look like this:
<table>
#HtmlDisplayForModel()
</table>
Looks better than the initial pornography isn't it?
As requested in the comments section here's how I would implement this using view models.
As always in an ASP.NET MVC application you start by defining the view models that will reflect the requirements of your view (which I repeat are: show a table with 3 columns):
public class ItemViewModel
{
public string Title { get; set; }
}
public class MyViewModel
{
public IEnumerable<ItemViewModel> Items { get; set; }
}
then you move on to the controller that will fill and pass this view model to the view:
public class HomeController : Controller
{
public ActionResult Index()
{
// Obviously in a real world application the data is your domain model
// and comes from a repository or a service layer depending on the complexity
// of your application. I am hardcoding it here for the
// purposes of the demonstration
var data = Enumerable.Range(1, 30).Select(x => new { Title = "title " + x });
var model =
from i in data.Select((value, index) => new { value, index })
group i.value by i.index / 3 into g
select new MyViewModel
{
Items = g.Select(x => new ItemViewModel { Title = x.Title })
};
return View(model);
}
}
and finally you write the corresponding view (~/Views/Home/Index.cshtml):
#model IEnumerable<MyViewModel>
<table>
#Html.DisplayForModel()
</table>
and the ~/Views/Home/DisplateTemplates/MyViewModel.cshtml display template:
#model MyViewModel
<tr>
#Html.DisplayFor(x => x.Items)
</tr>
and finally the corresponding ~/Views/Home/DisplateTemplates/ItemViewModel.cshtml display template:
#model ItemViewModel
<td>#Html.DisplayFor(x => x.Title)</td>
and that's pretty much it. Simple, clean, following good practices and conventions.
Obviously to bring this a step further you would introduce AutoMapper to perform the actual mapping between your domain models and view models and you will end up with a very elegant solution that will look like this:
public ActionResult Index()
{
IEnumerable<DomainModel> data = ...
var viewModel = Mapper.Map<IEnumerable<DomainModel>, IEnumerable<MyViewModel>>(data);
return View(viewModel);
}
or a step further:
[AutoMap(typeof(IEnumerable<DomainModel>), typeof(IEnumerable<MyViewModel>))]
public ActionResult Index()
{
IEnumerable<DomainModel> data = ...
return View(data);
}
Now we are starting to get into serious business.
The first thing that comes to mind is Phil Haack's better foreach loop
Using it you gain an index and can use it like
<ol>
#Model.Each(#<li>Item #item.Index of #(Model.Count() - 1): #item.Item.Title</li>)
</ol>
What you're specifically looking for should be something like:
#Model.ArticlesOnFirstSite.Each(#<td><div class="productsFrame"></div></td>#(#item.Index % 3 == 0 ? "</tr><tr>" : ""))
Something like this may work.
#{int i = 0;}
#foreach (var articleOnFirstPage in Model.ArticlesOnFirstSite)
{
#if ((i++ % 3) == 0) {
#if (i != 1) {
#:</tr>
}
#:<tr>
}
#:<td><div class="productsFrame"></div></td>
}
#if (i != 0) {
#:</tr>
}
And this is a brute force solution to your problem.
As other have suggested and as I suggest, you should change your methodology: use view models, group items by 3.
About how to correctly use model-view-controller pattern you can look on the web.
http://msdn.microsoft.com/en-us/library/gg416514(v=vs.98).aspx is a good start.
Like the others said, the best and prettiest solution is probably to do the grouping in the controller, but this might get the job done:
#for (int i = 0; i < Model.ArticlesOnFirstSite.Count; i += 3)
{
<tr>
#foreach (Article article in Model.ArticlesOnFirstSite.Skip(i).Take(3))
{
<td>#article.Title</td>
}
</tr>
}