Lesson 3
Multi-step Forms with State Management
Introduction

In this lesson, we will explore how to create multi-step forms in Razor Pages and manage state between steps. Multi-step forms are useful for splitting complex forms into smaller, more manageable sections, enhancing the user experience by not overwhelming users with too much information at once.

State management is critical in multi-step forms because it allows us to remember the information entered by the user across different steps. This lesson will guide you through building a simple two-step form using Razor Pages in ASP.NET Core and managing the form state using a singleton service.

Let's get started by creating a state management class and configuring the service in our project.

Configuring State Management

To manage form state across steps, we will use a singleton service. This ensures that the form data persists as the user navigates between steps. First, define the FormState class to store form data:

C#
1namespace MultiStep.Data 2{ 3 public class FormState 4 { 5 public string Step1Data { get; set; } 6 public string Step2Data { get; set; } 7 } 8}

Next, ensure that the FormState service is added to the dependency injection container in your Program.cs file:

C#
1// Add a simple in-memory object to store form state 2builder.Services.AddSingleton<FormState>();
Creating the First Step Form

We'll start by creating the first step of our multi-step form. We create a Step1.cshtml file and add the following code:

HTML, XML
1@page 2@model Step1Model 3<form method="post"> 4 <div class="form-group"> 5 <label asp-for="Step1Data" class="control-label">Input Step 1 Data:</label> 6 <input asp-for="Step1Data" name="Step1Data" class="form-control" /> 7 <span asp-validation-for="Step1Data" class="text-danger"></span> 8 </div> 9 <button type="submit" class="btn btn-primary">Next</button> 10</form> 11<a href="/">Back to Home</a>

In this code, we have created a form with an input field for the first step data. The asp-for tag helpers are used to bind the input field to the Step1Data property in the model. The asp-validation-for tag helper displays validation errors, if any.

Adding Validation with Data Annotations in Step 1

Next, we will add validation to the Step1Model. We create the Step1.cshtml.cs file and modify it as follows:

C#
1using Microsoft.AspNetCore.Mvc; 2using Microsoft.AspNetCore.Mvc.RazorPages; 3using MultiStep.Data; 4using System.ComponentModel.DataAnnotations; 5 6public class Step1Model : PageModel 7{ 8 private readonly FormState _formState; 9 10 public Step1Model(FormState formState) 11 { 12 _formState = formState; 13 } 14 15 [BindProperty] 16 [Required] 17 public string Step1Data { get; set; } 18 19 public IActionResult OnPost() 20 { 21 if (!ModelState.IsValid) 22 { 23 return Page(); 24 } 25 26 _formState.Step1Data = Step1Data; 27 return RedirectToPage("Step2"); 28 } 29}

In this model, we use the [Required] data annotation to ensure that Step1Data is not left empty. The BindProperty attribute binds form data to the Step1Data property.

The form submission is managed by the OnPost method. When the form is submitted, this method validates the input data. If the data is valid, it updates the _formState.Step1Data with the form data and redirects to the second step.

Creating the Second Step Form

For the second step, we create the Step2.cshtml file and include the following code:

HTML, XML
1@page 2@model Step2Model 3 4@if (Model.ShowResult) 5{ 6 <h2>Form Submission Complete</h2> 7 <p><strong>Step 1 Data:</strong> @Model.Step1Data</p> 8 <p><strong>Step 2 Data:</strong> @Model.Step2Data</p> 9} 10else 11{ 12 <form method="post"> 13 <div class="form-group"> 14 <label asp-for="Step2Data" class="control-label">Input Step 2 Data:</label> 15 <input asp-for="Step2Data" name="Step2Data" class="form-control" /> 16 <span asp-validation-for="Step2Data" class="text-danger"></span> 17 </div> 18 <button type="submit" class="btn btn-primary">Submit</button> 19 </form> 20} 21<a href="/">Back to Home</a>

In this file, we created a form with an input field for the second step data and display the results after form submission if ShowResult is true.

Accessing State Data in `Step2.cshtml.cs`

Now, let's create the Step2.cshtml.cs file and modify it as follows:

C#
1using Microsoft.AspNetCore.Mvc; 2using Microsoft.AspNetCore.Mvc.RazorPages; 3using MultiStep.Data; 4using System.ComponentModel.DataAnnotations; 5 6public class Step2Model : PageModel 7{ 8 private readonly FormState _formState; 9 10 public Step2Model(FormState formState) 11 { 12 _formState = formState; 13 } 14 15 [BindProperty] 16 [Required] 17 public string Step2Data { get; set; } 18 19 public string Step1Data => _formState.Step1Data; 20 21 [BindProperty(SupportsGet = true)] 22 public bool ShowResult { get; set; } 23 24 public void OnGet() 25 { 26 Step2Data = _formState.Step2Data; 27 } 28 29 public IActionResult OnPost() 30 { 31 if (!ModelState.IsValid) 32 { 33 return Page(); 34 } 35 36 _formState.Step2Data = Step2Data; 37 return RedirectToPage(new { ShowResult = true }); 38 } 39}

In this model, we access the state data from the first step using the _formState object and display it in the view.

The OnPost method validates the second step data and updates the _formState.Step2Data property. If validation passes, it sets ShowResult to true and reloads the page to display the results.

And that's it! We now have a 2-step form that manages the intermediate steps and displays the data entered through both steps at the end. Adding more steps to this pipeline, if needed, is also fairly simple.

Summary and Next Steps

In this lesson, you learned how to create a multi-step form in Razor Pages and manage state across steps. We covered:

  • Writing HTML and Razor code for multi-step forms.
  • Adding validation using data annotations.
  • Managing state between form steps using a singleton service.
  • Handling form submissions and displaying results.

Managing state in multi-step forms improves user experience by preserving data entered by users across different steps. Congratulations on reaching the end of the course! To reinforce your learning, proceed to the practice exercises, and continue building on these foundational skills. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.