View Components allow creating functionality similar to a controller action method independent to a controller. Thus, view components can be used from different controllers, and they can also be created in a library, and used from multiple Web applications. Thus, view components can be used for a login panel, a shopping cart, a tag cloud – just anything you want to use across multiple controllers, and maybe also across multiple web applications.
This article shows how you can create and use view components. View Components are new with ASP.NET MVC Core 1.0, and got a new feature with version 1.1 in that they can be used with tag helpers.
Creating the View Component
A view component can be created in a library. With the sample application, I’ve created a .NET Core library. For view components, the NuGet package Microsoft.AspNetCore.Mvc.ViewFeatures need to be added:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp1.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="1.1.1" > </ItemGroup> </Project>
The sample view component shows a list of books based on a publisher. For this, the immutable Book
class is defined with properties BookId, Title, and Publisher:
public class Book { public Book(int bookId, string title, string publisher) { BookId = bookId; Title = title; Publisher = publisher; } public int BookId { get; } public string Title { get; } public string Publisher { get; } public override string ToString() => Title; }
The view component makes use of dependency injection. The contract that is used by the view component is defined by the interface IBooksService
:
public interface IBooksService { Task<IEnumerable<Book>> GetBooksAsync(); }
A view component needs to derive from the base class ViewComponent. Using the attribute ViewComponent, a name can be assigned to the view component. This is the name the view components can be referenced. Without this attribute, the class name is used to reference the view component. With the constructor of the BooksViewComponent class, the previously created IBooksService
interface needs to be injected. The heart of the view component is the InvokeAsync method. A view component can either define a synchronous method Invoke, or the asynchronous variant InvokeAsync. With the parameters, just define the parameters you need. The type and number of parameters is not fixed. With the sample code, a parameter of type string is used to return books only from the publisher passed. The return type of the method needs to be IViewComponentResult. The View method of the base class returns ViewViewComponentResult which in turn implements IViewComponentResult. The View method is very similar to the View method in the controller as overloads are available where you can pass the name of a view, and/or the model. With the sample code just the model is assigned, and the default view will be chosen:
[ViewComponent(Name = "BooksList")] public class BooksViewComponent : ViewComponent { private readonly IBooksService _booksService; public BooksViewComponent(IBooksService booksService) => _booksService = booksService; // InvokeAsync public async Task<IViewComponentResult> InvokeAsync(string publisher) { var books = await _booksService.GetBooksAsync(); return View(books.Where(b => b.Publisher == publisher)); } }
A view component implements the synchronous method Invoke or the asynchronous method InvokeAsync. With the parameters you can define the parameters you need.
Implementing the Service
The service that is used by the view component is implemented in the class BooksSampleService
:
public class BooksSampleService : IBooksService { private List<Book> _books = new List<Book> { new Book(1, "Professional C# 6 and .NET Core 1.0", "Wrox Press"), new Book(2, "Professional C# 5.0 and .NET 4.5.1", "Wrox Press"), new Book(3, "Enterprise Services with the .NET Framework", "Addison Wesley") }; public Task<IEnumerable<Book>> GetBooksAsync() => Task.FromResult<IEnumerabl<Book>>(_books); }
With the Startup class, the service is registered with the DI container:
public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); services.AddSingleton<IBooksService, BooksSampleService>(); }
Creating a view for the View Component
The view component is independent of the controller, so it’s best to define the view shared. This requires a specific directory structure. With the view component named BooksList, the directory for the view is Views\Shared\Components\BooksList. The default view has the file name Default.cshtml. With the sample code, just an unordered list containing the book titles is created:
@using ComponentsLibrary.Models @model IEnumerable<Book>; <h2>Books View Component</h2> <ul> @foreach (var book in Model) { <li>@book.Title</li> } </ul>
Using the View Component from the Controller
Now it’s possible to use the view component directly from the controller. In the HomeController
, the method Books1
invokes the method ViewComponent which is defined as virtual method in the Controller base class. This method is overloaded, so you can pass the name or type of the view component and the parameters as an object:
public IActionResult Books1() => ViewComponent("BooksList", "Wrox Press");
The method ViewComponent defines an object parameter where you can pass the values to the InvokeAsync method of the view component. If you have multiple parameters, you can pass these using an anonymous object where the property names match the parameter names:
public IActionResult Books1a() => ViewComponent("BooksList", new { publisher = "Wrox Press" });
Opening the view, the result from the view component is shown:
Using the View Component from a View
You can invoke the view component from a view – which likely is more used than invoking it from the controller. With the Home controller, the method Books2 returns a default view:
public IActionResult Books2() => View();
In the view Books2.cshtml, the view component is invoked using the InvokeAsync method. Component is a property of the page that returns an object IViewComponentHelper. With the IViewComponentHelper, overloaded versions of the InvokeAsync method are defined to pass the view component as type or with the name:
<h2>Books 2</h2> @await Component.InvokeAsync("BooksList", new { publisher = "Wrox Press" })
The result now shows the view with the shared layout and the view component:
Using a Tag Helper for the View Component
New with ASP.NET Core 1.1 is the option to invoke the view component with a tag helper. To enable tag helpers, the namespace of the view component needs to be added with @addTagHelper directive:
@using ViewComponentsSample @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, ComponentsLibrary
With this in place, the view-component can be written using the vc prefix and the name of the view component. The view component name changes with the tag helper: uppercase letters change to lowercase with an –. Parameters can be written using simple HTML attributes:
<h2>Books 3</h2> <vc:books-list publisher="Wrox Press" /> <hr /> <vc:books-list publisher="Addison Wesley" />
This is the result running the application and showing both Wrox Press and Addison Wesley books:
Using ASP.NET Core 1.1, view components can be used with tag helpers
Summary
View components belong to the ASP.NET MVC Core extension that didn’t exist with previous ASP.NET MVC versions. They allow creating functionality independent of the controller, and a shared view. The ASP.NET Core 1.1 functionality with tag helpers for view components offer a nice use.
Sample Code
The sample code is available at GitHub.
To run the sample code you need Visual Studio 2017.
Have fun with programming and learning!
Christian
More Information
More information about ASP.NET Core is available in my new book and my workshops:
Professional C# 6 and .NET Core 1.0
Image from © Wavebreakmedia Ltd | Dreamstime.com Green and yellow eye on blue face
An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.
/Views/_ViewImports.cshtml
Cannot resolve TagHelper containing assembly ‘MyAssembly.MyComponentNamespace. Error: Could not load file or assembly ‘MyAssembly.MyComponentNamespace’ or one of its dependencies. The system cannot find the file specified.
–
@using MyAssembly
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyAssembly.MyComponentNamespace
It seems, @addTagHelper accepts an assembly and not a namespace. So my view components cannot be used as tag helpers.
LikeLike
Your example doesn’t show any difference between View Component and Partial View. So what’s the point? If you’re boring to write Partial Views you can try View Components???
LikeLike
Den, the Partial View is dependent on a controller, the View Component is independent of a controller.
LikeLike