2017-03-06

Introduction

In the previous post we looked at partial views. Partial views have been around for a long time in ASP.NET MVC and they work in much the same way in .NET Core MVC. Partial views are a means of factoring out parts of a view into smaller, reusable and more manageable units. Partial views can also have their own dependencies. So they behave just like “full” views but are meant to be rendered within a parent view.

In this post we’ll look at an alternative to partial views called view components. View components are new in this version of MVC.

View components are also documented on the official .NET Core guide here. We’ll look try out most of it in this post. We’ll be working in our demo application DotNetCoreBookstore.

View components

View components are sort of a small version of a full MVC cycle. They have a controller and a view that follow a naming convention but are rendered within a parent view just like partial views. Since view components have their own controllers they can also have their own dependencies and parameters. However, they are not “open” controllers that can be directly invoked from a URL like /books/details . They are hidden MVC elements that are normally invoked from within a parent view using a new helper class called “Component”.

We’ll start by building a view component for the book details. In other words we’ll create the view component equivalent of the partial view from the previous post.

View component controllers are by convention placed in a folder called ViewComponents. So go ahead and insert a new folder called ViewComponents to the root of the web project. View component controllers are normal C# classes that follow a naming convention just like MVC controllers do. The easiest way to automatically register a view component controller is to derive from the ViewController base class in the Microsoft.AspNetCore.Mvc namespace and give it a name that ends with “ViewComponent”.

We’ll add a new C# class called BookDetailsViewController into the ViewComponents folder. A VC controller must have a function called Invoke that returns an IViewComponentResult object. The Invoke method can have 0, 1 or more parameters. We’ll see in a bit how the caller can specify the values for the Invoke method parameters.

The BookDetailsViewComponent class is very simple:

The Invoke function has a parameter of type BookDetailsViewModel which is passed into a view called Details.

View component views must be placed in folders that also follow a naming convention. The book details view component will be used with books only, i.e. it won’t be shared with other controllers. The folder naming rule is the following for this case: Views/[ControllerName]/Components/[ComponentNameWithoutViewComponentAttachedToItsName]/[NameOfView.cshtml]. In our example we have to add a Details.cshtml view file to the Views/Books/Components/BookDetails folder:



The view component Details.cshtml is identical to the _BookDetails.cshtml partial view from the previous post with the exception of having to add a using statement:

We’re now ready to invoke the component from the Details view of BooksController:

Component.InvokeAsync is an asynchronous method so we have to apply the await keyword for it to be rendered properly. The InvokeAsync function requires the name of the view component without the “ViewComponent” name extension. We can also supply the input parameters in an anonymous object. It will be passed into the Invoke method of the BookDetailsViewComponent object.

Run the application and navigate to a detail page like /books/details/1. You’ll see that the component view is rendered as expected.

Object dependencies in view components

In this example we’ll see that view component controller can also have their abstract object dependencies just like normal MVC controllers. Currently we have a section called FunnyMessage in _Layout.cshtml:

We’ll add a common message to all pages that use the layout. It will no longer be the responsibility of the individual views to render this section, it will be handled by a view component in the layout view.

Add a new C# class called FunnyMessageViewComponent to the ViewComponents folder:

We’re planning to use this component in many views so it’s a good idea to share it. The folder naming convention in this case is Shared/Components/[ComponentNameWithoutTheViewComponentNameExtension]/[ViewName.cshtml]. In our case it will be Shared/Components/FunnyMessage/Show.cshtml:



Show.cshtml is very simple:

Here’s how to invoke it from _Layout.cshtml:

Navigate to /books and you’ll see the rendered message. The abstract IStringFormatter dependency of FunnyMessageViewComponent was resolved in Startup.cs in a previous part of this series:

An asynchronous invoke method in the view component

It can happen that the view component object needs to call an asynchronous function that returns a Task. Here’s a FunnyMessageAsyncViewComponent class which is the same FunnyMessageViewComponent but the message is formatted asynchronously. We pretend that message formatting is a resource heavy task that should be completed on a different thread:

Note that the function to implement is not Invoke anymore but InvokeAsync.

We’ll need another Show view in the Shared/Components/FunnyMessageAsync folder to adhere to the naming conventions:



This Show.cshtml has the following body:

Here’s how it can be invoked from _Layout.cshtml:

The asynchronous version will be rendered just like the synchronous one.

We’ll continue in the next post.

View the list of MVC and Web API related posts here.

Show more