Search code examples
asp.netasp.net-coreasp.net-core-tag-helpers

Asp.Net Core 2 Complex Tag Helper


I'm new with Asp.Net Core and I'm having problems with Tag Helpers.

I'm trying to create a Tag Helper for 2 scenarios: - Input group - Checkbox

When I start from an asp-for tag I have no problem building other tags, but I don't know how to build "intelligent" tags such as an input tag with the asp-for field and all the validations.

Here is what I'm trying to build:

INPUT GROUP

<div class="input-group">
    <input class="form-control" placeholder="Search" type="text" data-val="true" data-val-maxlength="Max value" data-val-maxlength-max="5" id="SearchText" name="SearchText" value="" maxlength="5">
    <span class="input-group-append"><button class="btn btn-light" id="myButtonId" type="button">Search</button></span>
</div>

And this is what I have now:

<div class="input-group">
    <input asp-for="SearchText" class="form-control" placeholder="Buscar" add-on-button="myButtonId" add-on-append="true" />
</div>

And this is the Tag Helper

[HtmlTargetElement("input", Attributes = "add-on-button,add-on-append", ParentTag = "div")]
public class TextBoxButtonHelper : TagHelper
{
    public string AddOnButton { get; set; }
    public bool AddOnAppend { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        var tagAddOn = new TagBuilder("span");
        tagAddOn.AddCssClass(AddOnAppend ? "input-group-append" : "input-group-prepend");

        var tagButton = new TagBuilder("button");
        tagButton.Attributes.Add("id", AddOnButton);
        tagButton.Attributes.Add("class", "btn btn-light");
        tagButton.Attributes.Add("type", "button");
        tagButton.InnerHtml.Append(context.AllAttributes["placeholder"]?.Value.ToString());

        tagAddOn.InnerHtml.AppendHtml(tagButton);

        if (AddOnAppend)
            output.PostElement.AppendHtml(tagAddOn); 
        else
            output.PreElement.AppendHtml(tagAddOn);
    }
}

What I'd like to have would be something like this:

<input-tag asp-for="SearchText" class="form-control" placeholder="Buscar" button="myButtonId" append="true" />

So just one tag that would generate all the code, but I don't know how to generate the "TextBoxFor" inside the Tag Helper.

CHECKBOX The same problem with the chechbox, I need to create this from just one tag:

<div class="custom-control custom-control-primary custom-checkbox">
    <input class="custom-control-input" data-val="true" data-val-required="The SearchActive field is required." id="SearchActive" name="SearchActive" type="checkbox" value="true">
    <label class="custom-control-label" for="SearchActive">Solo activos</label>
    <input name="SearchActive" type="hidden" value="false">
</div>

Thanks for helping...


Solution

  • Thank you for your answers, but none of the solutions you gave answered my question, but helped finding the solution.

    This is the Tag helper for both, it works fine, in case someone wants to use it.

    INPUT GROUP WITH BUTTON + INPUT

    The html we are looking for is:

    <div class="input-group">
        <input placeholder="Search" class="form-control" type="text" data-val="true" data-val-maxlength="Max" data-val-maxlength-max="5" id="SearchText" name="SearchText" value="" maxlength="5">
        <span class="input-group-append">
            <button class="btn btn-light" id="btnSearch" type="button">Search</button>
        </span>
    </div>
    

    The final helper tag would be:

    <input asp-for="SearchText" placeholder="Search" add-on-button="btnSearch" add-on-button-text="Search" add-on-button-icon="ti-search" add-on-append="true" class="form-control" />
    

    As you see, we can indicate the name of the button, the text and/or icon (we can build a button with icon, text or both) and also we can indicate if the button goes at the beginning or at the end.

    This is the Tag Helper:

    [HtmlTargetElement("input", Attributes = "add-on-button,add-on-button-text,add-on-button-icon,add-on-append")]
    public class TextBoxButtonTag : TagHelper
    {
        public string AddOnButton { get; set; }
        public string AddOnButtonText { get; set; }
        public string AddOnButtonIcon { get; set; }
        public bool AddOnAppend { get; set; }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            var tagSpan = new TagBuilder("span");
            tagSpan.AddCssClass(AddOnAppend ? "input-group-append" : "input-group-prepend");
    
            var tagButton = new TagBuilder("button");
            tagButton.Attributes.Add("id", AddOnButton);
            tagButton.Attributes.Add("class", "btn btn-light");
            tagButton.Attributes.Add("type", "button");
    
            if (!string.IsNullOrEmpty(AddOnButtonIcon))
            {
                var tagButtonIcon = new TagBuilder("i");
                tagButtonIcon.Attributes.Add("class", string.Concat("ti-search", string.IsNullOrEmpty(AddOnButtonText) ? "" : " pr-1"));
                tagButtonIcon.Attributes.Add("style", "vertical-align: middle");
                tagButton.InnerHtml.AppendHtml(tagButtonIcon);
            }
    
            tagButton.InnerHtml.AppendHtml(AddOnButtonText);
    
    
            tagSpan.InnerHtml.AppendHtml(tagButton);
    
            output.PreElement.AppendHtml(@"<div class='input-group'>");
            if (AddOnAppend)
                output.PostElement.AppendHtml(tagSpan); 
            else
                output.PreElement.AppendHtml(tagSpan);
            output.PostElement.AppendHtml("</div>");
        }
    }
    

    CHECKBOX WITH SPECIAL DESIGN

    The html we are looking for is:

    <div class="custom-control custom-control-primary custom-checkbox">
        <input type="checkbox" data-val="true" data-val-required="The SearchActive field is required." id="SearchActive" name="SearchActive" value="true" class="custom-control-input">
        <label class="custom-control-label" for="SearchActive">Only active</label>
        <input name="SearchActive" type="hidden" value="false">
    </div>
    

    The final helper tag would be:

    <input asp-for="SearchActive" add-on-checkbox="Only active" />
    

    This is the Tag Helper:

    [HtmlTargetElement("input", Attributes = "add-on-checkbox")]
    public class CheckBoxTag : TagHelper
    {
        [HtmlAttributeName("asp-for")]
        public ModelExpression Source { get; set; }
    
        public string AddOnCheckbox { get; set; }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.Add("class", "custom-control-input");
    
            var tagLabel = new TagBuilder("label");
            tagLabel.AddCssClass("custom-control-label");
            tagLabel.Attributes.Add("for", Source.Name);
            tagLabel.InnerHtml.Append(AddOnCheckbox);
    
            var tagHidden = new TagBuilder("input");
            tagHidden.Attributes.Add("name", Source.Name);
            tagHidden.Attributes.Add("type", "hidden");
            tagHidden.Attributes.Add("value", "false");
    
            output.PreElement.AppendHtml(@"<div class='custom-control custom-control-primary custom-checkbox'>");
            output.PostElement.AppendHtml(tagLabel);
            output.PostElement.AppendHtml(tagHidden);
            output.PostElement.AppendHtml("</div>");
        }
    }
    

    In order to work, for both cases, you need to indicate all of the properties. There is a way to make optional those properties but I'm not implementing here.

    I hope this helps someone since I haven't found any article telling how to do this.

    Thanks for your helping.