Iterating in a Template

Let’s revisit part of the non-efficient HTML from the introduction, where we hard-coded coffee options in a list.

1
2
3
4
5
6
<ol>
   <li>French Roast</li>
   <li>Espresso</li>
   <li>Kopi Luwak</li>
   <li>Instant</li>
</ol>

If we want to add, remove, or edit the list items, we need to go in and change the individual tags, which is a poor use of our time. Fortunately, there is a way to streamline the process.

In C#, we use a foreach Loop to iterate through the items in a data collection.

1
2
3
4
foreach (type item in items)
{
   //...code here...
}

Razor templates allow us to use a foreach loop to display items in a collection.

1
2
3
4
5
6
<ol>
   @foreach (type collectionItem in ViewBag.collectionProperty)
   {
      <li>@collectionItem</li>
   }
</ol>

Let’s explore line 2 to better understand how we are using the foreach loop in the Razor View.

  1. The @ specifies the C# portion of the template.
  2. The @foreach loop is initiated inside of a list element (either <ol> or <ul>).
  3. collectionItem represents an individual item or element within the collection.
  4. ViewBag.collectionProperty represents any collection that has been assigned as a property on ViewBag.

We can think of this syntax as saying, β€œFor each item within the ViewBag property, collectionProperty, repeat this HTML tag, but use the current value of collectionItem.”

Let’s apply this new concept to the HTML coffee list example. Assume that we store each of the coffee names as strings in a List called ViewBag.coffeeOptions.

1
2
3
4
5
6
<ol>
   @foreach (string coffeeType in ViewBag.coffeeOptions)
   {
      <li>@coffeeType</li>
   }
</ol>

Some points to note:

  1. coffeeOptions is accessible to the template because it is a property of the ViewBag object.

  2. In the first pass through the loop, coffeeType takes the value of the first coffee name in the coffeeOptions list.

  3. @coffeeType displays the value of coffeeType in the view, so the li element will show the first coffee name.

  4. Each successive iteration, coffeeType takes the next value in the list, and Razor adds a new <li></li> element to display that data.

Warning

@foreach creates one HTML tag for each item in a collection. BE CAREFUL where you place it.

Consider the following Razor code:

1
2
3
4
5
6
7
8
9
<div>
   <h3>Coffee Types</h3>
   @foreach (string coffeeType in ViewBag.coffeeTypes)
   {
      <ol>
         <li>@coffeeType</li>
      </ol>
   }
</div>

The final HTML produced is one heading, 4 ordered lists, and 4 coffee names. When this view is rendered, each coffee type is labelled with β€œ1”.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<div>
   <h3>Coffee Types</h3>
   <ol>
      <li>French Roast</li>
   </ol>

   <ol>
      <li>Espresso</li>
   </ol>

   <ol>
      <li>Kopi Luwak</li>
   </ol>

   <ol>
      <li>Instant</li>
   </ol>
</div>

Nested Loops

Assume you have a collection of different CoffeeShop objects. Each object contains a string field for name and a field that is a list of of the brews available, coffeeOptions.

Below, we nest loops to display a list of the shop names and their brew options.

Sample Razor template:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@foreach (var coffeeShop in ViewBag.coffeeShops)
{
   @*Each shop name*@
   <p>@coffeeShop.Name</p>
   <ul>
      @foreach(string coffeeType in coffeeShop.CoffeeOptions)
      {
         @*Each coffee type available*@
         <li>@coffeeType</li>
      }
   </ul>
}

Sample HTML output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<p>Central Perk</p>
<ul>
   <li>Espresso</li>
   <li>Instant</li>
</ul>

<p>Brews Brothers</p>
<ul>
   <li>French Roast</li>
   <li>Kopi Luwak</li>
</ul>

Apart from the nested loops displayed above, here are some other items you may find useful to note from the example above.

  • Razor comments are seen on lines 3 and 8 in the first code block above. Comments in Razor are nested between @* and *@. You may have noticed the comment block present on the top of a new view file.

  • ViewBag.coffeeShops is a list of CoffeeShop objects but we’ve used var on line 1 to type the coffeeShop item.

    In some limited circumstances, we can use the var keyword to implicitly type a variable. When this keyword is used, C# still assigns a type to coffeeShop through inference. It looks and sees that we are assigning coffeeShop to the value at the list index, which is a CoffeeShop object. Thus, coffeeShop is of type CoffeeShop.

    Alternatively, Razor does also allow us to import a custom class, such as CoffeeShop. If we wanted to do so, we could import the class or its namespace at the top of the template with a using statement.

Warning

We use var above to simplify the example and focus on the loop action. In general, we recommend that you avoid using var while you are learning C#. Even after you become more experienced with the language, you will still only want to use it sparingly and in specific circumstances. Explicitly declaring the type of your variables makes for more readable code, to name only one benefit.

Check Your Understanding

Question

What is the HTML outcome you expect from the Razor code below?

1
2
3
4
5
6
7
8
9
<div>
   <h3>Coffee Types</h3>
   <ol>
      @foreach (string coffeeType in ViewBag.coffeeTypes)
      {
         <li>@coffeeType</li>
      }
   </ol>
</div>
  1. One heading, 4 ordered lists, and 4 coffee names (each name labeled as β€œ1”)?
  2. One heading, one ordered list, and 4 coffee names (with the names labeled β€œ1”, β€œ2”, β€œ3”…)?
  3. 4 headings, 4 ordered lists, and 4 coffee names (each name labeled as β€œ1”)?
  4. 4 headings, 4 ordered lists, and 16 coffee names (with the names labeled β€œ1”, β€œ2”, β€œ3”…)?
Question

What is the HTML outcome you expect from the Razor code below?

1
2
3
4
5
6
7
8
9
@foreach (string coffeeType in ViewBag.coffeeTypes)
{
   <div>
      <h3>Coffee Types</h3>
      <ol>
         <li>@coffeeType</li>
      </ol>
   </div>
}
  1. One heading, 4 ordered lists, and 4 coffee names (each name labeled as β€œ1”)?
  2. One heading, one ordered list, and 4 coffee names (with the names labeled β€œ1”, β€œ2”, β€œ3”…)?
  3. 4 headings, 4 ordered lists, and 4 coffee names (each name labeled as β€œ1”)?
  4. 4 headings, 4 ordered lists, and 16 coffee names (with the names labeled β€œ1”, β€œ2”, β€œ3”…)?