An introduction to ViewComponents - a login status view component

Share on:

View components are one of the potentially less well known features of ASP.NET Core Razor views. Unlike tag-helpers which have the pretty much direct equivalent of Html Helpers in the previous ASP.NET, view components are a bit different.

In spirit, they fit somewhere between a partial view and a full controller - approximately like a ChildAction. However whereas actions and controllers have full model binding semantics and the filter pipeline etc, view components are invoked directly with explicit data. They are more powerful than a partial view however, as they can contain business logic, and separate the UI generation from the underlying behaviour.

View components seem to fit best in situations where you would want to use a partial, but that the rendering logic is complicated and may need to be tested.

In this post, I'll use the example of a Login widget that displays your email address when you are logged in:

and a register / login link when you are logged out:

This is a trivial example - the behaviour above is achieved without the use of view components in the templates. This post is just meant to introduce you to the concept of view components, so you can see when to use them in your own applications.

Creating a view component

View components can be defined in a multitude of ways. You can give your component a name ending in ViewComponent, you can decorate it with the [ViewComponent] attribute, or you can derive from the ViewComponent base class. The latter of these is probably the most obvious, and provides a number of helper properties you can use, but the choice is yours.

To implement a view component you must expose a public method called InvokeAsync which is called when the component is invoked:

public Task<IViewComponentResult> InvokeAsync();

As is typical for ASP.NET Core, this method is found at runtime using reflection, so if you forget to add it, you won't get compile time errors, but you will get an exception at runtime:

Other than this restriction, you are pretty much free to design your view components as you like. They support dependency injection, so you are able to inject dependencies into the constructor and use them in your InvokeAsync method. For example, you could inject a DbContext and query the database for the data to display in your component.

The LoginStatusViewComponent

Now you have a basic understanding of view components, we can take a look at the LoginStatusViewComponent. I created this component in a project created using the default MVC web template in VisualStudio with authentication.

This simple view component only has a small bit of logic, but it demonstrates the features of view components nicely.

You can see I have chosen to derive from the base ViewComponent class, as that provides me access to a number of helper methods.

We are injecting two services into the constructor of our component. These will be fulfilled automatically by the dependency injection container when our component is invoked.

Our InvokeAsync method is pretty self explanatory. We are checking if the current user is signed in using the SignInManager<>, and if they are we fetch the associated ApplicationUser from the UserManager<>. Finally we call the helper View method, passing in a template to render and the model user. If the user is not signed in, we call the helper View without a template argument.

The calls at the end of the InvokeAsync method are reminiscent of action methods. They are doing a very similar thing, in that they are creating a result which will execute a view template, passing in the provided model.

In our example, we are rendering a different template depending on whether the user is logged in or not. That means we could test this ViewComponent in isolation, testing that the correct template is displayed depending on our business requirements, without having to inspect the HTML output, which would be our only choice if this logic was embedded in a partial view instead.

Rendering View templates

When you use return View() in your view component, you are returning a ViewViewComponentResult (yes, that name is correct!) which is analogous to the ViewResult you typically return from MVC action methods.

This object contains an optional template name and view model, which is used to invoke a Razor view template. The location of the view to execute is given by convention, very similar to MVC actions. In the case of our LoginStatusViewComponent, the Razor engine will search for views in two folders:

Views\Components\LoginStatus; and

Views\Components\Shared

If you don't specify the name of the template to find, then the engine will assume the file is called default.cshtml. In the example I provided, when the user is signed in we explicitly provide a template name, so the engine will look for the template at

Views\Components\LoginStatus\LoggedIn.cshtml; and

Views\Components\Shared\LoggedIn.cshtml

The view templates themselves are just normal razor, so they can contain all the usual features, tag helpers, strongly typed models etc. The LoggedIn.cshtml file for our LoginviewComponent is shown below:

There is nothing special here - we are using the form and action link tag helpers to create links and we are writing values from our strongly typed model to the response. All bread and butter for razor templates!

When the user is not logged in, I didn't specify a template name, so the default name of default.cshtml is used:

This view is even simpler as we didn't pass a model to the view, it just contains a couple of links:

Invoking a view component

With your component configured, all that remains is to invoke it from your view. View components can be invoked from a different view by calling, in this case, @await Component.InvokeAsync("LoginStatus"), where "LoginStatus" is the name of the view component. We can call it in the header of our _Layout.cshtml:

Calling View Components like TagHelpers in ASP.NET Core 1.1.0

View components work well, but one of the things that seemed like a bit of a step back was the need to explicitly use the @ symbol to render them. One of the nice things brought to Razor with ASP.NET Core was tag-helpers. These do pretty much the same job as the HTML helpers from the previous ASP.NET MVC Razor views, but in a more editor-friendly way.

For example, consider the following block, which would render a label, text box and validation summary for a property on your model called Email

Syntax highlighting is easier for basic editors and you don't need to use ugly @ symbols to escape the class properties - everything is just that little bit nicer. In ASP.NET Core 1.1.0, you can get similar benefits for calling your tag helpers, by using a vc: prefix.

To repeat my LoginStatus example in ASP.NET Core 1.1.0, you first need to register your view components as tag helpers in _ViewImports.cshtml (where WebApplication1 is the namespace of your view components) :

@addTagHelper *, WebApplication1

and you can then invoke your view component using the tag helper syntax:

Note the name of the tag helper here, vc:login-status. The vc helper, indicates that you are invoking a view component, and the name of the helper is our view component's name (LoginStatus) converted to lower-kebab case (thanks to the ASP.NET monsters for figuring out the correct name)!

With these two pieces in place, your tag-helper is functionally equivalent to the previous invocation, but is a bit nicer to read:)

Summary

This post provided an introduction to building your first view component, including how to invoke it. You can find sample code on GitHub. In the next post, I'll show how you can pass parameters to your component when you invoke it.