Display templates (along with Editor templates) are a powerful feature of ASP.NET MVC. They allow us to create reusable HTML snippets, much like we do with partial views. In this post, we'll discuss how display templates can simplify our view logic. We'll display a collection of data without a single foreach
loop in sight.
Let's say we have a page that needs to display a table of products. Each product has a description, colour and price. We also need to calculate and display the total price of all products. We'll create a ViewModel
for this page to hold our product data. We'll also create a separate ViewModel
for each product.
Here's what our ShowProductsViewModel
looks like:
public class ShowProductsViewModel
{
public IEnumerable<ProductViewModel> Products { get; set; }
public int TotalPrice { get; set; }
}
Our individual ProductViewModel
looks like this:
public class ProductViewModel
{
public int ProductId { get; set; }
public string Description { get; set; }
public string Colour { get; set; }
public decimal Price { get; set; }
}
Before we get into the nitty gritty of displaying our data, we need a view to display it. We also need a controller to retrieve the data, create the ViewModel
and bind it to the view. Let's use a ProductController
and a ShowProducts
view. To keep things simple, let's assume we're fetching our products from a business service. Ignoring the implementation of this service, here’s what the interface might look like:
public interface IBusinessService
{
IList<Product> GetProducts();
}
We'll do some simple mapping inside the controller to ready our data for the view. It's outside the scope of this post, but at this point I would tend to pass this work off to a factory.
public class ProductController : Controller
{
using System.Linq;
private IBusinessService businessService;
public ProductController()
{
businessService = new BusinessService();
}
public ActionResult ShowProducts()
{
var products = businessService.GetProducts(); var model = new ShowProductsViewModel
{
Products = products.Select(MapProduct)
TotalPrice = products.Sum(x => x.Price)
};
return View(model);
}
private ProductViewModel MapProduct(Product source)
{
var productViewModel = new ProductViewModel
{
ProductId = source.Id,
Description = source.Description,
Colour = source.Colour,
Price = source.Price
};
return productViewModel;
}
}
Now that we have our scaffolding set up, how can we approach this? We could loop through our product collection inside our view with a foreach
. The problem I have with this approach is that it introduces code logic into the view. Granted, it's only a single foreach
loop, but there is a far more elegant way to handle this.
Enter the Display Template
If your project doesn't have one already, create a DisplayTemplates
folder within Views/Shared
. Now create a partial view called ProductViewModel.cshtml and set the Model as ProductViewModel
. The standard naming convention is to give the partial the same name as the model. You can provide a different name if you wish and pass the name of the template at display time. We are going to display our products in a table, so our display template is nice and simple. It just contains the table row for our product:
@model ProductViewModel
<tr data-productid="@Model.ProductId"> <!---->
<td>@Model.Description</td>
<td>@Model.Colour</td>
<td>@Model.Price</td>
</tr>
Finally, we come to our view. To use the display template, we call the Html.DisplayFor
extension method:
@model ShowProductsViewModel
@ {
Layout = "~/Views/Shared/_Layout.cshtml"
}
<table>
<thead>
<tr>
<th>Description</th>
<th>Colour</th>
<th>Price</th>
</tr>
</thead>
<tbody>
@Html.DisplayFor(m => m.Products)
</tbody>
</table>
<p><span>@Model.TotalPrice</span></p>
In summary, what did we cover just now?
- We mapped some data from our business service to a
ViewModel
inside our controller (we could have used a factory for this)
- We created a display template to display a single product
- We created a view to display all our product data
- We used the
DisplayFor
extension method to invoke our display template on our products
- We had no need for a
foreach
loop, anywhere, resulting in a much cleaner view
View the original article