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
IEmployeeRepository.cs
Include Add() method to add a new employee
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.
_Layout.cshtml
The navigation menu is in the Layout view. Include the following HTML for the Add menu item.
Edit.cshtml
Edit.cshtml.cs
Dropdownlist validation in ASP.NET Core
To make the Department dropdownlist field required
Employee.cs
Decorate Department property in the Employee class with the [Required] attribute.
Edit.cshtml
Include asp-validation-for tag helper just below the Department dropdownlist.
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
- Adding a new employee
- 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);
}
{
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
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
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; }
}
{
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>
Hi Venkat,
ReplyDeleteIf we are already in edit page then we click on Add in top navigation then it is not routing to Add
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"'
ReplyDeleteHere we are automatically getting validation for PhotoPath how to remove that
ReplyDelete