NO. | TITLE |
---|---|
1 | Introduction |
2 | Installation |
3 | Identity |
4 | Net 5 CRUD (Example ONLY) |
5 | How to run this project |
6 | Insert Library |
7 | Edit Login Partial |
8 | CRUD code - View - Insert - Update - Delete |
-
Download this project and open your terminal or VS Code.
-
On your terminal type this command, but first you need to make sure you know where your project folder is. For my case, I put it on my desktop
code -r TrainingRazor
-
"TrainingRazor" name on this command is the folder name of the project. The name depends on you.
-
System will open the project on your Code Editor.
-
Other than that, by using VS Code, you can just drag project folder on VS Code and and trust the author.
-
If VS Code authorize you to insert some assets, just click Yes/Accept
-
Before we start to code, we need to download library called Datatable, just use default setting when download Datatable. Other than that, we need to download library called FontAwesome.
What is Datatable?
What is FontAwesome?
Test FontAwesome v4
-
Insert the Datatable library and FontAwesome inside folder lib on wwwroot folder.
-
After that insert this code
<link rel="stylesheet" type="text/css" href="~/lib/DataTables/datatables.min.css"/> <link rel="stylesheet" type="text/css" href="~/lib/font-awesome-4.7.0/css/font-awesome.min.css" />
inside _Layout.cshtml on
-
Then, we need to insert this code
<script src="~/lib/DataTables/datatables.min.js"></script>
inside _Layout.cshtml on
-
We need to edit file _LoginPartial.cshtml inside folder Pages > Shared. This is because there is some error when redirect link to logout.
-
Open _LoginPartial.cshtml and edit this line as below :
From :
TO :
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl='@Url.Page("/Account/Login", new { area = "Identity" })' method="post" >
-
Save the file.
CRUD Code
-
For this project, we gonna use page Index to show one table that consist all data from database table that we have created.
-
Open file Index.cshtml and replace the code :
@page "{handler?}/{id?}" @model IndexModel @{ ViewData["Title"] = "Home page"; } <div> <div class="text-center mt-4"> <h1>Training Razor</h1> <p>This Project consist of Create, Read, Update and Delete process.</p> </div> <div align="left" class="mt-5 mb-4"> <a asp-page="Manage" asp-page-handler="Insert" class="btn btn-primary">Insert</a> </div> @if(TempData["error"]!=null) { <div class="alert alert-danger d-flex align-items-center alert-dismissible fade show" role="alert"> <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i>   <div> <span>@TempData["error"]</span> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> </div> } @if(TempData["success"]!=null) { <div class="alert alert-success d-flex align-items-center alert-dismissible fade show" role="alert"> <i class="fa fa-check-circle text-success" aria-hidden="true"></i>   <div> <span>@TempData["success"]</span> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> </div> } <div class="table-responsive"> <table id="data" class="display table table-striped table-bordered"> <thead> <tr> @if(User.IsInRole("SystemAdmin")) { <th class="text-center col-md-2 dont-sort">NO</th> <th class="text-center col-md-5 sort-this">PRODUCT NAME</th> <th class="text-center col-md-3">PRICE</th> <th class="text-center col-md-2 dont-sort"></th> } else if(User.IsInRole("Customer")) { <th class="text-center col-md-2 dont-sort">NO</th> <th class="text-center col-md-5 sort-this">PRODUCT NAME</th> <th class="text-center col-md-3">QUANTITY</th> <th class="text-center col-md-2">TOTAL PRICE</th> <th class="text-center col-md-2 dont-sort"></th> } </tr> </thead> <tbody> @if(User.IsInRole("SystemAdmin")) { @for(int i=0; i<Model.products.Count(); i++) { <tr> <td class="text-center"></td> <td class="text-center">@Model.products[i].Name</td> <td class="text-center">RM @string.Format("{0:F2}", (@Model.products[i].Price))</td> <form id="product" method="post"> <td class="text-center"> <a class="btn btn-link" asp-page="Manage" asp-page-handler="Update" asp-route-id="@Model.products[i].Id"><i class="fa fa-edit" aria-hidden="true"></i></a> <button class="btn btn-link text-danger" asp-page-handler="Delete" asp-route-id="@Model.products[i].Id" type="submit"><i class="fa fa-trash" aria-hidden="true"></i></button> </td> </form> </tr> } } else if(User.IsInRole("Customer")) { @for(int i=0; i<Model.custProducts.Count(); i++) { <tr> <td class="text-center"></td> <td class="text-center">@Model.custProducts[i].Name</td> <td class="text-center">@Model.custProducts[i].Quantity</td> <td class="text-center">RM @string.Format("{0:F2}", (@Model.custProducts[i].TotalPrice))</td> <form id="product" method="post"> <td class="text-center"> <a class="btn btn-link" asp-page="Manage" asp-page-handler="Update" asp-route-id="@Model.custProducts[i].Id"><i class="fa fa-edit" aria-hidden="true"></i></a> <button class="btn btn-link text-danger" asp-page-handler="Delete" asp-route-id="@Model.custProducts[i].Id" type="submit"><i class="fa fa-trash" aria-hidden="true"></i></button> </td> </form> </tr> } } </tbody> </table> </div> </div> @section Scripts { <script src="~/js/main.js"></script> }
-
If you got an error, it is because we still didn't edit the code on view-model. Open Index.cshtml.cs and paste this code :
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using TrainingRazor.Data; using TrainingRazor.Models; namespace TrainingRazor.Pages; //THIS VIEW VIEW-MODEL public class IndexModel : BaseModel { private readonly ApplicationDbContext _context; //CONNECTION FOR DATABASE public IndexModel(ApplicationDbContext context, UserManager<ApplicationUser> userManager) : base(userManager) //REFER TO BASE CLASS MODEL { _context = context; } public List<CustProductModel> custProducts { get; set; } //ENTITY VARIABLE DECLARATION public List<Product> products { get; set; } //ENTITY VARIABLE DECLARATION public async Task<ActionResult> OnGet() { if(User.IsInRole("Customer")) //LOAD DATA BY USER ROLE { var currentUser = await GetCurrentUser(); //GET CURRENT USER FROM METHOD CLASS custProducts = new List<CustProductModel>(); //CREATE NEW LIST //GET DATA FROM DATABASE var data = await _context.CustPurchaseds.Include(x => x.Creator) //INCLUDE OTHER TABLE .Include(x => x.Product) //INCLUDE OTHER TABLE .Where(x => x.Creator == currentUser) //GET DATA ONLY FROM CURRENT USER AUTHENTICATION .ToListAsync(); if(data!=null) //CHECK AVAILABILITY OF DATA { for(int i=0; i< data.Count(); i++) //LOOP DATA { var totalPrice = data[i].Product.Price * data[i].Quantity; //CALCULATE TOTAL PRICE //INSERT DATA INTO LIST var custProduct = new CustProductModel() { Id = data[i].Id, Name = data[i].Product.Name, Quantity = data[i].Quantity ?? 0, TotalPrice = totalPrice, }; custProducts.Add(custProduct); } } } else if(User.IsInRole("SystemAdmin")) //LOAD DATA BY USER ROLE { products = new List<Product>(); //CREATE NEW LIST products = await _context.Products.ToListAsync(); //GET DATA FROM DATABASE } return Page(); } } //BASE CLASS MODEL -- THIS MODEL CAN BE CALL ON ANOTHER VIEW-MODEL public class BaseModel : PageModel { private readonly UserManager<ApplicationUser> _userManager; public BaseModel(UserManager<ApplicationUser> userManager) { _userManager = userManager; } //METHOD CLASS TO GET CURRENT USER INFORMATION protected async Task<ApplicationUser> GetCurrentUser() { return await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name); } }
-
After that, we need to create a new Entity Model file called InputModel.cs inside Models folder.
-
Copy and paste this code inside InputModel.cs file.
namespace TrainingRazor.Models { public class CustProductModel { public int Id { get; set; } public string Name { get; set; } public int? Quantity { get; set; } public decimal? TotalPrice { get; set; } } }
-
You can see that all the errors are gone.
-
After that, create one file inside js folder and name it main.js
-
Copy and paste this code inside main.js and save the file.
(function ($) { var t = $('#data').DataTable({ pageLength: 5, "lengthMenu": [[5, 10, 25, 50, -1], [5, 10, 25, 50, "SEMUA"]], "order": [[ $('th.sort-this').index(), 'desc' ]], "columnDefs": [ { "searchable": false, "orderable": false, "targets": "dont-sort" }, ], }); t.on( 'order.dt search.dt', function () { t.column(0, {search:'applied', order:'applied'}).nodes().each( function (cell, i) { cell.innerHTML = i+1; } ); }).draw(); })(jQuery);
-
You can try run the web application first to see whether there is error or not. Click FN + F5 on your keyboard or just F5, depends on your keyboard.
-
You can see that there are no rows on the table when you login as Customer role because we still didn't insert the data; table inside database is still empty. Moreover, if you login as Admin role, you can see that the table shows list of product that you insert when you migrate database on Part_3_Identity.
-
Now, we gonna create a new page call Manage.
-
Right click on folder Pages, click New C#, then, click Razor Page.
-
Below alert will popup; insert Manage and click Enter button on keyboard.
-
You can see on Pages folder, there are 2 new file created called Manage.cshtml (View) and Manage.cshtml.cs (View-Model)
-
Open Manage.cshtml and replace with this code :
@page "{handler?}/{id?}" @model ManageModel @{ ViewData["Title"] = "Manage"; } <div> <div class="mt-4"> <h1> Insert Data </h1> </div> <div class="mt-2"> <form method="post"> @if(User.IsInRole("Customer")) { <div class="form-group mt-3"> <label asp-for="InputCustPurchasing.ProductId" class="col-form-label"></label> <select asp-for="InputCustPurchasing.ProductId" class="form-control"> <option price="0.00" value="">Please Choose</option> @foreach(var item in Model.products) { <option price="@item.Price" value="@item.Id">@item.Name</option> } </select> <span asp-validation-for="InputCustPurchasing.ProductId" class="text-danger"></span> </div> <div class="form-group mt-3"> <label asp-for="InputCustPurchasing.Quantity" class="col-form-label"></label> <input asp-for="InputCustPurchasing.Quantity" class="form-control"> <span asp-validation-for="InputCustPurchasing.Quantity" class="text-danger"></span> </div> <div class="form-group mt-3"> <label for="total-price" class="col-form-label">Total Price (RM)</label> <span id="total-price" class="form-control">0.00</span> </div> } else if(User.IsInRole("SystemAdmin")) { <div class="form-group mt-3"> <label asp-for="InputProduct.Name" class="col-form-label"></label> <input asp-for="InputProduct.Name" class="form-control"> <span asp-validation-for="InputProduct.Name" class="text-danger"></span> </div> <div class="form-group mt-3"> <label asp-for="InputProduct.Price" class="col-form-label"></label> <input asp-for="InputProduct.Price" class="form-control"> <span asp-validation-for="InputProduct.Price" class="text-danger"></span> </div> } <div class="form-group mt-4"> <a asp-page="/Index" class="btn btn-primary mr-3">Back</a> <button type="submit" asp-page-handler="Save" class="btn btn-success ml-3">Save</button> </div> </form> </div> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} <script src="~/js/manage.js"></script> }
-
Then, open Manage.cshtml.cs and replace with this code :
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using TrainingRazor.Data; using TrainingRazor.Models; namespace TrainingRazor.Pages { public class ManageModel : BaseModel //REFER TO BASE CLASS MODEL { private readonly ApplicationDbContext _context; //CONNECTION FOR DATABASE public ManageModel(ApplicationDbContext context, UserManager<ApplicationUser> userManager) : base(userManager) //REFER TO BASE CLASS MODEL { _context = context; } [BindProperty] //MUST BIND INPUT MODEL FOR DATA TO REMAINS WHEN FORM IS POST public InputCustPurchasingModel InputCustPurchasing { get; set; } //ENTITY VARIABLE DECLARATION [BindProperty] public InputProductModel InputProduct { get; set; } //ENTITY VARIABLE DECLARATION public List<Product> products { get; set; } public async Task<IActionResult> OnGetInsert() { if(User.IsInRole("Customer")) { products = await _context.Products.ToListAsync(); InputCustPurchasing = new InputCustPurchasingModel() } else if(User.IsInRole("SystemAdmin")) { InputProduct = new InputProductModel(); } return Page(); } public async Task<ActionResult> OnPostSave() { if(User.IsInRole("Customer")) { var currentUser = await GetCurrentUser(); //CALL METHOD FROM BaseModel //INSERT INPUT DATA FROM INPUT ENTITY MODEL, INSIDE DATABASE ENTITY MODEL var custPurchased = new CustPurchased() { Creator = currentUser, ProductId = InputCustPurchasing.ProductId, Quantity = InputCustPurchasing.Quantity }; await _context.CustPurchaseds.AddAsync(custPurchased); //ADD DATA } else if(User.IsInRole("SystemAdmin")) { var product = new Product() { Name = InputProduct.Name, Price = InputProduct.Price }; await _context.Products.AddAsync(product); } await _context.SaveChangesAsync(); //SAVE DATA INTO DATABASE TempData["success"] = "Success to insert"; return RedirectToPage("Index"); //REDIRECT SYSTEM TO PAGE INDEX } } }
-
Insert this code inside file InputModel.cs :
Above this code namespace TrainingRazor.Models
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema;
Below CustProductModel class :
public class InputCustPurchasingModel { public ApplicationUser Creator { get; set; } [Required(ErrorMessage = "Please select Product")] [Display(Name = "Product")] public int? ProductId { get; set; } [Required(ErrorMessage = "Please insert Quantity")] [Range(1, 999999999999, ErrorMessage = "Insert at least 1 quantity")] [Display(Name = "Quantity")] public int Quantity { get; set; } } public class InputProductModel { [StringLength(500)] [Required(ErrorMessage = "Please insert product name")] [Display(Name = "Product Name")] public string Name { get; set; } [Column(TypeName = "decimal(18, 2)")] [Required(ErrorMessage = "Please insert product price")] [Display(Name = "Product Price (RM)")] public decimal Price { get; set; } }
-
Do not run the project yet, now, we gonna create a new js file inside js folder called manage.js
-
Copy and paste this code inside file manage.js
(function ($) { // KEY UP FUNCTION; CODE WILL RUN WHEN YOU ENTER VALUE ON KEYBOARD $("#InputCustPurchasing_Quantity").keyup( function (e) { Calculate(); //CALL METHOD }); // CHANGE FUNCTION; CODE WILL RUN IF THERE ARE SOME CHANGES ON DROPDOWN $("#InputCustPurchasing_ProductId").change( function (e) { Calculate(); //CALL METHOD }); // BLUR FUNCTION; WHEN INPUT LOSE FOCUS, FIXED THE VALUE TO 2 DECIMAL PLACES $("#InputProduct_Price").blur(function(){ var value = parseFloat(this.value); $(this).val(value.toFixed(2)); }); })(jQuery); // CALCULATE FUNCTION function Calculate() { // GET VALUE FROM ATTRIBUTE IN ID BY FINDING THE ELEMENT OPTION THAT IS SELECTED var price = parseFloat($("#InputCustPurchasing_ProductId").find('option:selected').attr('price')); var totalPrice = parseFloat($("#InputCustPurchasing_Quantity").val() * price); $("#total-price").html(totalPrice.toFixed(2)); // SHOW BACK THE VALUE ON ELEMENT BY ID }
-
Save everything and run project, try insert data on page Manage by click the Insert button. If everything is okay, you can see the data inserted inside table/database.
-
You also can try login on Admin account and try insert a new product.
-
Insert this code on InputModel.cs file
public int Id { get; set; }
Insert on both entity model class like this and save the file :
-
Open Manage.cshtml.cs file and insert this code
Insert between handler OnGetInsert() method and declaration of products
public bool IsUpdate { get; set; } //VARIABLE BOOL TO KNOW IF THE CONDITIO IS UPDATE OR NOT // WILL BE RUN ON PAGE LOADING WITHOUT LOADING WITH HANDLER public async Task<IActionResult> OnGet() { IsUpdate = false; products = await _context.Products.ToListAsync(); InputCustPurchasing = new InputCustPurchasingModel(); InputProduct = new InputProductModel(); return Page(); }
like this
Then, insert this code inside OnGetInsert() handler method
IsUpdate = false;
like this
After that, insert this code below OnGetInsert() handler method
public async Task<IActionResult> OnGetUpdate(int? id) { if(id != null) { IsUpdate = true; if(User.IsInRole("Customer")) { products = await _context.Products.ToListAsync(); var purchased = await _context.CustPurchaseds.FirstOrDefaultAsync(x => x.Id == id); InputCustPurchasing = new InputCustPurchasingModel() { Id = purchased.Id, ProductId = purchased.ProductId, Quantity = purchased.Quantity ?? 0 }; } else if(User.IsInRole("SystemAdmin")) { var product = await _context.Products.FirstOrDefaultAsync(x => x.Id == id); InputProduct = new InputProductModel() { Id = product.Id, Name = product.Name, Price = product.Price }; } } else { return RedirectToPage("/Index"); } return Page(); }
like this
Moreover, insert this code below OnPostSave() handler method
public async Task<ActionResult> OnPostUpdate() { if(User.IsInRole("Customer")) { var purchased = await _context.CustPurchaseds.FirstOrDefaultAsync(x => x.Id == InputCustPurchasing.Id); if(purchased!=null) { purchased.ProductId = InputCustPurchasing.ProductId; //UPDATE INFORMATION purchased.Quantity = InputCustPurchasing.Quantity; } } else if(User.IsInRole("SystemAdmin")) { var product = await _context.Products.FirstOrDefaultAsync(x => x.Id == InputProduct.Id); if(product!=null) { product.Name = InputProduct.Name; product.Price = InputProduct.Price; } } await _context.SaveChangesAsync(); //SAVE DATA INTO DATABASE TempData["success"] = "Success to update"; return RedirectToPage("Index"); //REDIRECT SYSTEM TO PAGE INDEX }
like this
-
Open Manage.cshtml and insert/edit this code
Change this code from
<h1> Insert Data </h1>
to this
<h1> @if(Model.IsUpdate) { <span>Update</span> } else { <span>Insert</span> } Data </h1>
After that, insert this code
<input type="hidden" asp-for="InputCustPurchasing.Id">
like this
Moreover, insert this code
<input type="hidden" asp-for="InputProduct.Id">
like this
Futhermore, change this code from
<div class="form-group mt-4"> <a asp-page="/Index" class="btn btn-primary mr-3">Back</a> <button type="submit" asp-page-handler="Save" class="btn btn-success ml-3">Save</button> </div>
to this
<div class="form-group mt-4"> <a asp-page="/Index" class="btn btn-primary mr-3">Back</a> @if(Model.IsUpdate) { <button type="submit" asp-page-handler="Update" class="btn btn-success ml-3">Update</button> } else { <button type="submit" asp-page-handler="Save" class="btn btn-success ml-3">Save</button> } </div>
-
Besides that, you need to edit manage.js for to running Calculate function when page Manage is loading. You can just insert Calculate(); as below
-
You can save everything and run the project, from the list that you have on page Index, you can click the edit button
after that, you can see the page redirect to page Manage, and data that you inserted is on input field
try to change the input data and click the Update button
system will redirect to page Index and you can see the data are changing
-
Edit you Index.cshtml by inserting this code
onclick="return ConfirmDelete()"
on this code
-
After that, insert this code on Index.cshtml.cs below OnGet() handler method
public async Task<IActionResult> OnPostDelete(int? id) { if(id!=null) { var currentUser = await GetCurrentUser(); if(User.IsInRole("Customer")) { var purchased = await _context.CustPurchaseds.Include(x => x.Creator) .FirstOrDefaultAsync(x => x.Id == id); if(purchased!=null) { if(purchased.Creator == currentUser) //CHECK WHETHER CURRENTUSER IS THE ONE THAT PURCHASED IT { _context.CustPurchaseds.Remove(purchased); } } else { TempData["error"] = "Purchased not found"; return RedirectToPage(); } } else if(User.IsInRole("SystemAdmin")) { var purchased = await _context.CustPurchaseds.ToListAsync(); var product = await _context.Products.FirstOrDefaultAsync(x => x.Id == id); if(product!=null) { for(int i = 0; i < purchased.Count; i++) //LOOP THROUGH PURCHASED TABLE TO SEE WHETHER PRODUCTS BEEN PURCHASED { if(product.Id != purchased[i].ProductId) { _context.Products.Remove(product); } else { TempData["error"] = "Product been purchased"; return RedirectToPage(); } } } } await _context.SaveChangesAsync(); TempData["success"] = "Success to delete"; } return RedirectToPage(); }
-
Futhermore, open you main.js file and insert this code
function ConfirmDelete() { return confirm("Are you sure you want to delete?"); }
like this
-
Run your project and try to delete some information.