# 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也不會消失.