Quick Tip: How to Load ViewComponents in Razor Pages

August 4th, 2021

What do you mean 400 error? In today's quick tip, we examine how to load ViewComponents through Razor Pages using JavaScript

ViewComponents first appeared in the latest version of ASP.NET Core back in 2016 providing developers with a component-based mentality.

Before ViewComponents, HTMHelpers were available to create "partial" HTML sections. If we needed to render a portion of HTML, we would call a Controller and return back a PartialViewResult with usually a name and an optional model.

public IActionResult OnGetPartial() =>
    new PartialViewResult
    {
        ViewName = "_MyPartialView",
        ViewData = myModel,
    };

When ViewComponents arrived, we could attach business logic to their views, making them almost like mini-controllers. This gave us more control on what to display instead of mixing our C# with HTML. Business logic is just one of the many benefits to using ViewComponents

However, with Razor Pages, there wasn't an obvious way to load ViewComponents through JavaScript where you could display pockets of data on a webpage.

I shouldn't say that.

This WAS actually documented under the ViewComponents section, but it was for accessing it through a Controller, not through a Razor Page.

After working on a recent project with Razor Pages, you can achieve the same result, but it's a different approach and what I consider to be a "code smell."

Access ViewComponents through Razor Pages

To access your ViewComponent, you first need an "entry" page.

Once you create your Razor Page, you will see the standard OnGet() and returns the view attached to it by default.

public void OnGet()
{
}

However, if you want to return a ViewComponent instead, change your ActionResult to point to a ViewComponent instead.

public IActionResult OnGet()
{
    var myModel = new MyModel();
    return ViewComponent("MyWidget", myModel);
}

Your MyWidgetViewComponent should be located in your search path. These search paths, by default, are:

Of course, you can change these locations to fit the requirements of your project.

If it can't find the ViewComponent, it'll bomb.

What about the JavaScript?

Let's say you have an Refresh button on your web page and you want a section updated.

Logically, you want to group your calls together so they're not all over the place.

For example, if I want to refresh a section of a page, I want to keep a refresh method in that Razor Page. I don't want to make a refresh call to another Razor Page. You want to keep it together on the current page. All of your functionality, including JavaScript "Ajax" calls, should be contained in that Razor Page.

Here's the process I follow:

  1. Create a method in the Razor Page called OnGetRefresh(). Your OnGetRefresh should return a ViewComponent() like above (see the MyWidgetViewComponent).
  2. In the web page, create a fetch to call "/Index?handler=Refresh"
  3. When your JavaScript fetch returns the data back to the page, you'll replace the content in the page with the newly-returned HTML.

While this Refresh example is simplified, this gives you a starting point for taking it further.

Another example is POST-ing data back based on an action and returning back a ViewComponent with the updated HTML section. Your method name would change from OnGet to OnPost. This is a Razor Page convention over configuration concept.

Caveat to POST-ing data

Which reminds me, there are a couple of gotchas when you go down this road.

These two issues have taken me close to a day to find the answer, so I'm hoping they provide a shortcut and help others out in the process. :-)

Conclusion

In this post, we examined a way to load ViewComponents through JavaScript and discussed some of the nuances when working with ViewComponents in ASP.NET 5.

Oh, you remember how I mentioned at the beginning of this post where it feels like a code smell?

I'm referring to the "?handler=<action>" when posting back to a Razor Page.

I understand it's required to execute various methods and verbs, but it still feels...different and doesn't sit well with me. However, at the moment, I can't come up with a better solution.

So we continue with the handler approach. :-)

Did I miss a "gotcha?" How do you call your ViewComponents from JavaScript? Post your comments below and let's discuss.