Search code examples
c#asp.net-coretag-helpers

Asp .Net Core Bootstrap 4 Tabs tag helper


I'm trying to render this

<nav>
  <div class="nav nav-tabs" id="nav-tab" role="tablist">
    <a class="nav-item nav-link active" id="nav-home-tab" data-toggle="tab" href="#nav-home" role="tab" aria-controls="nav-home" aria-selected="true">Home</a>
    <a class="nav-item nav-link" id="nav-profile-tab" data-toggle="tab" href="#nav-profile" role="tab" aria-controls="nav-profile" aria-selected="false">Profile</a>
    <a class="nav-item nav-link" id="nav-contact-tab" data-toggle="tab" href="#nav-contact" role="tab" aria-controls="nav-contact" aria-selected="false">Contact</a>
  </div>
</nav>
<div class="tab-content" id="nav-tabContent">
  <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">...</div>
  <div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">...</div>
  <div class="tab-pane fade" id="nav-contact" role="tabpanel" aria-labelledby="nav-contact-tab">...</div>
</div>

into something like

<tabs>
    <tabs-nav>
        <tab></tab>
        <tab></tab>
    </tabs-av>
    <tabs-content>
        <tab-pane></tab-pane>
        <tab-pane></tab-pane>
    </tabs-content>
</tabs>

Problem is that i have no idea how can i achieve nested tags. I have made one for bootstrap card like this

 [HtmlTargetElement("card")]
    public class CardTagHelper : TagHelper
    {
        public string Title { get; set; }
        public string Icon { get; set; }
        public string Url { get; set; }
        public string Class { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            if (string.IsNullOrWhiteSpace(Class))
                Class = "custom-card";

            output.TagName = "CardTagHelper";
            output.TagMode = TagMode.StartTagAndEndTag;

            var preContent = new StringBuilder();
            preContent.AppendFormat($@"
                    <div class='card {Class}'>
                        <div class='card-header'>
                            <div class='card-title'>");

            if (Url != null)
            {
                preContent.AppendFormat($@"
                                <a href='{Url}'>
                                    <div class='float-left return'>
                                        <i class='fas fa-arrow-alt-circle-left d-block'></i>
                                    </div>
                                </a>");
            }

            preContent.AppendFormat($@"<i class='{Icon}'></i>
                                {Title}
                            </div>                   
                        </div>

                        <div class='card-body'>");

            var postContent = new StringBuilder();
            postContent.AppendFormat($@"                                   
                        </div>
                        <div class='overlay' style='display:none;'>
                            <i class='fas fa-spinner fa-spin text-bold' style='opacity:0.8;'></i>
                        </div>
                        <div class='card-footer'>
                        </div>
                    </div>");

            output.PreContent.SetHtmlContent(preContent.ToString());
            output.PostContent.SetHtmlContent(postContent.ToString());
        }
    }

Which basically lets me wrap any content i want in

<card title="" icon="" url=""> 

    //Content

</card>

I would like to know at least how can i create the nested tags as showed in the prefered output as I cannot find any example with something similar on microsoft docs

EDIT

So I implemented based on the answwer in here and it seems to achieve what i wanted

 [HtmlTargetElement("tabs-nav")]
    public class TabNavTagHelper : TagHelper
    {

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            await base.ProcessAsync(context, output);

            output.TagName = "ul";
            output.Attributes.Add("class", "nav nav-tabs");
            output.Attributes.Add("role", "tablist");

            output.Content.AppendHtml(await output.GetChildContentAsync(false));

        }
    }

[HtmlTargetElement("tabs")]
public class TabTagHelper : TagHelper
{

    public string Id { get; set; }
    public string Nome { get; set; }
    public string Class { get; set; }
    public bool Selected { get; set; }

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        await base.ProcessAsync(context, output);

        output.TagName = "li";
        output.TagMode = TagMode.StartTagAndEndTag;
        output.Attributes.Add("class", "nav-item");
        output.Content.AppendFormat($"<a class='nav-link {Class}' id='{Id}' data-toggle='tab' href='#home' role='tab' aria-controls='home' aria-selected='{Selected}'>{Nome}</a>");

    }

Solution

  • I couldn't really understand the question much, But I'm assuming that you have two tag helpers. One for tabs-nav and one for tab that do their own thing. To achieve something like this. In the ProcessAsync method of your tag helper, you need to get the child content and append that to the output. You do that like this.

    //This is the parent that helper
    [HtmlTargetElement("tabs-nav")]
    public class TabsNavTagHelper : TagHelper
    {
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            await base.ProcessAsync(context, output);
            //Your parent tag helper over logic here
            output.Content.AppendHtml(await output.GetChildContentAsync(false));
        }
    }
    //This is the child that helper
    [HtmlTargetElement("tabs")]
    public class TabTagHelper : TagHelper
    {
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            await base.ProcessAsync(context, output);
            //Your child tag helper over logic here
        }
    }