Quick introduction to View Components in ASP.NET Core MVC

View components is newly introduced feature in ASP.NET Core MVC in replacement of partial views. View components are very similar to partial views and allow users to write reusable components without using model binding. View components support async as well as they render in chunk instead of the whole response. Previously, ChildActions used to implement controller logic, on introducing view components 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.

Before starting, If you're beginner in ASP.Web Core MVC,  talk over with my earlier article quick start to configure ASP.Net Core MVC on the way to aid to configure MVC in ASP.Web Core project.

To display 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.
Articles.cs
public class Articles
{
    public string Title { get; set; }

    public DateTime PublishedDate { get; set; }

    public int TotalComments { get; set; }
}

Add services

Add new class "ArticlesServices" in the Models folder. ArticlesServices have GetArticles method that returns the list of articles. I had created demo data to display on the view.
ArticlesService.cs
public class ArticlesService
{
    public List<Articles> GetRecentArticles()
    {
        var articleList = new List<Articles>() {
            new Articles {Title="Quick start to create RESTful Web API in ASP.NET Core",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=10 },
            new Articles {Title="View injection using inject in ASP.NET Core Mvc",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=1 },
            new Articles {Title="Quick start to configure ASP.NET Core Mvc",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=3 },
            new Articles {Title="Shadow properties in Entity framework core (EF 7)",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=5 },
            new Articles {Title="10 Entity Framework 7 (EF core 1.0) features that you must know",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=12 },
            new Articles {Title="Entity Framework 7 (EF core 1.0) In-Memory Provider (for testing) simplified",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=4 },
            new Articles {Title="Expression-bodied members - C# 6 Language New features",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=2 },
            new Articles {Title="Null-conditional operators - C# 6 Language New features",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=10 },
            new Articles {Title="String interpolation - C# 6 Language New features",PublishedDate=Convert.ToDateTime("08/08/2016"),TotalComments=1 }
        };

        return articleList;
    }
}

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 of following methods,
  • 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).

RecentArticlesViewComponents.cs
[ViewComponent(Name = "RecentArticles")]
public class RecentArticlesViewComponents : ViewComponent
{
    public async Task<IViewComponentResult> InvokeAsync()
    {
        return View(await GetRecentArticlesAsync());
    }

    private Task<List<Articles>> GetRecentArticlesAsync()
    {
        ArticlesService articleService = new ArticlesService();
        return Task.FromResult(articleService.GetRecentArticles());
    }
}
A view component defines its logic in an InvokeAsync method that returns an IViewComponentResult. Parameters come directly from invocation of the view component, not from model binding. A view component never handles a request. Typically 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 from 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. That will displays 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 conventions. The runtime searches for the view in the following paths:
  • 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
view-components-aspnetcore-solution-explorer-dotnetspan-img
Have you noticed one thing, why I had used RecentArticles instead of RecentArticlesViewComponents?, This is because I declared view component name RecentArticles using  [ViewComponent(Name = "RecentArticles")] attribute in view component class. If you are not doing the same, then RecentArticlesViewComponent would be much preferred then RecentArticles while creating the view.

Invoking a view component

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
@model IEnumerable<MVCViewComponents.Models.Articles>
<ul>
    @foreach (var article in Model)
    {
        <li>@article.Title (@article.TotalComments) - @article.PublishedDate" </li>
    }
</ul>
To use view component, we have to call Component.InvokeAsync with parameters from view.
@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 argument to view components.

Dependency Injection in view component

ASP.NET Core provides in-build 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
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc();

    services.AddTransient<Models.ArticlesService>();
}
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
[ViewComponent(Name = "RecentArticles")]
public class RecentArticlesViewComponents : ViewComponent
{
    private ArticlesService articleService;
    public RecentArticlesViewComponents(ArticlesService articleService)
    {
        this.articleService = articleService;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        return View(await GetRecentArticlesAsync());
    }

    private Task<List<Articles>> GetRecentArticlesAsync()
    {
        return Task.FromResult(articleService.GetRecentArticles());
    }
}
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 service as previous. I have used constructor injection to inject services in view component. Now I can access GetRecentArticles() method using the same object I had declared in the class.

Output

This will displays the list of recent articles using view component.


Reference
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