ASP.NET Core 1.0 (previously known as ASP.NET 5) is a redesign of ASP.NET that we have been using all these years. In earlier versions of ASP.NET, Web API was provided as a separate Web API framework; but going forward in the ASP.NET Core 1.0 release, Web API is merged with ASP.NET MVC, termed as MVC 6. This provides an advantage of creating applications using HTML and API services, using a single framework.
ASP.NET MVC 6 contains some great features for modern web developers which we have already covered in our articles over here:
If you are already using ASP.NET MVC 5 (the previous version of ASP.NET MVC) then you will find the above links useful.
Why ViewComponents?
ASP.NET MVC 5 allows us to create Model layers and then expose these models using controllers to display data to Views, or access it from Views. But what if we want to reuse a specific User Interface (in short a reusable UI component) on all Views? E.g. let us say we want to provide a search functionality (TextBox and Search Button) across all Views. In this case, while using MVC 5, we need to create a separate Partial View and render it across all Views or we need to create a separate HtmlHelper method for managing view rendering. In such cases of Partial View or HtmlHelper, it is important for us to make sure that they should work for each Model. Since a View is associated with Action Method of the MVC Controller class, we pass Model from Action Method to the View, to scaffold view. All partial Views/HtmlHelper are now bound to the Model for the View. In this case, creating reusable Partial View/HtmlHelper independent from a specific model is a challenging and maintenance wise costly job. A simpler solution was needed.
View Components in ASP.NET Core 1.0 (previously known as ASP.NET 5)
In ASP.NET Core 1.0 (previously known as ASP.NET 5), several new features were introduced including the View Component. A View Component is similar to partial views but more powerful compared to them. A major functionality of the View Component is that it renders a chunk instead of the whole response. This can be used to render View along with data e.g. Login/Logout Panel across all Views, Navigation Bar, recent updates in data etc. Another big improvement over ChildActions is that View Components allows you to run asynchronous operations on them.
Section 1: Creating a ViewComponent class
View Component has two sections, the first one is the class created using the following specifications:
- A class derived from ViewComponent.
- Applying ViewComponentAttribute class as attribute on the class that is to be used as ViewComponent.
- Creating a class where the name of the class ends with suffix ViewComponent.
- The class which we intend using as a ViewComponent must be public, non-abstract, and non-generic.
- If we want to work with data-context object (from Entity Framework or any other data access mechanism, service, repository.), then using constructor injection, we can make the dependency available to the ViewComponent class.
Section 2: ViewComponent - The Implementation
To implement this application, we will be using Visual Studio 2015 and ASP.NET Core 1.0. Please visit https://www.dotnetcurry.com/aspnet-mvc/1215/building-aspnet-mvc-6-entity-framework-7-app-using-aspnet-5 to understand, how to create an ASP.NET MVC 6 application from scratch.
Step 1: This application builds on the application we created previously at https://www.dotnetcurry.com/aspnet-mvc/1215/building-aspnet-mvc-6-entity-framework-7-app-using-aspnet-5. So please read all the steps in that article and download the code. Extract the application and open it in Visual Studio 2015. This is the MVC6_App application and contains Models, Views and Controllers folder. Run the application and the following result will be displayed:
Clicking on Categories and Products will show the Index pages for both.
Step 2: As we discussed in the Section 1, we will be creating a View Component. In the project, add a new folder of name ViewComponents. In this folder add a class file with the following code in it:
using Microsoft.AspNet.Mvc;
using MVC6_App.Models;
using System.Linq;
namespace MVC6_App.ViewComponents
{
public class ProductListViewComponent : ViewComponent
{
private readonly AppDbContext ctx;
public ProductListViewComponent(AppDbContext c)
{
ctx = c;
}
public IViewComponentResult Invoke(string filter)
{
var res = (from p in ctx.Product.ToList()
where p.ProductName.StartsWith(filter)
select p).ToList();
return View(res);
}
}
}
The above code has the following specifications:
- The class ProductListViewComponent (Note the suffix is ViewComponent) is derived from ViewComponent base class.
- This uses constructor injection to instantiate the AppDbContext class. This class is used for data access.
- The Invoke() method is exposed as public method and called on the view. This method can access any number of parameters. In the current case, this method accesses a string parameter and filters all products that start with the string variable value. We can have InvokeAsync() method for an asynchronous execution.
Step 3: To use View Component in the Razor view, we need to follow these steps:
- Under the Shared sub-folder of Views folder, add a folder having the same name as the ViewComponent class with the ViewComponent suffix. In our case the name of the folder will be ProductList.
- In this folder, add a new Razor View of name Default.cshtml.
In this View add the following code.
@model IEnumerable<MVC6_App.Models.Product>
<h3>Max. Sold Products</h3>
<ul>
@foreach (var prd in Model)
{
<li>@prd.ProductName</li>
}
</ul>
The above view code is using Product as the Model class. This iterate through the model and display products by ProductName.
Step 4: Open Index.cshtml in the Products sub-folder of the Views folder. We will have a foreach loop which iterates through all products. After the completion of this loop, add the following code in the View
@Component.Invoke("ProductList", "D")
The @Component.Invoke is the syntax for calling View Component. The first parameter is the name of the View Component. This will be searched from the Views > Shared > (Folder Having Same name of View Component class) > Default.cshtml. The second parameter is the parameter which will be passed to the Invoke method of the ViewComponent class.
Step 5: Run the application and View the Products/Index view. The following result will be displayed:
The ViewComponent is shown on the top of the view showing the Max. Sold Products which starts from character D.
We can reuse the same ViewComponent across all Views where we need the Product Model.
We can generalize the ViewComponent so that it can be used for any model (I have used for IEnumerable type here.) In our application, we may have several Index Views and we may want to show the data filtered on the Index View using ViewComponent. In this case we should create a single ViewComponent which will return Html Contents with desired data.
Step 6: In the ViewComponents folder, add a new class of name SearchViewComponent. Add the following code in it
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using MVC6_App.Models;
using System.Collections;
using System.Linq;
using System.Reflection;
namespace MVC6_App.ViewComponents
{
public class SearchViewComponent : ViewComponent
{
private readonly AppDbContext ctx;
public SearchViewComponent(AppDbContext c)
{
ctx = c;
}
public IViewComponentResult Invoke(object [] model, string property, string filter)
{
var t = (model[0]).GetType();
PropertyInfo[] properties = t.GetProperties();
IEnumerable Res = null;
foreach (PropertyInfo p in properties)
{
if (p.Name == property)
{
Res = from pP in model
where ((string)pP.GetType().GetProperty(p.Name).GetValue(pP, null)).StartsWith(filter)
select pP;
break;
}
}
string html = "<h3>The Filtered Data</h3><ul>";
foreach (var item in Res)
{
html += "<li>" + item.GetType().GetProperty(property).GetValue(item, null) + "</li>";
}
html += "</ul>";
return new ContentViewComponentResult(new HtmlString(html));
}
}
}
In the above view component, we have the Invoke method with following parameters
· Object array
· string property
· string filter
We will be using this ViewComponent to check if the models array has the property matching with the values passed for the input parameter, and then using the value of the filter, we will search records from model array having property value that starts with the filter.
The Invoke() method uses reflection to access the class type from the array. Using reflection, properties from the object are read. If the input parameter property value is present in the properties, then using a LINQ query, we can read records from model array. This result is stored in Res variable. The method further creates an Html string with ul and li and appends data in it. The method return the Html string using the ContentViewComponentResult object.
We do not need a separate folder for this ViewComponent because the Invoke method already return ContentViewComponentResult object
Step 7: In the Index.cshtml of the Products folder in View folder, add the following code below the html table
@Component.Invoke("Search", Model.ToArray(), "ProductName", "D")
Run the Application and Navigate to the Products/Index and the following result will be displayed
Test this on other views like Categories/Index etc.
Conclusion
View Components in ASP.NET MVC play an important role for reusing UI across pages. Going forward, they are suitable replacements to ChildActions and Partial Views. One big improvement over ChildActions is that View Components allows you to run asynchronous operations on them.
Download the entire source code of this article (Github)
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn