Introduction to ASP.NET Core part 16: tag helpers cont’d with forms

Introduction

In the previous post we started discussing a new feature in .NET Core MVC, namely tag helpers. There are numerous tag libraries available out there but for .NET it’s something new. The built-in tag library allow us to create dynamic URL using more HTML elements and attributes and less C#. The current version of the tag library offers many HTML-based alternatives of the Html helpers. The tag library elements are transformed into real HTML on the server side so the end users never get to see them and there’s no need for any magic JavaScript library to convert attributes such as “asp-action” into “href” on the client side. Occasionally though we have to revert back to using the Html helpers since not all functionality is available through the tag library yet. We can however expect it to grow more and more in the future as .NET Core matures. Using either the Html helpers or the tag library is largely a matter of taste. If you’re fine with a lot of C# in the Razor views then go with the Html helpers.

In this post we’ll continue to explore the built-in tag library and build a form using the asp tags.

Form related tags in the library

We have the following C#/HTML markup in Create.cshtml which creates a form to insert new books:

@using (Html.BeginForm())
{   
    @Html.ValidationSummary()
    <div>
        @Html.LabelFor(m => m.Author)
        @Html.TextBoxFor(m => m.Author)
        @Html.ValidationMessageFor(m => m.Author)
    </div>
    <div>
        @Html.LabelFor(m => m.Title)
        @Html.EditorFor(m => m.Title)
        @Html.ValidationMessageFor(m => m.Title)
    </div>
    <div>
        @Html.LabelFor(m => m.Price)
        @Html.TextBoxFor(m => m.Price)
        @Html.ValidationMessageFor(m => m.Price)
    </div>
    <div>
        @Html.LabelFor(m => m.NumberOfPages)
        @Html.TextBoxFor(m => m.NumberOfPages)
        @Html.ValidationMessageFor(m => m.NumberOfPages)
    </div>
    <div>
        @Html.LabelFor(m => m.Genre)
        @Html.DropDownListFor(m => m.Genre, Html.GetEnumSelectList(typeof(DotNetCoreBookstore.Domains.Genre)))
    </div>
    <div>
        <input type="submit" name="create-book" value="Save" />
    </div>
}

The goal is to transform as much of that markup as possible into its tag library equivalent. Let’s take it bit by bit. Html.BeginForm is replaced by the “form” element of the tag library. The for element has the “asp-” attributes to indicate the action and controller names. In our case it’s not necessary to declare either of them since we’re on the same controller and same action method. We’ll use the asp-action and asp-controller attributes anyway just for documentation purposes. A difference between the Html helper and the tag library helper is that we explicitly have to declare the insertion of an anti-forgery token whereas this happens automatically with the BeginForm helper method. This is where we are to begin with:

<h1>Insert a new book with the tag library</h1>
<form asp-action="Create" asp-antiforgery="true" asp-controller="Books">

</form>

Next comes the label, the editor and the validation message. A label is simply a “label” and the editor is represented by an “input” element in the tag library. The more interesting bit is the binding. In LabelFor and EditorFor we declare the property with a lambda expression such as…

@Html.LabelFor(m => m.Genre)

The equivalent attribute is called “asp-for” in the Razor tag library. The attribute is followed by the name of the property in the model. You’ll get help from IntelliSense as well while typing out the property names.

There’s no separate element for validation messages, it’s simply a span with a tag library attribute “asp-validation-for”. Here’s the markup for all properties except for the Genre:

<h1>Insert a new book with the tag library</h1>
<form asp-action="Create" asp-antiforgery="true" asp-controller="Books">
    @Html.ValidationSummary()
    <div>
        <label asp-for="Author"></label>
        <input asp-for="Author" />
        <span asp-validation-for="Author"></span>
    </div>
    <div>
        <label asp-for="Title"></label>
        <input asp-for="Title" />
        <span asp-validation-for="Title"></span>
    </div>
    <div>
        <label asp-for="Price"></label>
        <input asp-for="Price" />
        <span asp-validation-for="Price"></span>
    </div>
    <div>
        <label asp-for="NumberOfPages"></label>
        <input asp-for="NumberOfPages" />
        <span asp-validation-for="NumberOfPages"></span>
    </div>
    <div>
        <input type="submit" name="create-book" value="Save" />
    </div>
</form>

Note that even if there’s no content between the opening and closing label and span elements we still have to close them with a proper closing element:

<label asp-for="NumberOfPages"></label>

The following will cause the label to not show up in the screen:

<label asp-for="NumberOfPages" />

A drop down list is represented by a select element in the tag library. Apart from the asp-for attribute it also has asp-items to declare the list of select list items. Its value will be the same Html.GetEnumSelectList function as before, there’s no separate solution for that in the tag library. Here’s the form in its entirety:

<h1>Insert a new book with the tag library</h1>
<form asp-action="Create" asp-antiforgery="true" asp-controller="Books">
    @Html.ValidationSummary()
    <div>
        <label asp-for="Author"></label>
        <input asp-for="Author" />
        <span asp-validation-for="Author"></span>
    </div>
    <div>
        <label asp-for="Title"></label>
        <input asp-for="Title" />
        <span asp-validation-for="Title"></span>
    </div>
    <div>
        <label asp-for="Price"></label>
        <input asp-for="Price" />
        <span asp-validation-for="Price"></span>
    </div>
    <div>
        <label asp-for="NumberOfPages"></label>
        <input asp-for="NumberOfPages" />
        <span asp-validation-for="NumberOfPages"></span>
    </div>       
    <div>
        <label asp-for="Genre"></label>
        <select asp-for="Genre" asp-items="@Html.GetEnumSelectList(typeof(DotNetCoreBookstore.Domains.Genre))"></select>
    </div>
    <div>
        <input type="submit" name="create-book" value="Save" />
    </div>
</form>

Go ahead and test this form in the demo application, it will work in exactly the same way.

We’ll continue with custom tags in the next post.

View the list of MVC and Web API related posts here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

One Response to Introduction to ASP.NET Core part 16: tag helpers cont’d with forms

  1. Zhen Jia says:

    Can select tag helper be validated through data annotation? like input tag helper?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

Bite-size insight on Cyber Security for the not too technical.

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: