Lesson Sunday

In the last lesson, we added code to our ItemsController to create Items. In this lesson, we'll add the necessary views to allow users to create Items through a form. In the process, we'll cover several HTML helper methods. A helper method is exactly what it sounds like - a method that helps make our lives as developers a little easier. In this case, our helper methods will specifically make it easier to render HTML in a .cshtml file.


First, let's update the contents of our Views/Home/Index.cshtml. Let's remove the links to the categories and add a link to see our items instead. In the past, we created a standard HTML link like this:

<a href="/items">See all items</a>

Let's use an HTML helper method to render our link instead. Edit Index.cshtml to look like this:

Views/Home/Index.cshtml
@{
  Layout = "_Layout";
}

<h1>Welcome to the To Do List!</h1>
<p>@Html.ActionLink("See all items", "Index", "Items")</p> 

The ActionLink() method is called on @Html. It takes two required parameters as well as an optional third parameter.

  • The first parameter "See all items" is the display text for the link. In other words, a user will see a link that says "See all items".

  • The second parameter "Index" is the target action in the controller.

  • The third parameter Items is optional, and it specifies the controller that we want to route to. More specifically, our third parameter is the name of the controller without Controller appended to it. Since we've included this third parameter, this ActionLink will take us to the Index() route method in the ItemsController.

Take note. If we don't include the optional third parameter, like this:

<p>@Html.ActionLink("See all items", "Index")</p> 

The ActionLink method will default to the controller that corresponds to the view that the ActionLink method executes from. Since we're in a View belonging to Home, we would be routed to the HomeController's Index() route method (also known as an action).

Let's run the server to confirm that our new link works.

Updating the Items Views

Before we create our form, let's use a shiny new HTML helper method to add a link to our Views/Items/Index.cshtml view that will link to the form page.

Views/Items/Index.cshtml
...
<p>@Html.ActionLink("Add new item", "Create")</p>

This time around, we omit the third parameter as we are going to a route in the Items controller.

HTML Helper Methods: Forms


We now have a link to a view for our Create() method but we haven't created a view yet. In past projects, we used standard HTML to create forms. Let's use some new HTML helper syntax to make this easier:

Views/Items/Create.cshtml
@{
  Layout = "_Layout";
}

@model ToDoList.Models.Item

<h4>Add a new task</h4>
@using (Html.BeginForm())
{
  @Html.LabelFor(model => model.Description)
  @Html.TextBoxFor(model => model.Description)
  <input type="submit" value="Add new task" />
}
<p>@Html.ActionLink("Show all items", "Index")</p>

Our form makes use of several HTML helper methods, including another ActionLink() to link to our Index() route method. Let's take a closer look at the new helper methods included above.

Model Directives

We'll start with the first line, which is called a model directive:

@model ToDoList.Models.Item

A model directive tells our view what type of data will be passed into the view from the controller route. In this case, we're telling the view that it will receive a model that is an Item. These directives provide a simple way for our application to reference strongly-typed models.

We must be very specific in defining the type. Note that we include both the namespace and the class in that order: ToDoList.Models.Item. This forces the compiler to look in the right place for the type. Now we can use the model we specify in our view.

We will be using model directives regularly in our views, not just with forms, and we'll cover a few more examples soon.

HTML Helper Methods for Forms

Next, let's take a closer look at our new form:

@using (Html.BeginForm())
{
  @Html.LabelFor(model => model.Description)
  @Html.TextBoxFor(model => model.Description)
  <input type="submit" value="Add new task" />
}

Instead of adding a form using plain HTML, we use the helper method Html.BeginForm(). First, notice that the Html.BeginForm() method is called within a using statement: @using (Html.BeginForm()). What this using statement does is add a closing HTML </form> tag to the form. Alternatively, instead of creating a using block, you can call the Html.EndForm() helper method to close the </form> tag. We won't use this second method in our examples, but you are welcome to use it!

Next, it's important to note that BeginForm() sends an HTTP POST request by default to the current URL. In other words, this form will send a POST request to the Create() route. In HTML, the opening form tag would look like this:

<form action="/Items/Create" method="post">

If we were to load this page in the browser, we can inspect the form and see the above form action in the DOM's elements. This is the reason we no longer use RESTful routing! The default HTML helper method for a form will create a post request to the route matching the filename it was called in. Since we used the helper method in /Items/Create.cshtml, it will make a post request to /Items/Create. Similarly, we will be updating our routes to match what is in the Microsoft documentation for proper routing in ASP.NET Core projects, such as using Details instead of Show. You can look in the documentation for HTML helper methods to see how to add additional arguments, to modify the action, method, and arguments of forms, inputs, and links created with HTML helper methods, but for the sake of simplicity we will continue forward using this format.

In the form itself, we use two new methods: LabelFor() and TextBoxFor(). The first generates a label for a form field while the second generates a text box. They both take the following argument:

model => model.Description

This is called a lambda expression. In short, a lambda expression is just a way to write an anonymous function in a condensed fashion. Lambda expressions are used in a variety of languages and are similar to arrow functions in JavaScript.

Lambda expressions can also be parsed. The specifics of that are beyond the scope of this program, but are the way the HTML helper methods utilize the lambda expressions. In the case of HTML helpers for forms, we just need to know that the lambda will look like this: (model => model.<property-name>).

For more information about lambdas, check out the Microsoft documentation.

We can also use HTML helper methods to generate many other form elements as well. To learn more, check out the IHtmlHelper Interface documentation. The number of methods listed can be overwhelming but we recommend taking a look at the options.

Now that we've added a form, we can run our application and create new Items. We are able to create new items and view a list of items without a single line of SQL code.

Adding HTML Helper Methods to the Index View


Let's make a few more updates to Items/Index.cshtml. In the process, we'll get more practice with HTML helper methods. Here's our updated view:

Views/Items/Index.cshtml
@{
  Layout = "_Layout";
}

@using ToDoList.Models;
@model List<ToDoList.Models.Item>;

<h1>Items</h1>

<ul>
  @foreach(Item item in Model)
  {
    <li>@Html.ActionLink($"{item.Description}", "Details", new { id = item.ItemId })</li>
  }
</ul>

<p>@Html.ActionLink("Add new item", "Create")</p>
  • We start with a using directive followed by the model directive: @model List<ToDoList.Models.Item>;. Now our view has access to the necessary list of Items.

  • We also have a @foreach loop that includes an HTML helper method to link to a specific item's detail:

<li>@Html.ActionLink($"{item.Description}", "Details", new { id = item.ItemId })</li>

This ActionLink() method incorporates a technique called string interpolation. This is similar to using a template literal in JavaScript. When we use string interpolation, C# will evaluate an expression inside a string.

We can test this in the dotnet-script REPL. Enter $ dotnet-script into the command line to access the REPL, and then try this out:

> string greeting = "How are you?";
> string newGreeting = $"{greeting}";
> newGreeting
"How are you?"

In the example above, we pass the value of greeting into a string using string interpolation: $"{greeting}". Because we are using string interpolation, the expression is evaluated and passed into the string.

Our ActionLink() method will route to a Details route method (or action) in the ItemsController that we haven't created yet.

Finally, we're passing a different kind of variable as an optional extra argument to ActionLink(). The ActionLink method can take an object containing the data that will be passed as arguments to the Action. The code new { id = item.ItemId } creates an anonymous object with the property id. This is how .NET knows which id to use in the Details() action. However, keep in mind that property names of the object we pass in must match parameter names used by the target method. In this case, it will need to match the parameter names in our Details() route method id, like this: Details(int id).

If we run the server, we'll see that all of the items on our list are now clickable. However, we don't have a corresponding route in our controller yet. In the next lesson, we'll create this route along with a view.

In this lesson, we covered a number of different HTML helper methods, including model directives, link helper methods and form helper methods. While the new syntax may feel a little strange at first, these helper methods can make our lives much easier as developers.

Repository Reference

Follow the link below to view how a sample version of the project should look at this point. Note that this is a link to a specific branch in the repository.

Example GitHub Repo for To Do List

Lesson 25 of 36
Last updated March 23, 2022