Quick Intro To View Components In ASP.NET Core MVC

View components is a newly introduced feature in ASP.NET Core MVC in replacement of partial views. View components are very similar to partial views and allow the users to write reusable components without using model binding. View components support async as well as they render in chunks instead of the whole response. Previously, ChildActions were used to implement controller logic to introduce view components. The ChildActions feature is removed from ASP.NET Core.
 
In this article, we will create a simple recent article list (widget), using view components and see how effectively it will replace partial view.
 
To display the recent articles list, we need to create Articles class and ArticlesService (to return a list of articles).
 
Add model class

Add new folder, name it "Models". In Models folder, add new class, enter the class name "Articles" and tap OK.

Article.cs 
  1. public class Articles  
  2. {  
  3.     public string Title { getset; }  
  4.   
  5.     public DateTime PublishedDate { getset; }  
  6.   
  7.     public int TotalComments { getset; }  
  8. }  
Add services

Add new class "ArticlesServices" in the Models folder. ArticlesServices has GetArticles method, which returns the list of articles. I had created demo data to display on the view.

ArticlesService.cs
  1. public class ArticlesService  
  2. {  
  3.     public List<Articles> GetRecentArticles()  
  4.     {  
  5.         var articleList = new List<Articles>() {  
  6.             new Articles {Title="Quick start to create RESTful Web API in ASP.NET Core",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=10 },  
  7.             new Articles {Title="View injection using inject in ASP.NET Core Mvc",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=1 },  
  8.             new Articles {Title="Quick start to configure ASP.NET Core Mvc",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=3 },  
  9.             new Articles {Title="Shadow properties in Entity framework core (EF 7)",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=5 },  
  10.             new Articles {Title="10 Entity Framework 7 (EF core 1.0) features that you must know",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=12 },  
  11.             new Articles {Title="Entity Framework 7 (EF core 1.0) In-Memory Provider (for testing) simplified",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=4 },  
  12.             new Articles {Title="Expression-bodied members - C# 6 Language New features",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=2 },  
  13.             new Articles {Title="Null-conditional operators - C# 6 Language New features",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=10 },  
  14.             new Articles {Title="String interpolation - C# 6 Language New features",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=1 }  
  15.         };  
  16.   
  17.         return articleList;  
  18.     }  
  19. }  
Create a view component

A view component consists of two parts, the class (typically derived from ViewComponent) and the result it returns (typically a view). Like Controller view component supports POCO, methods and dependency injections.
 
A view component can be created by any method, given below-
  • Deriving from ViewComponent.
  • Decorating a class with the [ViewComponent] attribute.
  • have name ends with the suffix ViewComponent.
Like controllers, view components must be public, non-nested and non-abstract classes. As a view component does not take part in controller life cycle, we cannot use filters in it.
 
Let's start. Create a new class with suffix "ViewComponent"(as we are doing, while creating a controller).
  1. [ViewComponent(Name = "RecentArticles")]  
  2. public class RecentArticlesViewComponents : ViewComponent  
  3. {  
  4.     public async Task<IViewComponentResult> InvokeAsync()  
  5.     {  
  6.         return View(await GetRecentArticlesAsync());  
  7.     }  
  8.   
  9.     private Task<List<Articles>> GetRecentArticlesAsync()  
  10.     {  
  11.         ArticlesService articleService = new ArticlesService();  
  12.         return Task.FromResult(articleService.GetRecentArticles());  
  13.     }  
  14. }  
A view component defines its logic in an InvokeAsync method, which returns an IViewComponentResult. Parameters come directly from an invocation of the view component, not from model binding. A view component never handles a request. Typically, it initializes a model and passes it to a view by calling the ViewComponent View method. View components are not reachable directly as an HTTP endpoint. They are invoked by your code (usually in a view).
 
Here, we have initialized ArticleService class and using it's object. We are calling GetRecentArticles to retrieve a list of articles. This will display the same output, as shown at the end of the article. 
 
Create a view for view component

To create a view for view component, we need to follow the conventions. The run time searches for the view in the paths are given below-
  • Views/<controller_name>/Components/<view_component_name>/<view_name>
  • Views/Shared/Components/<view_component_name>/<view_name>
As per conventions, we need to create Views/Home/Components/RecentArticles/Default.cshtml or Views/Shared/Components/RecentArticles/Default.cshtml. You can see the structure in below image,

 
 
 
Have you noticed one thing, why I had used RecentArticles, instead of RecentArticlesViewComponents?, This is because, I declared a view component name RecentArticles, using [ViewComponent(Name = "RecentArticles")] attribute in the view component class. If you are not doing the same, RecentArticlesViewComponent would be preferred more than RecentArticles, while creating the view. 
 
Let's create Default.chtml view for a view component. In view, we are simply looping the IEnumerable<Articles> and displaying the article information one by one.

Default.chtml
  1. @model IEnumerable<MVCViewComponents.Models.Articles>  
  2. <ul>  
  3.     @foreach (var article in Model)  
  4.     {  
  5.         <li>@article.Title (@article.TotalComments) - @article.PublishedDate" </li>  
  6.     }  
  7. </ul>  
To use view component, we have to call Component.InvokeAsync with the parameters from view, as given below-
  1. @await Component.InvokeAsync("RecentArticles")  
I passed RecentArticles as it is our view component name. In optional, you can pass anonymous type parameters (like new {Id = Model.Id}) to pass the argument to view the components.
 
Dependency Injection in view component

ASP.NET Core provides in-built support for dependency injection. The key to dependency injection in ASP.NET Core MVC is to register your Services in the Startup File's ConfigureServices method.

Startup.cs
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     // Add framework services.  
  4.     services.AddMvc();  
  5.   
  6.     services.AddTransient<Models.ArticlesService>();  
  7. }  
Once you have registered the new service with the dependency injection container, you can either inject the Service into the constructor of a view component class or the property of a view component class.

RecentArticlesViewComponents.cs
  1. [ViewComponent(Name = "RecentArticles")]  
  2. public class RecentArticlesViewComponents : ViewComponent  
  3. {  
  4.     private ArticlesService articleService;  
  5.     public RecentArticlesViewComponents(ArticlesService articleService)  
  6.     {  
  7.         this.articleService = articleService;  
  8.     }  
  9.   
  10.     public async Task<IViewComponentResult> InvokeAsync()  
  11.     {  
  12.         return View(await GetRecentArticlesAsync());  
  13.     }  
  14.   
  15.     private Task<List<Articles>> GetRecentArticlesAsync()  
  16.     {  
  17.         return Task.FromResult(articleService.GetRecentArticles());  
  18.     }  
  19. }  
Declare ArticleService object in the class. Inject Service object, using constructor injection. You can get GetRecentArticles(), using the same object without creating a new object of the Service, as done previously. I have used constructor injection to inject the Services in the view component. Now, I can access GetRecentArticles() method, using the same object; I had declared in the class.
 
Output - This will display the list of the recent articles, using the view component.


References
  • https://docs.asp.net/en/latest/intro.html
  • https://docs.asp.net/en/latest/tutorials/first-web-api.html
  • https://docs.asp.net/en/latest/migration/webapi.html
  • https://docs.asp.net/en/latest/mvc/views/view-components.html