Search code examples
asp.net-coreasp.net-core-3.0c#-8.0asp.net-core-tag-helpersasp.net-core-viewcomponent

C# Passing an object into a ViewComponent using a TagHelper


I'm trying to create a ViewComponent that I can use with TagHelpers where I pass in an object as the parameter.

So, for example, I have this ViewComponent—which could end up having more parameters:

public IViewComponentResult Invoke(string? pageTitle, string? description, string? headerView)

And I'd like to do something like:

public class PageHeaderViewComponentOptions 
{
    public string pageTitle2;
    public string Description;
}

public IViewComponentResult Invoke(PageHeaderViewComponentOptions phvc)

And use it like:

<vc:page-header phvc:page-title2="Test Title"></vc:page-header>

But, this isn't working.

I also tried explicitly setting the attribute information on the parameter class as follows:

[HtmlTargetElement("PageHeaderViewController")]
public class PageHeaderViewComponentOptions
{
    [HtmlAttributeName("page-title")]
    public string pageTitle2 { get; set; }
    public string Description;
}

With no luck either.

My thought is that I could pass that object directly to the view. Typing this, I wonder if passing in the parameters separately and then assigning each to a member of a specific view model might be a better idea.

Any help here would be greatly appreciated.


Solution

  • In order to add an object as a parameter you'll need to pass an object.

    This object can be created in the controller and passed as part of the view model, e.g.:

    @model MyApp.MyViewModel
    

    Where MyViewModel is something like:

    public class MyViewModel
    {
        public PageHeaderViewComponentOptions PhvcOptions { get; set; }
    }
    

    And you pass the object like this:

    <vc:page-header phvc="PhvcOptions"></vc:page-header>
    

    Inside of a razor code block:

    @{
        var PhvcOptions = new PageHeaderViewComponentOptions
            {
                pageTitle2 = "Test Title";
            };
    }
    

    Where you pass the object like this:

    <vc:page-header phvc="@PhvcOptions"></vc:page-header>
    

    Or even inline, which looks something like this:

    <vc:page-header phvc="@(new PageHeaderViewComponentOptions { pageTitle2 = "Test Title" })"></vc:page-header>
    

    Though that defeats the purpose and string input should be used instead:

    <vc:page-header pageTitle2="Test Title"></vc:page-header>
    

    The advantage of an object is that it may be null and fields of the object may be omitted. Where string parameters need to be present in the tag, even when empty.

    Inserting objects is IMO also useful when you can use a part of the view model as a parameter, as shown above, or when the view model implements different interfaces and can therefore be passed as the parameter itself:

    <vc:page-header phvc="Model"></vc:page-header>
    

    Where MyViewModel is something like:

    public class MyViewModel : IPageHeaderViewComponentOptions 
    {
        public string pageTitle2 { get; set; }
    }
    

    And

    public interface IPageHeaderViewComponentOptions 
    {
        string pageTitle2;
    }
    

    I didn't test the code and there may be typos. Especially with the @. But you get the idea. Either approach should work.