Last Updated: 3/3/2018

Background

I dipped my toes into the ASP.NET ecosystem in 2004—a time during which the Web Forms application model’s popularity was fueled by a cult-like following. In the Microsoft web technology stack, Web Forms was deemed the mainstay of enterprise-grade, line-of-business applications. With a weak knowledge of web standards, Web Forms became my faithful crutch for quickly building quality applications. The three pillars of the web (HTML, CSS, and JavaScript) were largely abstracted away from me. Until my web development skills matured a bit, I was content with straying from the “bare metal” of the web. I needed to pay rent and chase unrealistic project deadlines.

Web Forms boasts a plethora of robust, native server controls—LinkButton and GridView, to name a few. This suite of controls remains one of the most appealing facets of Web Forms today, through its enablement of rapid web application development.

Fast forward to early 2011. My knowledge of web standards had progressed, and I wanted to employ those skills in my day job. I began evaluating MVC 3 as the application model successor for my future project work. One thing became clear. The Web Forms philosophies posed a significant barrier to entry. I longed for the server controls which Web Forms provided out of the box. Convincing my frugal manager to purchase a third-party suite of controls was improbable.

Web Forms may have become passé to many of us, yet certain elements strongly influence the seasoned ASP.NET developers of today. The LinkButton control is one such element which has left a legacy. For various reasons, I’ve since moved on from ASP.NET 4.x to ASP.NET Core—a reinvigorated framework in which Web Forms doesn’t exist.

The Problem

Where am I going with this? I was recently chatting with a friend and colleague at Microsoft, Isaac Levin. Isaac was working with a customer on an ASP.NET Core Razor Pages application and wondered whether the Anchor Tag Helper offers a mechanism for posting a form.

To put this into perspective, consider the following page model in a Razor Pages application:


public class IndexModel : PageModel
{
public void OnGet()
{
}
public void OnPost()
{
}
}

view raw

Index.cshtml.cs

hosted with ❤ by GitHub

The page model’s associated Index.cshtml file contains the following markup:


<form method="post">
<button asp-page="/Index" asp-page-handler="Post">Submit Form</button>
</form>

view raw

Index.cshtml

hosted with ❤ by GitHub

Upon clicking the Submit Form button, the page model’s SELRES_018f778b-da93-4b26-ac0e-8156268134afSELRES_21bf4bc2-4909-4c91-8f5c-0009283acbd0SELRES_a2fc22ca-02c0-4dee-b84a-4368d4f7e18eOnPostSELRES_a2fc22ca-02c0-4dee-b84a-4368d4f7e18eSELRES_21bf4bc2-4909-4c91-8f5c-0009283acbd0SELRES_018f778b-da93-4b26-ac0e-8156268134af method is invoked. Wouldn’t it be nice if we had this same behavior with an anchor tag?

Web Forms provides this functionality via the LinkButton. Consider the following markup:


<asp:LinkButton ID="lbtnSubmit" runat="server" PostBackUrl="~/About.aspx" Text="Submit Form" />

view raw

Default.aspx

hosted with ❤ by GitHub

The preceding code snippet resides in Default.aspx, which is injected into a master page containing a form. This LinkButton control posts the parent form, contained within the master page, to About.aspx upon clicking the associated Submit Form link. It’s the PostBackUrl property that we’re seeking in ASP.NET Core.

After a quick consultation with Taylor Mullen, a Microsoft engineer supporting Tag Helpers, it was determined this isn’t possible out of the box. Some basic JavaScript is required to programmatically submit the form upon click of the link. I pondered this problem for the next few days and decided to write a Tag Helper to support this scenario. Surely, if Isaac and I saw a need for it in the real world, others would eventually want the same feature.

The Solution

I created a custom LinkButtonTagHelper class, appropriately named after the Web Forms control from which the idea was born. As an aside, I’ve blogged about Tag Helper creation before here. And to avoid rehashing how to build a Tag Helper, I encourage you to visit the definitive source here.

To briefly recap my solution, I created the following C# class:


[HtmlTargetElement("link-button")]
public class LinkButtonTagHelper : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
const string HIDDEN_FIELD_ID = "form_marker";
output.TagName = "a";
output.Attributes.SetAttribute("onclick",
$@"document.getElementById(""{HIDDEN_FIELD_ID}"").form.submit();");
output.PostElement.SetHtmlContent($@"<input type=""hidden"" id=""{HIDDEN_FIELD_ID}"" />");
}
}

It’s the Process method that does the heavy lifting. Notice it produces an anchor tag and attaches an onclick event. The onclick event finds a hidden field, which is also added by this Tag Helper, and submits the hidden field’s parent form.

I configured CI and CD in VSTS via a build and a release definition, which are responsible for building the Tag Helper and packing & publishing a NuGet package. The fruits of that labor are found in the TagHelperSuite NuGet package.

After installing the TagHelperSuite package, the LinkButtonTagHelper is registered with the following directive:


@addTagHelper *, TagHelperSuite

The Web Forms LinkButton behavior is restored by using the <link-button> tag inside of a form:


<form asp-page-handler="Post">
<link-button>Test Link</link-button>
</form>

view raw

Index.cshtml

hosted with ❤ by GitHub

For more details on this solution, see the associated GitHub repository here.

3 Comments »

Leave a comment