ASP.NET Core 6.0 - Homegrown TagHelpers

This article will describe the implementation of custom tag helpers. I will assume you have downloaded the ASP.NET Core 6.0 - Homegrown Analytics Project.

Filtering a boolean property with a checkbox facilitates 2 states: include and only. I developed a group of radio buttons to allow 3 states: include, exclude, and only. The PageHit has a boolean property named CookieConsent indicating non essential cookies are allowed. A Consented checkbox can query all pageHits or pageHits with CookieConsent.

[Display(Name = "Consented")]
public bool ConsentedOnly { get; set; }
if (consentedOnly) pageHitsQuery = pageHitsQuery.Where(ph => ph.CookieConsent).AsQueryable();

A 3 button radio button group and a ConsentedFilter int property can query all pageHits, pageHits with CookieConsent, and pageHits without CookieConsent.

[Display(Name = "Consented")]
public int ConsentedFilter { get; set; }
if (ConsentedFilter == 1) pageHitsQuery = pageHitsQuery.Where(ph => !ph.CookieConsent).AsQueryable();
if (ConsentedFilter == 2) pageHitsQuery = pageHitsQuery.Where(ph => ph.CookieConsent).AsQueryable();

We use Microsoft. AspNetCore. Mvc. TagHelpers all the time. When a html tag is recognized the tag text is bold. When an attribute is recognized the attribute text is bold. Most of the built in tag helpers target known html elements. I use the radio button boolean filter on many pages for a lot of properties. Creating a custom BoolFilterTagHelper is easier than initial research suggests. After reviewing MS Docs on Tag Helpers and Tag Helper Components, my expectations grew as I followed the example at MS Docs - Author Tag Helpers in ASP.NET Core. The EmailTagHelper example demonstrated you can create a custom tag name and PascalCase property names in C# get translated to kebab-case attributes in html.

using Microsoft.AspNetCore.Razor.TagHelpers;
namespace HomegrownAnalyticsNet6.Areas.Analytics.Models.TagHelpers;
/// <summary>
/// <see cref="ITagHelper" /> implementation of a radio button group with Include, Exclude, and Only radio buttons.
/// </summary>
public class BoolFilterTagHelper : TagHelper
{
    /// <summary>
    /// Radio button group name. Suffix of input ids. Must match an int property/parameter name on the page.
    /// </summary>
    public string FilterFor { get; set; } = string.Empty;
    /// <summary>
    /// Radio button group header text.
    /// </summary>
    public string FilterLabel { get; set; } = string.Empty;
    /// <summary>
    /// Indicates the checked radio button. 0 = Include, 1 = Exclude, or 2 = Only.
    /// </summary>
    public int FilterValue { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        if (string.IsNullOrEmpty(FilterFor))
            output.Content.SetHtmlContent("<h4>The bool-filter tag requires a filter-for attribute.</h4>");
        else
        {
            var includeChecked = FilterValue == 0 ? "checked" : "";
            var excludeChecked = FilterValue == 1 ? "checked" : "";
            var onlyChecked = FilterValue == 2 ? "checked" : "";

            string markup = @$"<div class='d-inline-flex flex-wrap p-0 ps-2 alert alert-primary align-top mb-1'>
                    <h6 class='d-inline mt-1 me-2 mb-0'>{FilterLabel}</h6>
                    <div class='form-check form-check-inline mt-1'>
                        <input type='radio' value='0' class='form-check-input' id='Include{FilterFor}'
                            {includeChecked} name='{FilterFor}'  onclick='this.form.submit();' />
                        <label class='form-check-label fw-bold' for='Include{FilterFor}'>
                            Include
                        </label>
                    </div>
                    <div class='form-check form-check-inline mt-1'>
                        <input type='radio' value='1' class='form-check-input' id='Exclude{FilterFor}' 
                            {excludeChecked} name='{FilterFor}' onclick='this.form.submit();' />
                        <label class='form-check-label fw-bold' for='Exclude{FilterFor}'>
                            Exclude
                        </label>
                    </div>
                    <div class='form-check form-check-inline mt-1'>
                        <input type='radio' value='2' class='form-check-input' id='Only{FilterFor}' 
                            {onlyChecked} name='{FilterFor}' onclick='this.form.submit();' />
                        <label class='form-check-label fw-bold' for='Only{FilterFor}'>
                            Only
                        </label>
                    </div>
                </div>";

            output.Content.SetHtmlContent(markup);
        }
    }
}

The BoolFilterTagHelper reduces the html for the 3 button radio button group to a single html tag. To make all custom TagHelper classes available to all Razor pages, implement the addTagHelper directive with a wildcard and the project name in all _ViewImports.cshtml files.

@using HomegrownAnalyticsNet6.Areas.Analytics.Models;
@using HomegrownAnalyticsNet6.Areas.Analytics.Models.Entities;
@using HomegrownAnalyticsNet6.Areas.Analytics.Services;
@using Microsoft.AspNetCore.Authorization;
@namespace HomegrownAnalyticsNet6.Areas.Analytics.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, HomegrownAnalyticsNet6

Meaningful xml comments provide BoolFilterTagHelper Intellisense.

TagHelper Intellisense.

I liked the BoolFilterTagHelper so much, I developed TagHelpers for SegmentsFilter, CurrentIpFilter, and DateTime. The DateTimeTagHelper implements a date-time picker.

Tag Helpers Html.

The images display the AnalyticsSampleData derived from a KenHaggerty.Com db backup.

Dashboard Desktop. Dashboard Mobile.
Width

Live Demonstration

I publish the Users Without Passwords Project (UWPP) on the FIDO subdomain and the Users With Comments Project (UWCP) on the Preview subdomain. I integrated the HAP with the published projects. The subdomains have their own databases. Registered users can request permission to access the Admin - Analytics Dashboard. The permission will be granted automatically. The UWCP implements the Users With Device 2FA Project (UWD2FAP). Preview requires 2FA enabled to access admin pages.

After you register, browse to Manage Account > Visit Log. Click the More link in the HAP banner. Request permission.

Preview Visit Log.
Preview Request Permission.
Ken Haggerty
Created 09/28/22
Updated 09/28/22 01:50 GMT

Log In or Reset Quota to read more.

Article Tags:

Analytics Model
Successfully completed. Thank you for contributing.
Processing...
Something went wrong. Please try again.
Contribute to enjoy content without advertisments.
You can contribute without registering.

Comments(0)

Loading...
Loading...

Not accepting new comments.

Submit your comment. Comments are moderated.

User Image.
DisplayedName - Member Since ?