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

Create form in asp.net core razor pages

Suggested Videos
Part 17 - Handle multiple forms in asp.net core razor pages | Text | Slides
Part 18 - TempData in ASP.NET Core | Text | Slides
Part 19 - ASP.NET Core razor pages validation | Text | Slides

In this video, we will discuss implementing a razor page that help us create a new employee.


In our previous videos in this series, we already implemented Edit razor page. The HTML markup and the code required to implement a Create razor page is very similar to an Edit razor page.

Instead of creating a separate Create razor page and duplicating the HTML and code, we are going to modify the code in Edit razor page so it can be used for both the workflows i.e 

  1. Adding a new employee 
  2. Editing an existing employee

IEmployeeRepository.cs

Include Add() method to add a new employee

public interface IEmployeeRepository
{
    IEnumerable<Employee> GetAllEmployees();
    Employee GetEmployee(int id);
    Employee Update(Employee updatedEmployee);
    Employee Add(Employee newEmployee);
}

MockEmployeeRepository.cs

Provide implementation for Add() method. At the moment, we are still working with in-memory data. So we have to manually compute the ID value of the new employee being added. 

When we add support for SQL server, we do no have to manually compute the employee id value. This will be automatically provided by SQL server when a new record is inserted. We will see this in action in our upcoming videos.

public class MockEmployeeRepository : IEmployeeRepository
{
    private List<Employee> _employeeList;

    public MockEmployeeRepository()
    {
        _employeeList = new List<Employee>()
        {
            new Employee() { Id = 1, Name = "Mary", Department = Dept.HR,
                Email = "mary@pragimtech.com", PhotoPath="mary.png" },
            new Employee() { Id = 2, Name = "John", Department = Dept.IT,
                Email = "john@pragimtech.com", PhotoPath="john.png" },
            new Employee() { Id = 3, Name = "Sara", Department = Dept.IT,
                Email = "sara@pragimtech.com", PhotoPath="sara.png" },
            new Employee() { Id = 4, Name = "David", Department = Dept.Payroll,
                Email = "david@pragimtech.com" },
        };
    }

    public IEnumerable<Employee> GetAllEmployees()
    {
        return _employeeList;
    }

    public Employee GetEmployee(int id)
    {
        return _employeeList.FirstOrDefault(e => e.Id == id);
    }

    public Employee Update(Employee updatedEmployee)
    {
        Employee employee = _employeeList.FirstOrDefault(e => e.Id == updatedEmployee.Id);

        if (employee != null)
        {
            employee.Name = updatedEmployee.Name;
            employee.Email = updatedEmployee.Email;
            employee.Department = updatedEmployee.Department;
            employee.PhotoPath = updatedEmployee.PhotoPath;
        }

        return employee;
    }

    public Employee Add(Employee newEmployee)
    {
        newEmployee.Id = _employeeList.Max(e => e.Id) + 1;
        _employeeList.Add(newEmployee);
        return newEmployee;
    }
}

_Layout.cshtml

implement create razor page

The navigation menu is in the Layout view. Include the following HTML for the Add menu item.

<li class="nav-item">
    <a class="nav-link text-dark" asp-area=""
        asp-page="/Employees/Edit">Add</a>
</li>

Edit.cshtml

@*When adding a new employee we would not pass any ID value.
    So, make the id route parameter optional.*@
@page "{id:min(1)?}/{handler?}"
@model RazorPagesTutorial.Pages.Employees.EditModel
@{
    ViewData["Title"] = "Edit";
    var photoPath = "~/images/" + (Model.Employee.PhotoPath ?? "noimage.jpg");
    // If Employee ID > 0, then we are editing an existing employee else creating new
    // employee. So set the pageHeader variable text accordingly
    var pageHeader = Model.Employee.Id > 0 ? "Edit" : "Create";
}
<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>
</form>

<hr />

@*The pageHeader variable is used here*@
<h1>@pageHeader</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 asp-validation-summary="All" class="text-danger">
    </div>

    <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">
            <span asp-validation-for="Employee.Name" class="text-danger"></span>
        </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">
            <span asp-validation-for="Employee.Email" class="text-danger"></span>
        </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">Save</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;
        }

        [BindProperty]
        public Employee Employee { get; set; }

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

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

        public string Message { get; set; }

        // Make the id parameter optional
        public IActionResult OnGet(int? id)
        {
            // if id parameter has value, retrieve the existing
            // employee details, else create a new Employee
            if (id.HasValue)
            {
                Employee = employeeRepository.GetEmployee(id.Value);
            }
            else
            {
                Employee = new Employee();
            }

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

            return Page();
        }

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

                    Employee.PhotoPath = ProcessUploadedFile();
                }

                // If Employee ID > 0, call Update() to update existing 
                // employee details else call Add() to add new employee
                if (Employee.Id > 0)
                {
                    Employee = employeeRepository.Update(Employee);
                }
                else
                {
                    Employee = employeeRepository.Add(Employee);
                }
                return RedirectToPage("Index");
            }

            return Page();
        }

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

            TempData["message"] = Message;

            return RedirectToPage("Details", new { id = 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;
        }
    }
}

Dropdownlist validation in ASP.NET Core

To make the Department dropdownlist field required

asp.net core dropdownlist validation

Employee.cs

Decorate Department property in the Employee class with the [Required] attribute.

public class Employee
{
    public int Id { get; set; }
    [Required, MinLength(3, ErrorMessage = "Name must contain at least 3 characters")]
    public string Name { get; set; }
    [Required]
    [Display(Name = "Office Email")]
    [RegularExpression(@"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
    ErrorMessage = "Invalid email format")]
    public string Email { get; set; }
    public string PhotoPath { get; set; }
    [Required]
    public Dept? Department { get; set; }
}

Edit.cshtml

Include asp-validation-for tag helper just below the Department dropdownlist.

<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>
        <span asp-validation-for="Employee.Department" class="text-danger"></span>
    </div>
</div>

asp.net core tutorial for beginners

3 comments:

  1. Hi Venkat,

    If we are already in edit page then we click on Add in top navigation then it is not routing to Add

    ReplyDelete
  2. That's because the 'id' value is retained within the route, one way to overcome this is to modify your anchor tag to use 'href' directly: 'href="@(ViewContext.HttpContext.Request.PathBase)/Employees/Edit"'

    ReplyDelete
  3. Here we are automatically getting validation for PhotoPath how to remove that

    ReplyDelete

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