Practical ASP.NET
Creating Reusable View Components in ASP.NET Core
ASP.NET Core lets you bundle up business functionality along with its related UI into a view component that you can reuse in throughout your application.
In this column, I'm going to take a look at the next version of the .NET Framework's Web application development platform: ASP.NET Core. One of the more useful new features in ASP.NET Core is the addition of view components, which, in many ways, hark back to ASP.NET Web Parts. Like Web Parts, view components bundle up both logic and UI to create a package you can reuse throughout your ASP.NET Core application. With view components, you can also share the business logic component among multiple ASP.NET Core projects (something I'll discuss in a later column).
Of course, ASP.NET MVC also has Partial Views for creating reusable UI components. You could even integrate business logic into Partial Views though it was … difficult (but not impossible). View components, however, mimic the Controller/Action Method/View structure of an ASP.NET application, making combining UI and business logic a more natural fit with the structure of your ASP.NET Core application.
Caveats: To increase my chances of success when experimenting with this new technology, I downloaded the .NET Core 2.0 SDK and used Visual Studio 2017 to build my case study (I got a free copy of Visual Studio 2017 through the MVP Reconnect program). When I created my project, in the New ASP.NET Core Web Application dialog that appears after I selected my project template, I made sure that the framework box at the top of the dialog was set to .NET Core 2.0. I then added a Controller to my project, which caused Visual Studio to give me the option of picking the Full Dependencies option that adds every NuGet package Microsoft considers relevant to creating a Web application to my project -- and I took it. I also used NuGet Manager to add all of the AspNetCore Razor packages and the Microsoft.AspNetCore.Mvc.ViewFeatures package. Finally, after all that, I used NuGet Manager to update all my packages to the latest version.
Creating a View Component
A standard ASP.NET MVC page involves processing by both a Controller class and a View. Similarly, a view component involves processing by both a view component class and a View. The view component class file can be put in any folder you want -- for this case study, I've put mine in the Models folder. This class must inherit from the ViewComponent class.
You have two choices for flagging your class to ASP.NET Core as a view component. You can give your class a name and append the name with "ViewComponent" or you can decorate your class with the ViewComponent attribute, setting your view component's name through the attribute's Name property. Effectively, that means (from ASP.NET Core's point of view) these declarations are identical:
public class CustomerAddressViewComponent: ViewComponent
{ }
[ViewComponent(Name = "CustomerAddress")]
public class AddressManager: ViewComponent
{}
While your view component class file can go anywhere you want, the View used by your view component must be nested through the following folders:
- The Views folder
- Either a Controller folder or the Shared folder
- A folder called Components
- And, finally, a folder with a name that matches your view component's name
Since the point of a view component is to create something you can use in multiple places in your application, it makes the most sense to put your view component in the Views folder's Shared folder. However, if you have a view component that you only intend to use from one Controller you could put the View inside one of your View\ folders.
So, assuming I'm creating a view component called CustomerAddress that will be used from multiple controllers, I'd put its View in the /Views/Shared/Components/CustomerAddress folder. The name of the View is up to you, but if you don't want to explicitly use the View name in your code, you should call the View "Default." Currently only C# is supported for ASP.NET applications, so I end up with a View whose full pathname is /Views/Shared/Components/CustomerAddress/Default.cshtml. By the way, if you put the View in the wrong folder, at runtime you'll get the usual View error message that details all the places that ASP.NET looked in for your View. Inside a View you use the same Razor code as you use in any other View.
Adding Business Logic
Now that you've created your view component's class and View, it's just a matter of adding methods to your class and putting Razor markup in your View.
In your class, you're allowed one method and it must:
- Have the name InvokeAsync
- Return a Task object typed to IViewComponentResult
- Be decorated with the async keyword
- Accept a single parameter (of any type)
The async attribute will take care of wrapping whatever object you return from your InvokeAsync method inside a Task object. Currently, the only method built into the base ViewComponent class that returns an IViewComponentResult object is the View method. That all means that a simple implementation of an InvokeAsync method that returns an Address object would look like this:
public async Task InvokeAsync(string CustomerId) {
Address addr;
//...retrieve or build Address object for a specified customer
return View(addr);
}
If you don't do any asynchronous processing in the InvokeAsync method (or if you do but don't use the await keyword with it) you'll get a warning message that your method will be run synchronously. Everything will still work, however.
Your InovkeAsync method will only accept a single parameter so, if you want to pass multiple values to the method, then you must pass those values through properties on an object or as members of a collection.
If you don't want to call your View "Default," then, as in a Controller's Action method, you can pass the name of the View you do want as the first parameter to the View method. Your ViewComponent can also have a constructor that accepts parameters. However, you'll need to count on ASP.NET Core's dependency injection engine to pass any parameters to that constructor.
Of course, creating a view component isn't much help if you can't invoke it, so I'll look at that in a later column.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.