I want to automate the creation of certain links in my Razor Page application.
I came up with this tag helper class that attaches on the anchor tag. Ideally it should analyze the request object, extrapolate some extra properties and pass them as extra route values.
[HtmlTargetElement("a", Attributes = "sort-by", ParentTag = "th")]
public class SortableTableHeaderTagHelper(IHttpContextAccessor httpContextAccessor) : TagHelper
{
public override int Order => int.MinValue;
[HtmlAttributeName("sort-by")]
public string SortBy { get; set; } = default!;
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var request = httpContextAccessor.HttpContext!.Request;
var currentSortBy = request.Query["sortBy"].ToString();
var currentSortOrder = Enum.TryParse<SortDirection>(request.Query["sortOrder"].ToString(), out var sortOrderValue) ? sortOrderValue : SortDirection.Ascending;
var currentQuery = request.Query["query"].ToString();
var isAscending = currentSortOrder == SortDirection.Ascending;
var sortOrder = currentSortBy == SortBy && isAscending ? SortDirection.Descending : SortDirection.Ascending;
var iconClass = currentSortBy == SortBy ? (isAscending ? "fa-sort-up" : "fa-sort-down") : "fa-sort";
output.Attributes.SetAttribute("asp-route-sortBy", SortBy);
output.Attributes.SetAttribute("asp-route-sortOrder", sortOrder);
if (!string.IsNullOrWhiteSpace(currentQuery))
{
output.Attributes.SetAttribute("asp-route-query", currentQuery);
}
output.PostContent.AppendHtml(
$"""
<i class='fa {iconClass}'></i>
""");
}
}
The problem I am facing is that the AnchorTagHelper doesn't pick up the new attributes and generates the basic URL.
<a asp-page="/Contacts/Index" sort-by="company">
@Texts["TableHeaderCompany"]
</a>
Is rendered as
<a asp-route-sortby="company" asp-route-sortorder="Ascending" href="/contacts">Company<i class="fa fa-sort"></i></a>
Instead of
<a href="/contacts?sortBy=company&sortOrder=Ascending">Company<i class="fa fa-sort"></i></a>
Do you have any idea?
The reason why your codes doesn't work is you set a tag helper inside another tag helper which will not work. You need write the LinkGenerator to generate the link and set the herf with that value.
More details, you could refer to below example:
[HtmlTargetElement("a", Attributes = "sort-by")]
public class SortableTableHeaderTagHelper : TagHelper
{
private readonly LinkGenerator _linkGenerator;
private readonly IHttpContextAccessor _httpContextAccessor;
public SortableTableHeaderTagHelper(LinkGenerator linkGenerator, IHttpContextAccessor httpContextAccessor)
{
_linkGenerator = linkGenerator;
_httpContextAccessor = httpContextAccessor;
}
[HtmlAttributeName("sort-by")]
public string SortBy { get; set; } = default!;
[HtmlAttributeName("asp-page")]
public string? Page { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var request = _httpContextAccessor.HttpContext!.Request;
var currentSortBy = request.Query["sortBy"].ToString();
var currentSortOrder = Enum.TryParse<SortDirection>(request.Query["sortOrder"].ToString(), out var sortOrderValue) ? sortOrderValue : SortDirection.Ascending;
var currentQuery = request.Query["query"].ToString();
var isAscending = currentSortOrder == SortDirection.Ascending;
var sortOrder = currentSortBy == SortBy && isAscending ? SortDirection.Descending : SortDirection.Ascending;
var routeValues = new RouteValueDictionary
{
{ "sortBy", SortBy },
{ "sortOrder", sortOrder }
};
if (!string.IsNullOrWhiteSpace(currentQuery))
{
routeValues["query"] = currentQuery;
}
var url = _linkGenerator.GetPathByPage(Page, values: routeValues);
output.Attributes.SetAttribute("href", url);
var iconClass = currentSortBy == SortBy
? (isAscending ? "fa-sort-up" : "fa-sort-down")
: "fa-sort";
output.PostContent.AppendHtml(
$"""
<i class='fa {iconClass}'></i>
""");
}
}
public enum SortDirection
{
Ascending,
Descending
}
Result: