- Tạo Model Product
- Xây dựng Controller hiện thị danh sách sản phẩm
- Kích hoạt Session
- Xây dựng cấu trúc dữ liệu Cart
- Thêm mặt hàng vào Cart
- Trang hiện thị Cart
- Tạo CartPartial, giỏ hàng ở menu
Trong phần này xây dựng chức năng tạo giỏ hàng đơn giản với ASP.NET Core, ta sẽ tạo trang liệt kê các sản phẩm và cho phép chọn sản phảm vào giỏ hàng Card.
Bài thực hành này tiếp tục trên ví dụ cũ mvcblog
:
Trang hiện thị các bài viết (phần 4),
tải mã nguồn về mở ra tiếp tục phát triển
ex068-fontendpost
Tạo Model Product
Tạo ra một lớp đơn giản biểu diễn các sản phẩm, đặt tên lớp là Product
[Table("Product")] public class Product { [Key] public int ProductId {set; get;} public string Name {set; get;} public string Description {set; get;} public decimal Price {set; get;} }
Cập nhật vào AppDbContext
public class AppDbContext : IdentityDbContext<AppUser> { / ... (nội dung khác) public DbSet<Product> Products {set; get;} / ... (nội dung khác) protected override void OnModelCreating (ModelBuilder builder) { / ... (nội dung khác) // SeedData - chèn ngay bốn sản phẩm khi bảng Product được tạo builder.Entity<Product>().HasData( new Product() { ProductId = 1, Name = "Đá phong thuỷ tự nhiên", Description = "Số 1 cao 40cm rộng 20cm dày 20cm màu xanh lá cây đậm", Price = 1000000 }, new Product() { ProductId = 2, Name = "Đèn đá muối hình tròn", Description = "Trang trí trong nhà Chất liệu : • Đá muối", Price = 1500000 }, new Product() { ProductId = 3, Name = "Tranh sơn mài", Description = "Tranh sơn mài loại nhỏ 15x 15 giá 50.000", Price = 50000 } , new Product() { ProductId = 4, Name = "Tranh sơn dầu - Ngựa", Description = "Nguyên liệu thể hiện : Sơn dầu", Price = 450000 } ); } }
Thực hiện tạo Migration và cập nhật vào Database, Migration mới đặt tên là AddProduct
dotnet ef migrations add AddProduct dotnet ef database update AddProduct
Trong quá trình tạo Db ở trên, có SeedData tạo ra 4 sản phẩm mẫu trong CSDL. Trong trường hợp muốn tạo các chức năng CRUD thì thực hiện tương tự như với Post trong ví dụ trước, tại đây chỉ sử dụng các sản phẩm mẫu chèn vào như trên để cho ngắn gọn.
Controller hiện thị danh sách sản phẩm
Ta xây dựng Controller - ProductController
hiện thị danh sách sản phẩm và các
chức năng liên quan đến cart như đưa một sản phẩm vào giỏ hàng, xóa sản phẩm khỏi giỏ hàng ... Các
chức năng đó thực hiện với các Action tương ứng như sau:
Controllers/ProductController.cs
[Route("/products")] public class ProductController : Controller { private readonly ILogger<ProductController> _logger; private readonly AppDbContext _context; public ProductController(ILogger<ProductController> logger, AppDbContext context) { _logger = logger; _context = context; } // Hiện thị danh sách sản phẩm, có nút chọn đưa vào giỏ hàng public IActionResult Index() { var products = _context.Products.ToList(); return View(products); } /// Thêm sản phẩm vào cart [Route("addcart/{productid:int}")] public IActionResult AddToCart([FromRoute]int productid) { var product = _context.Products .Where(p => p.ProductId == productid) .FirstOrDefault(); if (product == null) return NotFound("Không có sản phẩm"); // Xử lý đưa vào Cart ... return RedirectToAction(nameof(Cart)); } /// xóa item trong cart [Route("/removecart/{productid:int}", Name = "removecart")] public IActionResult RemoveCart([FromRoute]int productid) { // Xử lý xóa một mục của Cart ... return RedirectToAction(nameof(Cart)); } /// Cập nhật [Route ("/updatecart", Name = "updatecart")] [HttpPost] public IActionResult UpdateCart([FromForm]int productid, [FromForm]int quantity) { // Cập nhật Cart thay đổi số lượng quantity ... return RedirectToAction(nameof(Cart)); } // Hiện thị giỏ hàng [Route("/cart", Name = "cart")] public IActionResult Cart() { return View(); } [Route("/checkout")] public IActionResult CheckOut() { // Xử lý khi đặt hàng return View(); } }
Trang Index tương ứng với Action Index ở trên, hiện thị danh sách các sản phẩm, tại View Index.cshtml xây dựng như sau:
Views/Product/index.cshtml
@model List<mvcblog.Models.Product> @{ ViewData["Title"] = "Các sản phẩm"; } <div class="card-columns"> @foreach (var product in Model) { <div class=card> <h3 class="card-header">@product.Name</h3> <div class="card-body" style="height: 150px;"> @product.Description </div> <div class="card-footer"> <span class="text-muted">@(product.Price.ToString("n0")) VND</span> <a asp-route="addcart" asp-route-productid="@product.ProductId" class="btn btn-secondary btn-sm float-right">Đặt hàng</a> </div> </div> } </div>
Kích hoạt và sử dụng Session
Để xây dựng chức năng giỏ hàng, danh sách các mặt hàng sẽ lưu trong Session của hệ thống. Do vậy, ứng dụng cần đảm bảo kích hoạt Session - làm theo hướng dẫn tại Sử dụng Session trong ASP.NET , đồng thời cũng dùng kỹ thuật JSON để lưu dữ liệu nên cần đảm bảo tích hợp hai gói là:
dotnet add package Newtonsoft.Json dotnet add package Microsoft.AspNetCore.Session dotnet add package Microsoft.Extensions.Caching.Memory
Thêm vảo Startup.ConfigureServices
services.AddDistributedMemoryCache(); // Đăng ký dịch vụ lưu cache trong bộ nhớ (Session sẽ sử dụng nó) services.AddSession(cfg => { // Đăng ký dịch vụ Session cfg.Cookie.Name = "xuanthulab"; // Đặt tên Session - tên này sử dụng ở Browser (Cookie) cfg.IdleTimeout = new TimeSpan(0,30, 0); // Thời gian tồn tại của Session });
Trong Startup.Configure
cho thêm vào (sau UseStaticFiles()):
app.UseSession();
Giờ trong hệ thống Session đã làm việc, đồng thời cũng có thư viện JSON, một chức năng cần nhớ để áp dụng cho ví dụ này là:
-
Trong Controller, Session được truy cập thông qua giao diện ISession, giao diện này lấy được bằng thuộc tính
HttpContext.Session
var session = HttpContext.Session;
-
Các mục lưu trong session dưới dạng key/value. Bạn có thể đọc và lưu một chuỗi vào session bằng phương thức
GetString(key, value)
vàSetString(key, value)
. Tuy hai phương thức này là mở rộng của ISession nên cần cóusing Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http; /... var session = HttpContext.Session; string valueString = session.GetString ("key"); session.SetString("yourkey", "yourstring");
-
Thư viện
Newtonsoft.Json
giúp làm việc với JSON, ở đây cần nhớ hai chức năng. Chuyển một đối tượng thành chuỗi json và ngược lại phục hồi đối tượng từ chuỗi json
Để chuyển một đối tượng (thuộc tính đối tượng) thành chuỗi json dùng
SerializeObject
string jsonstring = JsonConvert.SerializeObject(ob);
Để chuyển chuỗi json thành đối tượng dùng
DeserializeObject<ObjectClas>
Type obj = JsonConvert.DeserializeObject<Type>(jsonstring);
Xây dựng cấu trúc Cart
Giỏ hàng ta xây dựng nó là một danh sách List, chứa các mục trong giỏ hàng, mỗi mục là một đối
tượng lớp CartItem
, lớp đó như sau:
public class CartItem { public int quantity {set; get;} public Product product {set; get;} }
Từ đó ta sẽ xây dựng các chức năng của Cart là các phương thức trong controller gồm:
public class ProductController : Controller { /... // Key lưu chuỗi json của Cart public const string CARTKEY = "cart"; // Lấy cart từ Session (danh sách CartItem) List<CartItem> GetCartItems () { var session = HttpContext.Session; string jsoncart = session.GetString (CARTKEY); if (jsoncart != null) { return JsonConvert.DeserializeObject<List<CartItem>> (jsoncart); } return new List<CartItem> (); } // Xóa cart khỏi session void ClearCart () { var session = HttpContext.Session; session.Remove (CARTKEY); } // Lưu Cart (Danh sách CartItem) vào session void SaveCartSession (List<CartItem> ls) { var session = HttpContext.Session; string jsoncart = JsonConvert.SerializeObject (ls); session.SetString (CARTKEY, jsoncart); } /...
Xây dựng chức năng thêm mặt hàng vào cart
Khi người dùng bấm vào Đặt hàng ở danh sách sản phẩm, thì nó sẽ chuyển đến Action AddToCart, có chức năng đưa sản phẩm đó vào Cart. Xây dựng Action đó như sau:
/// Thêm sản phẩm vào cart [Route ("addcart/{productid:int}", Name = "addcart")] public IActionResult AddToCart ([FromRoute] int productid) { var product = _context.Products .Where (p => p.ProductId == productid) .FirstOrDefault (); if (product == null) return NotFound ("Không có sản phẩm"); // Xử lý đưa vào Cart ... var cart = GetCartItems (); var cartitem = cart.Find (p => p.product.ProductId == productid); if (cartitem != null) { // Đã tồn tại, tăng thêm 1 cartitem.quantity++; } else { // Thêm mới cart.Add (new CartItem () { quantity = 1, product = product }); } // Lưu cart vào Session SaveCartSession (cart); // Chuyển đến trang hiện thị Cart return RedirectToAction (nameof (Cart)); }
Trang hiện thị Cart
Tương ứng với action Cart
// Hiện thị giỏ hàng [Route ("/cart", Name = "cart")] public IActionResult Cart () { return View (GetCartItems()); }
Views/Product/cart.cshtml
@model List<mvcblog.Models.CartItem> <h2>GIỎ HÀNG</h2> @if (Model.Count > 0) { decimal total = 0; int stt = 1; <table class="table"> <tr> <th>#</th> <th>Sản phẩm</th> <th>Giá</th> <th>Số lượng</th> <th>Thành tiền</th> <th></th> </tr> @foreach (var cartitem in Model) { var thanhtien = cartitem.quantity * cartitem.product.Price; total += thanhtien; <tr> <td>@(stt++)</td> <td>@cartitem.product.Name</td> <td>@(cartitem.product.Price.ToString("n0"))</td> <td><input asp-for="@cartitem.quantity" id="@($"quantity-{cartitem.product.ProductId}")"/></td> <td>@(thanhtien.ToString("n0"))</td> <td> <button class="btn btn-success updatecartitem" data-productid="@cartitem.product.ProductId">Cập nhật</button> <a asp-route="removecart" asp-route-productid="@cartitem.product.ProductId" class="btn btn-danger">Xóa</a> </td> </tr> } <tr> <td colspan="4" class="text-right">Tổng tiền</td> <td>@(total.ToString("n0"))</td> <td></td> </tr> </table> <a asp-controller="Product" asp-action="Checkout" class="btn btn-success">Gửi đơn hàng</a> @section Scripts { <script> $(document).ready(function () { $(".updatecartitem").click(function (event) { event.preventDefault(); var productid = $(this).attr("data-productid"); var quantity = $("#quantity-" + productid).val(); $.ajax({ type: "POST", url:"@Url.RouteUrl("updatecart")", data: { productid: productid, quantity:quantity }, success: function (result) { window.location.href = "@Url.RouteUrl("cart")"; } }); }); }); </script> } } else { <p class="alert alert-danger">Giỏ hàng trống</p> }
Trong đó đoạn mã tại section Script là JQuery - để cập nhật lại số lượng của một mặt hàng nào đó. Người dùng điền số lượng mới và bấm vào cập nhật. Khi bấm vào cập nhật sẽ sử dụng Ajax gửi (post) dữ liệu là ProductId và số lượng tương ứng đến Action UpdateCart, nội dung Action này như sau
/// Cập nhật [Route ("/updatecart", Name = "updatecart")] [HttpPost] public IActionResult UpdateCart ([FromForm] int productid, [FromForm] int quantity) { // Cập nhật Cart thay đổi số lượng quantity ... var cart = GetCartItems (); var cartitem = cart.Find (p => p.product.ProductId == productid); if (cartitem != null) { // Đã tồn tại, tăng thêm 1 cartitem.quantity = quantity; } SaveCartSession (cart); // Trả về mã thành công (không có nội dung gì - chỉ để Ajax gọi) return Ok(); }
Tương tự action RemoveCart có chức năng xóa một sản phẩm khỏi Cart, xây dựng nó như sau
/// xóa item trong cart [Route ("/removecart/{productid:int}", Name = "removecart")] public IActionResult RemoveCart ([FromRoute] int productid) { var cart = GetCartItems (); var cartitem = cart.Find (p => p.product.ProductId == productid); if (cartitem != null) { // Đã tồn tại, tăng thêm 1 cart.Remove(cartitem); } SaveCartSession (cart); return RedirectToAction (nameof (Cart)); }
Tạo Partial hiện thị giỏ hàng ở menu
Tạo ra partial _CartPartial.cshtml
để hiện thị giỏ hàng trên menu như sau:
Views/Shared/_CartPartial.cshtml
@using Microsoft.AspNetCore.Http @using Newtonsoft.Json @inject IHttpContextAccessor HttpContextAccessor @{ var session = HttpContextAccessor.HttpContext.Session; string jsoncart = session.GetString (mvcblog.Controllers.ProductController.CARTKEY); if (jsoncart != null) { var cart = JsonConvert.DeserializeObject<List<CartItem>> (jsoncart); <div class="nav-item"><a asp-route="cart">Giỏ hàng(@cart.Count)</a></div> } }
Trong Code trên truy cập Session từ View, do đó phải Inject IHttpContextAccessor.
Mở layout ra, chèn đoạn mã để render partial này tại menu
@await Html.PartialAsync("_CartPartial")
Mã nguồn tham khảo ASP_NET_CORE/mvcblog, hoặc tải về bản bài này ex068-cart