In an earlier column I decide to look at one of the more interesting new features in ASP.NET Core: view components. View components look very like a mini-Controller class and View except that you invoke view components from a View in order to add HTML to the View. Effectively, they bundle business logic and UI into a single reusable package.
In that previous article, I showed how to create the class and View that make up a view component. In this article, I'll show how to use that view component both from a View and from within an Action method. I'll also walk through how to share a view component among multiple projects.
Invoking Your View Component
In a View, you can invoke a view component one of two ways: use a View's Component property or add a Tag Helper to the View.
For my money, the simplest method is to simply call the InvokeAsync method from the View's Component property, passing the name of your view component and its parameters. You must use the await keyword here to ensure that your Task object is resolved before your View finishes processing. If you omit the await keyword Razor will shove the string representation of the Task object returned by the InvokeAsync method into your page.
So, to call my CustomerAddress component passing A123 as the customer id, I'd use this code in a standard View:
@await Component.InvokeAsync("CustomerAddress", "A123")
The only real downside to this method is that you don't get any IntelliSense support for entering the view component's name or parameters.
Invoking IntelliSense for View Components
If you'd prefer getting some IntelliSense support, then you'll want to use a Tag Helper to invoke your view component. With a Tag Helper, you add a tag to your View that shares your view component's name. The name for that tag must be your view component's name converted to lowercase and with hyphens before any uppercase letters (what's called kebob style). The tag name also needs the prefix vc: added to it. A view component called CustomerAddress class, therefore, is represented by a tag called vc:customer-address.
Any parameters that you want to pass to the InvokeAsync method must be provided through attributes on your new tag. The attribute name must match the parameter name, again converted to kebob format. To invoke my CustomerAddress view component as a Tag Helper, passing A123 to the CustomerId parameter, I'd add an element like this to a View:
<vc:customer-address customer-id="A123" />
This will only work, however, if you've told your project about your view component through an addTagHelper directive, even if your view component is in the same project as the View that's using it. You can put that in the Views using your tag or add it to the _ViewImports.cshtml file in your project's Views folder to use your view component in any View. The addTagHelper directive accepts two parameters: the name of your view component with or without the wildcard character (*), and the view component's namespace. An addTagHelper in the _ViewImports file like the following example lets you use any Tag Helper in the CustomerManagement namespace in any View:
@using SalesOrder @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, CustomerManagement
With the addTagHelper in place, IntelliSense will, in a View, prompt you through typing in the tag name and providing the parameter attributes.
View Components in Controller Methods
As a bonus, you can also invoke view components in Action methods. Typically, you'll want to do this in Action methods that return HTML to JSON calls. The Controller class's ViewComponent helper accepts the name of your view component and an anonymous object. The names of the properties on your anonymous object must match the names of your InvokeAsync method's parameters. This example invokes the CustomerAddress view component, passing A123 to a parameter called CustomerId:
public IActionResult Index() { return ViewComponent("CustomerAddress", new { CustomerId = "A123"}); }
Rather than passing the name of your view component, you can pass its Type object, giving you some IntelliSense support, as in this example:
return ViewComponent(typeof(CustomerAddressViewComponent), new { CustomerId = "A123"});
You must use the actual name of your view component class with this syntax. If you've used the ViewComponent attribute to name your view component (as I showed in my previous article), then you must use the name of the class here.
Sharing Across Projects
Not only can you reuse a view component within a single project, you can share a view component's class across multiple projects (but only the view component's class file, not its related View). It's the responsibility of the project using the view component to provide a View in one of the appropriate folders.
To support this, instead of defining your view component in an ASP.NET Core project, define the class in a Class Library project. Your Class Library project will need some ASP.NET Core libraries to support the ASP.NET Core classes and interfaces used in your view component. For the case study that I used in writing this article, I added the Microsoft.AspNetCore.All NuGet package to my Class Library project, which is probably more libraries than I needed.
To use your view component class in an ASP.NET Core project, first add a reference to the Class Library containing your view component. After that, you can just use the Component.InvokeAsync method in your Views or Action methods. If you want to use the Tag Helper syntax in your Views, you'll also need to add a addTagHelper directive that references your Class Library's namespace.
Really, my only complaint with view components is that they don't make quite as tidy a package as Web Parts did in the original version of ASP.NET. On the other hand, this is the first piece of ASP.NET technology that supports any kind of sharing across projects. Since that sharing improves consistency and reduces my workload, I'm a happy camper.
More