Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Handle multiple forms in asp.net core razor pages

Suggested Videos
Part 14 - Editing data in asp.net core razor pages | Text | Slides
Part 15 - Updating data in asp.net core razor pages | Text | Slides
Part 16 - File upload in asp.net core razor pages | Text | Slides

In this video we will discuss how to handle multiple forms in asp.net core razor pages.

Handler Method Selection in Razor Pages


ASP.NET core razor pages framework uses naming conventions to select a handler method. Consider the following Edit razor page.


handle multiple forms in asp.net core razor pages

To load this Edit razor page, browser issues a GET request. To handle this GET request, asp.net core razor pages framework looks for a method that is named OnGet() in the corresponding PageModel class.

When the Update button is clicked, browser issues a POST request. To handle this POST request, asp.net core razor pages framework looks for a method that is named OnPost() in the corresponding PageModel class.

So, the following is the naming convention used by the razor pages framework to find the handler method. The word "On" is prefixed to the request verb. So for the GET request, it looks for OnGet and for POST request, it is OnPost.

Multiple forms on a Razor Page

What if we have 2 or more forms on a given razor page. We have 2 forms on the following razor page.
  • Form 1 - Used for updating notification preferences
  • Form 2 - Used for updating employee data
asp.net core razor pages multiple forms
  • On Form 1 - We have Update Notification Preferences button
  • On Form 2 - We have Update button
In the PageModel class we have the following 2 handler methods

public void OnPostUpdateNotificationPreferences()
{
    // Code to Update Notification Preferences Data
}

public IActionResult OnPost(Employee employee)
{
    // Code to Update Employee Data
}

On Form 1, when Update Notification Preferences button is clicked we want OnPostUpdateNotificationPreferences() method to handle the request. We specify this using asp-page-handler tag helper on the <form> element as shown below. OnPost prefix is not required, but you can specify it if you want to.

<form method="post" asp-page-handler="UpdateNotificationPreferences">
    @*Form 1 - Notification Preferences*@
</form>

On Form 2, when Update button is clicked we want OnPost() method to handle the request. We specify this using asp-page-handler tag helper on the <form> element as shown below.

<form method="post" asp-page-handler="OnPost">
    @*Form 2 - Employee Data*@
</form>

When Update Notification Preferences button is clicked, the handler name is passed in the URL as a query string parameter.

https://localhost:12345/employees/edit/2/?handler=updatenotificationpreferences

If you prefer to pass the handler name as a route parameter instead of as a query string parameter, include a route parameter in the display template.

@page "{id:min(1)}/{handler?}"

With this change, the handler name is now passed in the URL as a route parameter.

https://localhost:44383/employees/edit/2/updatenotificationpreferences/

Edit.cshtml

@page "{id:min(1)}/{handler?}"
@model RazorPagesTutorial.Pages.Employees.EditModel
@{
    ViewData["Title"] = "Edit";
    var photoPath = "~/images/" + (Model.Employee.PhotoPath ?? "noimage.jpg");
}
<h1>Notification Preferences</h1>

<form method="post" asp-page-handler="UpdateNotificationPreferences">
    <div class="form-check">
        <input asp-for="Notify" class="form-check-input" />
        <label asp-for="Notify" class="form-check-label">
            Receive email notification when my details change
        </label>
    </div>
    <button type="submit" class="btn btn-primary">
        Update Notification Preferences
    </button>
    @if (!string.IsNullOrEmpty(Model.Message))
    {
        <div class="alert alert-primary">
            @Model.Message
        </div>
    }
</form>

<hr />

<h1>Edit</h1>

<form method="post" class="mt-3" enctype="multipart/form-data"
      asp-page-handler="OnPost">
    <input hidden asp-for="Employee.Id" />
    <input hidden asp-for="Employee.PhotoPath" />

    <div class="form-group row">
        <label asp-for="Employee.Name" class="col-sm-2 col-form-label">
        </label>
        <div class="col-sm-10">
            <input asp-for="Employee.Name" class="form-control" placeholder="Name">
        </div>
    </div>
    <div class="form-group row">
        <label asp-for="Employee.Email" class="col-sm-2 col-form-label"></label>
        <div class="col-sm-10">
            <input asp-for="Employee.Email" class="form-control" placeholder="Email">
        </div>
    </div>
    <div class="form-group row">
        <label asp-for="Employee.Department" class="col-sm-2 col-form-label"></label>
        <div class="col-sm-10">
            <select asp-for="Employee.Department" class="custom-select mr-sm-2"
                    asp-items="Html.GetEnumSelectList<Dept>()">
                <option value="">Please Select</option>
            </select>
        </div>
    </div>

    <div class="form-group row">
        <label asp-for="Photo" class="col-sm-2 col-form-label"></label>
        <div class="col-sm-10">
            <div class="custom-file">
                <input asp-for="Photo" class="custom-file-input form-control">
                <label class="custom-file-label">Click here to change photo</label>
            </div>
        </div>
    </div>

    <div class="form-group row col-sm-4 offset-4">
        <img class="imageThumbnail" src="@photoPath" asp-append-version="true" />
    </div>

    <div class="form-group row">
        <div class="col-sm-10">
            <button type="submit" class="btn btn-primary">Update</button>
            <a asp-page="/Employees/Index" class="btn btn-primary">Cancel</a>
        </div>
    </div>

    @section Scripts {
        <script>
            $(document).ready(function () {
                $('.custom-file-input').on("change", function () {
                    var fileName = $(this).val().split("\\").pop();
                    $(this).next('.custom-file-label').html(fileName);
                });
            });
        </script>
    }
</form>

Edit.cshtml.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesTutorial.Models;
using RazorPagesTutorial.Services;
using System;
using System.IO;

namespace RazorPagesTutorial.Pages.Employees
{
    public class EditModel : PageModel
    {
        private readonly IEmployeeRepository employeeRepository;
        private readonly IWebHostEnvironment webHostEnvironment;

        public EditModel(IEmployeeRepository employeeRepository,
                         IWebHostEnvironment webHostEnvironment)
        {
            this.employeeRepository = employeeRepository;
            this.webHostEnvironment = webHostEnvironment;
        }

        public Employee Employee { get; set; }

        [BindProperty]
        public IFormFile Photo { get; set; }

        [BindProperty]
        public bool Notify { get; set; }

        public string Message { get; set; }

        public IActionResult OnGet(int id)
        {
            Employee = employeeRepository.GetEmployee(id);

            if (Employee == null)
            {
                return RedirectToPage("/NotFound");
            }

            return Page();
        }

        public IActionResult OnPost(Employee employee)
        {
            if (Photo != null)
            {
                if (employee.PhotoPath != null)
                {
                    string filePath = Path.Combine(webHostEnvironment.WebRootPath,
                        "images", employee.PhotoPath);
                    System.IO.File.Delete(filePath);
                }

                employee.PhotoPath = ProcessUploadedFile();
            }

            Employee = employeeRepository.Update(employee);
            return RedirectToPage("Index");
        }

        public void OnPostUpdateNotificationPreferences(int id)
        {
            if (Notify)
            {
                Message = "Thank you for turning on notifications";
            }
            else
            {
                Message = "You have turned off email notifications";
            }

            Employee = employeeRepository.GetEmployee(id);
        }

        private string ProcessUploadedFile()
        {
            string uniqueFileName = null;

            if (Photo != null)
            {
                string uploadsFolder = Path.Combine(webHostEnvironment.WebRootPath, "images");
                uniqueFileName = Guid.NewGuid().ToString() + "_" + Photo.FileName;
                string filePath = Path.Combine(uploadsFolder, uniqueFileName);
                using (var fileStream = new FileStream(filePath, FileMode.Create))
                {
                    Photo.CopyTo(fileStream);
                }
            }

            return uniqueFileName;
        }
    }
}

asp.net core razor pages tutorial

3 comments:

  1. Handling multiple forms was very helpful in understanding how to use more
    than one OnPost handler.
    For cascading dropdowns, I have thought of using cookies and session.
    Examples that I am tring to implement are selecting Campus, building, room,
    or Continent, country, state and have them on the same page. All data is from database.
    When using .NET webforms, the solution is straight forward, because of viewstate and postback
    on dropdown.
    Do we have to use cookies and Session to maintain state for cascading dropdowns?

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Just to let you guys know, (in 3.1 at least) you can't skip 'OnPost' in '.cs' file and you can't include 'OnPost' in 'asp-page-handler', otherwise in won't bind properly.

    ReplyDelete

It would be great if you can help share these free resources