Quantcast
Channel: Andrew Lock | .NET Escapades
Viewing all articles
Browse latest Browse all 743

How to pass parameters to a view component

$
0
0
How to pass parameters to a view component

In my last post I showed how to create a custom view component to simplify my Razor views, and separate the logic of what to display from the UI concern.

View components are a good fit where you have some complex rendering logic, which does not belong in the UI, and is also not a good fit for an action endpoint - approximately equivalent to child actions from the previous version of ASP.NET.

In this post I will show how you can pass parameters to a view component when invoking it from your view, from a controller, or when used as a tag helper.

In the previous post I showed how to create a simple LoginStatusViewComponent that shows you the email of the user and a log out link when a user is logged in, and register or login links when the user is anonymous:

How to pass parameters to a view component

The view component itself was simple, but it separated out the logic of which template to display from the templates themselves. It was created with a simple InvokeAsync method that did not require any parameters:

public class LoginStatusViewComponent : ViewComponent  
{
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly UserManager<ApplicationUser> _userManager;

    public LoginStatusViewComponent(SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager)
    {
        _signInManager = signInManager;
        _userManager = userManager;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        if (_signInManager.IsSignedIn(HttpContext.User))
        {
            var user = await _userManager.GetUserAsync(HttpContext.User);
            return View("LoggedIn", user);
        }
        else
        {
            return View("Anonymous");
        }
    }
}

Invoking the LoginStatus view component from the _layout.cshtml involves calling Component.InvokeAsync and awaiting the response:

 @await Component.InvokeAsync("LoginStatus")

Updating a view component to accept parameters

The example presented is pretty simple, in that it is self contained; the InvokeAsync method does not have any parameters to pass to it. But what if we wanted to control how the view component behaves when invoked. For example, imagine that you want to control whether to display the Register link for anonymous users. Maybe your site that has an external registration system instead, so the "register" link is not valid in some cases.

First, lets create a simple view model to use in our "anonymous" view:

public class AnonymousViewModel  
{
    public bool IsRegisterLinkVisible { get; set; }
}

Next, we update the InvokeAsync method of our view component to take a boolean parameter. If the user is not logged in, we will pass this parameter down into the view model:

public async Task<IViewComponentResult> InvokeAsync(bool shouldShowRegisterLink)  
{
    if (_signInManager.IsSignedIn(HttpContext.User))
    {
        var user = await _userManager.GetUserAsync(HttpContext.User);
        return View("LoggedIn", user);
    }
    else
    {
        var viewModel = new AnonymousViewModel
        {
            IsRegisterLinkVisible = shouldShowRegisterLink
        };
        return View(viewModel);
    }
}

Finally, we update the anonymous default.cshtml template to honour this boolean:

@model LoginStatusViewComponent.AnonymousViewModel
<ul class="nav navbar-nav navbar-right">  
    @if(Model.IsRegisterLinkVisible)
    {
        <li><a asp-area="" asp-controller="Account" asp-action="Register">Register</a></li>
    }
    <li><a asp-area="" asp-controller="Account" asp-action="Login">Log in</a></li>
</ul>  

Passing parameters to view components using InvokeAsync

Our component is all set up to conditionally show or hide the register link, all that remains is to invoke it.

Passing parameters to a view component is achieved using anonymous types. In our layout, we specify the parameters in an optional parameter passed to InvokeAsync:

<div class="navbar-collapse collapse">  
    <ul class="nav navbar-nav">
        <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
        <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
        <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
    </ul>
    @await Component.InvokeAsync("LoginStatus", new { shouldShowRegisterLink = false })
</div>  

With this in place, the register link can be shown:

How to pass parameters to a view component

or hidden:

How to pass parameters to a view component

If you omit the anonymous type, then the parameters will all have their default values (false for our bool, but null for objects).

Passing parameters to view components when invoked from a controller

Passing parameters to a view component when invoked from a controller is very similar - just pass an anonymous method with the appropriate values when creating the controller:

public IActionResult IndexVC()  
{
    return ViewComponent("LoginStatus", new { shouldShowRegisterLink = false })
}

Passing parameters to view components when invoked as a tag helper in ASP.NET Core 1.1.0

In the previous post I showed how to invoke view components as tag helpers. The parameterless version of our invocation looks like this:

<div class="navbar-collapse collapse">  
    <ul class="nav navbar-nav">
        <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
        <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
        <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
    </ul>
    <vc:login-status></vc:login-status>
</div>  

Passing parameters to a view component tag helper is the same as for normal tag helpers. You convert the parameters to lower-kebab case and add them as attributes to the tag, e.g.:

<vc:login-status should-show-register-link="false"></vc:login-status>  

This gives a nice syntax for invoking our view components without having to drop into C# land use @await Component.InvokeAsync(), and will almost certainly become the preferred way to use them in the future.

Summary

In this post I showed how you can pass parameters to a view component. When invoking from a view in ASP.NET Core 1.0.0 or from a controller, you can use an anonymous method to pass parameters, where the properties are the name of the parameters.

In ASP.NET Core 1.1.0 you can use the alternative tag helper invocation method to pass parameters as attributes. Just remember to use lower-kebab-case for your component name and parameters! You can find sample code for this approach on GitHub.


Viewing all articles
Browse latest Browse all 743

Trending Articles