View Components in ASP.NET Core

gpeipman
34.2K views

Open Source Your Knowledge, Become a Contributor

Technology knowledge has to be shared and made accessible for free. Join the movement.

Create Content

What is view component

View components are powerful UI building blocks for areas on page that are not controlled by controller actions. Suppose we have page with dynamic menu. Dynamic menu is on layout page and it is not directly accessible for controllers. Previously we had child actions and HtmlHelper extensions. Child actions had issues with async and HtmlHelper extensions doesn't support framework-level dependency injection. View components are free of both of these issues.

Top menu data source

Let's use simple Entity Framework Core 2.0 database context with in-memory implementation for dynamic top menu.

public class MenuItem
{
    [Key]
    public int Id { get; set; }
    public string Title { get; set; }
    public string Url { get; set; }
    public bool OpenInNewWindow { get; set; }
}

public class TopMenuDbContext : DbContext
{
    public DbSet<MenuItem> MenuItems { get; set; }

    public TopMenuDbContext(DbContextOptions<TopMenuDbContext> options) : base(options)
    {
    }

    public void AddMenuItems()
    {
        MenuItems.Add(new MenuItem { Title = "Home", Url = "~/" });
        MenuItems.Add(new MenuItem { Title = "About", Url = "~/Home/About" });
        MenuItems.Add(new MenuItem { Title = "External", Url = "http://gunnarpeipman.com", OpenInNewWindow = true });

        SaveChanges();
    }
}

MenuItem is simple entity representing menu items in database. It is easy to switch from in-memory implementation to SQL Server, PostgreSQL, MySQL and any other database that is supported by Entity Framework Core.

Defining top menu view component

There is no general rule where view components must be located. This example uses Components folder in web project root. Similar to controllers, view components support constructor injection and they have also views.

public class TopMenuViewComponent : ViewComponent
{
    private readonly TopMenuDbContext _context;

    public TopMenuViewComponent(TopMenuDbContext context)
    {
        _context = context;

        _context.AddMenuItems();
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var model = _context.MenuItems.ToList();

        return await Task.FromResult((IViewComponentResult)View("Default", model));
    }
}

There is one important difference compared to controllers - view components have only one action called InvokeAsync(). In this action view component makes its work and returns result rendered by MVC.

Default view for view component is named as Default. On Linux platforms don't forget that names are case sensitive. Here is default view for our top menu view component.

@model IList<MenuItem>
<ul class="nav navbar-nav">
    @foreach (var item in Model)
    {
        var url = item.Url;
        var target = "_self";

        if(url.StartsWith("~"))
        {
            url = Url.Content(url);
        }
        if(item.OpenInNewWindow)
        {
            target = "_blank";
        }

        <li><a href="@url" target="@target">@item.Title</a></li>
    }
</ul>

If URL of menu item starts with tilde ("~") then it is considered to be relative to web application root and URL helper is used to get correct full URL for browser.

Adding top menu view component to layout page

With all pieces now in place it's time to add top menu view component to layout page. Here is the fragment of layout page where top menu view component is called.

<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">Template</a>
        </div>
        <div class="navbar-collapse collapse">
            @(await Component.InvokeAsync("TopMenu"))
        </div>
    </div>
</nav>

Demo

Try to click on menu items and see how Home and About links open in same window and External opens link to my blog in new window or tab.

Click Run to see demo

References

Open Source Your Knowledge: become a Contributor and help others learn. Create New Content