# Day14 分頁和購物車 ###### tags: `MVC` ## 分頁 最重要的就是要知道怎麼開始. ```csharp= public ActionResult Paging(int id =1){ // 目前所在頁數 int activePage = id // 每頁有幾筆 int pageRows = 3; // 總共有幾筆? var clothings = db.clothings.Count(); // 定義頁數的方法 int Pages = 0 if(clothing % pageRows == 0){ Pages = clothing/pageRows }else{ Pages = (clothing/pageRows)+1 } // 起始頁數 int startRows = (activePage -1) * pageRows List<clothings> clothingList = db.clothings.orderBy(x =>x.Id).skip(startRows).Take(pageRows).toList(); ViewData["Active"] = id; // 知道總頁數, 才會知道總共要跑幾頁 ViewData["Pages"] = Pages; return View(clothingList); } // 因此在VIEW中就可以這樣寫. // 重點就在於跑for迴圈, 要和ViewData["Active"]的值一致, 這樣active就可以把該給的給到別人. <nav aria-label="Page navigation example"> <ul class="pagination"> <li class="page-item"><a class="page-link" href="#">Previous</a></li> @for (int i = 1; i <= (int)ViewData["Pages"]; i++) { string active = i == (int)ViewData["Active"] ? "active" : ""; <li class="page-item @active"> <a class="page-link" href="/home/Index/@i">@i</a></li> } <li class="page-item"><a class="page-link" href="#">Next</a></li> </ul> </nav> ``` ## Sidebar-component化 把sidebar的內容, 存在資料庫當中 1. 先在`shoppingcontext` 新增`Dbset<SideBar>` 2. 到`configuration`中新增種子資料 3. `add-migration` -> `update-database` ### 效能最佳化 把Linq轉換成SQL語法, 透過SQL profiler得知, 每點選一次就會再去資料庫讀取, 因此可以透過單件的技巧, 讓他只要讀取一次即可. Static =>靜態被創立以後, 就會一直在記憶體裡面, 而值不會改變. 因此這邊介紹的[單件], 讓效能最佳化 --- ## 購物車 ### Application和session (伺服器端狀態管理) - 網頁一開始是stateless是為了應付高流量. - 關機以後資料就會消失. Application很適合用來計算網站人數耶, 因為大家都可以看到,每個人共用一份. Session是所有人獨立一份. ### Cookie(瀏覽器端) 解析下老師的寫法: - 一開始先進入Home/Index的Action, 看到回傳`return View(productService.GetAllProducts())` - Service裡面又看到呼叫`productRepo.QueryAllProducts()`, 最終`Return Product` - 到view(Home/Index)以後, 看到`@Html.Partial("_ProductPartial", item)` - `_Partial` 看到傳入的都會是`@model ProductViewModel` - 看到首頁有一個詳細資訊, 點下去後 ```htmlmixed= <a href="/Details/@Model.ProductId" class="btn btn-primary">詳細資料</a> ``` trigger的地方在於`Route.config`, 所以會觸發這個action. ```csharp= routes.MapRoute( name: "DetailsRouting", url: "Details/{ProductId}", defaults: new { controller = "Details", action = "FindProductById", ProductId = UrlParameter.Optional } ); ``` - 這個Action是定義在`Details` controller, 裡面的一個方法`FindProductById` - 看到這個方法會呼叫到SERVICE的`GetProductById`, 再來就是呼叫到Repo的` QueryProductById` - ` var product = products.Where(x => x.ProductId == productId).FirstOrDefault();` 最後會return這個`product`(集合) - 同時要去看`Details/FindProductById`的View, 可以看到最下面有一個ajax方法 - 要送這個資料到後端,詳細程式碼在底下 購物車只能夠把id和數量送出去, 不能送價格, 而價格是後端收到id以後, 去資料庫查出來的. ```javascript= function postValue() { let productId = $("#ProductId").text(); let productName = $("#ProductName").text(); let price = $("#Price").text(); let count = $("#Count").text(); $.ajax({ type: "POST", url: "/Shopping/AddToCart", // 這邊因為沒有真的資料庫, 因此才送出價格 data: { ProductId: productId, ProductName: productName, Price: price, Count: count }, dataType: "text", success: function (response) { alert(response); $("#MyCart").text("購物車(" + response + ")"); } }); ``` 可以看到這個ajax是送到`Shopping/AddToCart`. 所以看下這個方法, 是用甚麼方式來接收資料. 還有一個`Cart`的Model喔~ ```csharp= public int AddToCart(ProductViewModel productVM) { // 購物車實際長怎樣, 和想做的有關. List<Cart> cartItems = new List<Cart>(); if (Session["Cart"] ==null) { //初次直接將購買產品加入到購物車,不需做還原 Cart cart = new Cart { RecordId = 1, CartId = Guid.NewGuid().ToString(), ProductId = productVM.ProductId, Price = productVM.Price, Count = productVM.Count, CreatedDate = DateTime.Now }; cartItems.Add(cart); // 集合變數跑完後, 頁面跳轉就消失了, 所以把它存在Session裡面. Session["Cart"] = cartItems; } else { // 這一步是重點 cartItems = (List<Cart>)Session["Cart"]; //將Session中的購物車記錄還原成集合 Cart cart = new Cart { RecordId = cartItems.Count() + 1, CartId = Guid.NewGuid().ToString(), ProductId = productVM.ProductId, Price = productVM.Price, Count = productVM.Count, CreatedDate = DateTime.Now }; cartItems.Add(cart); // 在session做持續保存 Session["Cart"] = cartItems; } return cartItems.Count; } public ActionResult ListShoppingCart() { List<Cart> cartItems = (List<Cart>)Session["Cart"]; return View(cartItems); } ``` 最後就是要顯示有多少商品在購物車裡面了, 那因為點選按鈕是在`Nav`中 所以去一下`NavbarPartial`, 可以看到是導入到這個頁面中, 所以 ```htmlmixed= <li class="nav-item"> <a href="/Shopping/ListShoppingCart" class="nav-link text-dark" id="MyCart">購物車(@count)</a> </li> ``` 裡面的`Count` 去到`Shopping` Controller, 裡面的`ListShoppingCart` View的部分就是Crud產生出來的. ```csharp= public ActionResult ListShoppingCart() { List<Cart> cartItems = (List<Cart>)Session["Cart"]; return View(cartItems); } ``` ## Application補充 不同於Session, Application對所有進來網站的人給予同樣的結局 就像是觀站人數一樣, 所有人都可以看得到, 且不具隱密性. 使用方式如下: ```csharp= //Global.asax.cs Application["Tel"] = "0800-595-595"; //Index() public ActionResult Index() { string tel = HttpContext.Application["Tel"] as string; return View(); } //Index.cshtml @HttpContext.Current.Application["Tel"] ``` ## 思路 #### Home/Index作為網站進入首頁 這個範例是有分層的, 所以要先定義Resp和service. 把原始資料先放在Resp中. 而且在services裡面不受到`ActionResult`的約束, 所以可以自訂義型別. 在檢視頁面, 又透過_Partial建立個別的card. 注意傳遞的model是甚麼, 因為要給view顯示, 所以都是viewModel喔. #### Details/FindProductById作為明細頁 這個部分蠻精巧的, 因為無法知道傳遞的是甚麼Id, 所以透過route把 他統一給某一個action處理. 這裡依樣有分層的概念. #### Details/FindProductById.cshmtl建立購物車的Ajax程式設計 這一個頁面要傳送購物車訊息給到後端, 因此要寫一個ajax, 注意url和 要傳遞給後端的訊息. 同時這個資訊也會改變navbar的顯示. 我把它改了一個比較簡單的版本. #### Shopping控制器收到傳到產品資訊時,如何加入購物車,怎麼做處理?回傳購物車數量給前端 這一步驟是重點, 因為要把資料加入購物車要使用Session處理. 要分成兩個部分, 當CART裡面沒有資料和有資料的狀況, 處理方式不同 這部分還要新增一個集合來儲存個別的集合 * 前端Ajax將Shopping控制器回傳購物車數量資訊更新到HTML頁面上 * 按F5重新整理時的處理 * 控制器<-> ProductService <-> ProductRepository <-> SQL Server / NoSQL #### 點選購物車時,會導向購物車清單頁面 最後會導入到購買清單頁面, 另外頁面按F5也不會消失.