ViewComponents in ASP.NET 5 and ASP.NET MVC 6

Let’s have a quick look at another new feature in ASP.NET MVC 6, and that is the ViewComponent feature. View components are intended to be replacements to ChildActions and, to some extent, of partial views.

Traditionally in ASP.NET MVC (and in general in the textbook MVC pattern), you had to compose the entire model in the controller and pass it along to the view, which simply rendered the entire page based on the data from the model. The consequence of this is that the view does not need to explicitly ask for any data – as its sole purpose is to just act upon the model it received.

While this sounds very nice in theory, it has traditionally posed a number of practical difficulties. There are a number of reusable components on pretty much every website – think a menu, a shopping cart, lists of all kinds, breadcrumbs, metadata and so on – so things that appear on multiple pages.

Let’s have a look at how this is solved in MVC 6.

The reusable component problem

Having to compose and gather the data required to create these components in multiple controllers over and over can be really painful, and has often led developers to create a all kinds of model builders and inherited models (have you ever created a BaseModel containing things like menu or page metadata?).

One way to easy that pain was to try to use child actions – which were actions that could only be called from the view (not publicly by the client). I have seen plenty of MVC projects that used child actions to render such reusable components. Another alternative has always been to create elaborate HtmlHelpers. Unfortunately since the helpers were static, you couldn’t do dependency injection into them, so trying to load any extra data for the purpose of such helper, would require you to use service locator – the MVC global DependencyResolver.

View components are really a mesh of both HtmlHelper and child actions.

ViewComponents example

Consider the following sample service:

It’s not unreasonable to think that you might have code resembling something that in your projects. Imagine that we need to display a list of these promoted products in multiple places on the website.

Typically in MVC 5 and earlier, you would have a child action, an HtmlHelper or a base model builder to handle this. With view component you can simply add a view component class:

That class can use ASP.NET 5 dependency injection – which is very convenient. It can return something primitive like a JSON or a string, but it can also have its own partial view; in the above example the partial exists under /Views/Shared/Components/{componentname}/Default.cshtml. The Default.cshtml is the default convenion for all views of view component. You can also pass the name of the view returning i.e. return View(“_Products”, products)

In our case the Razor view would look like you would expect any partial view to look:

In order to embed this view component into a proper, main view, you use the Component property of the Razor pages – in the form of the following syntax:

There is also a sync version, if your component is synchronous. And that’s about it – a bit against the textbook MVC pattern, but very convenient and much cleaner than the old approaches or workarounds we had to use.

Some background info

In order for a class to be recognized as a view component, it needs to be:

  • public
  • non-abstract
  • non generic
  • end with ViewComponent suffix or be decorated with ViewComponentAttribute

As you can see, just like the rest of MVC 6, it heavily relies on conventions. To build a view component you do not need to inherit from any specific class or implement any interface.

As far as the actual functionality inside your view component, it is also defined by convention – it should be wrapped in one of the following methods (the method name and return type are important):

The default view component invoker (which could be changed if you wish) will first try to look for the async method, and if it’s not found, then it will try to fallback to the sync version. Afterwards, if it’s still not found, you’ll get a runtime error. This is important to note, because given the above mentioned requirements for a class to be considered a valid view component, it is still possible to get a runtime error if you misspell or misname the actual Invoke method.

Depending on what you need to do, there is a base class ViewComponent, which you can choose to inherit from for your own convenience. It will give you – through it’s expose properties – access to all contextual information about the view such as user principal information, current HttpContext, ViewData and all other information you typically would have inside a view. It will also allow you to easily attach a Razor view to the view component – which you can do by calling the base View() method when returning from your view component.

As far as discovery of view components goes, all assemblies that reference ASP.NET MVC packages will be considered valid to search. This is particularly important for developers of reusable, CMS-like components that are intended to be used cross-project. The discovery is controlled by a type called DefaultViewComponentDescriptorProvider, which allows you to extend itself with additional source to look at or provide different rules for the class to be recognized as view component.

Finally, it is also possible to pass data to the components. Then you add the relevant parameters to the signature, for example:

In order to invoke such a component from a view, you use an overload of the Component.InvokeAsync which takes param of object[]:

Be Sociable, Share!

  • Harry McIntyre

    A few thoughts on this
    – Can you pass data from the caller to ViewComponent?
    – Repeated Default.cshtml is a bit of a bummer (as filename navigation gets harder)

    – Is the //file comment required? What about the [ViewComponentAttribute]?

    Heh. I need to install MVC6 like, yesterday, then I can answer my own questions :)

    • http://www.strathweb.com/ Filip W

      1 – yes, thanks for the great question, I updated the post with some extra information regarding that
      2 – this is the default convention, you can also pass the name of the view i.e. return View(“_Products”, products)

      3 – no this is just for illustration, it was supposed to further clarify everything not introduce ambiguity, sorry :)

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1904()

  • http://rehansaeed.com Muhammad Rehan Saeed

    What are the performance implications of using a view component? Using child actions had a detrimental impact on performance.

  • lideth

    hi,
    can you explain about this line “@model IEnumerable”?

    I wonder about “ViewComponentSite”, where it come from?

    thanks

    • http://www.strathweb.com/ Filip W

      it’s just a fully qualified name of my model, it’s a regular Razor view. My project just happened to be called “ViewComponentSite”

      • lideth

        i’ve got it. thanks

        :) I have an other question
        —my component class————–
        public class MyViewComponents: ViewComponent
        {
        public List games;
        public async Task InvokeAsync()
        {
        //this.testing = await GetGameLists();
        await Task.Run(() =>
        {
        this.games = GetGameLists();
        });
        return View(this.games);////***my question point here
        }
        }
        **i wonder how can i use the parameter return in view “return View(this.game) in my viewcomponent(default.cshtml)? in this case i dont use Model class.

        thanks.

  • Pingback: Les liens de la semaine – Édition #141 | French Coding()

  • Aurimas Neverauskas

    Previously, as ChildAction was called by HtmlHelper.Action it was creating internal request – that resulted in about 5ms (or smth like that) overhead. Now, there should be no overhead at all as new request object is not constructed and new request pipeline is not invoked.

    • http://www.strathweb.com/ Filip W

      that’s correct, right now it’s much simpler. The “Component” property in the view (which is used to place the component in the Razor file) is an instance of “IViewComponentHelper”, and internally it will create the context object (by aggregating different view information) and use “IViewComponentInvokerFactory” to instantiate an “IViewComponentInvoker”. That invoker class is directly responsible for injecting dependencies into your view component and simply run the code you set up there. There is no extra fake internal request pipeline.

      BTW all the se above services are customizable and replaceable using the ASP.NET 5 DI.

      • lokitoth

        Is there some way to make these be strongly typed and bound automatically (rather than creating a set of helper methods per component) in Razor, vs. using name binding and runtime type binding for parameters passed into the Invoke/InvokeAsync calls?

  • Tony Shao

    So if the viewcomponent is async, it will run in parallel with the caller, is that the case?

  • http://dan.cx/ Daniel Lo Nigro

    It’s unfortunate that this needs magic strings… Is there a version with generics? (eg. @await Component.InvokeAsync())?

    • Anders Borum

      It’s actually a quite good question; I think the new C# 6 nameof() operator allows you to express the following: @await Component.InvokeAsync(nameof(PromotedProductsViewComponent));
      Obviously the “ViewComponent” suffix is part of the convention based controller resolution, but I’m sure this could easily be handled as well. I’m personally stoked about ViewComponents. They compare to what UserControls was to WebForms (and the dynamic loading of UserControls in WebForms was awesome).

  • coffeeapplied@gmail.com

    Very nice job explaining some of more intricate details.

  • mleanos

    Thank you for this example. Very helpful. One thing I’m trying to get my head around, and I haven’t seen mentioned yet, is when I would want to “bind” results from my ViewComponent to the model in my parent View. How would one accomplish this?

    For instance, I have a Model named “ProductViewModel” that has two properties “name”, and “vendors”; the latter being an array of strongly typed Vendor objects. Now in my ProductList view, I would like to include a ViewComponent for managing the list of Vendors; basic CRUD operations on just the “vendors” field of my main model. The view that I couple with my ViewComponent has a model of IEnumerable. When I modify any of the Vendor data, I’d like it to propagate back up to my ProductViewModel.

    Is this a proper use case for ViewComponent’s? Or am I thinking about this completely wrong?

  • mleanos

    Did you ever figure this out? I’m struggling with a similar use case.

  • Johnny Oshika

    I find this comment really interesting: “As far as discovery of view components goes, all assemblies that reference ASP.NET MVC packages will be considered valid to search. “. I’m trying to get view components from a separate assembly working in an MVC 6 project without any success. I posted my problem here on StackOverflow: http://stackoverflow.com/q/34236850/188740. Any chance you can weigh in on the topic?

  • Darrell Tunnell

    Thanks – this helped me out when implementing the main menu for the site and it all worked swimmingly, seems pretty straightforward. Think some of the tooling is lagging though, for example it would be nice if you could scaffold a new ViewComponent similar to how you can scaffold other types of project items. Also the folder and file naming conventions for the view (cshtml file) aren’t particular likeable – i’m not a fan personally of many nested folders in the project tree) – and there isn’t any convention over where to put the ViewComponent class it seems? Although I have put mine in the ViewModels folder. Scaffolding a new ViewComponent would help create those folders and files according to the conventions so would be useful I reckon!