用戶操作 → AJAX 呼叫 → WCF 服務 → LINQ 查詢 → 回傳 JSON → 前端動態渲染,全程無整頁重新整理。
SearchBar.ascx 中的搜尋按鈕點擊處理:
function searchButton_Click() {
var params = { PostalCode, MenuType, RestaurantCategory, DeadLine };
if (!isInSearchPage) {
window.location.href = "search.aspx?" + $.param(params); // 首次搜尋導向結果頁
} else {
var svc = new DinnerNow.Services.IMenuSearchService();
svc.FindRestaurant(params.PostalCode, params.MenuType, params.RestaurantCategory, params.DeadLine,
onRestaurantSeachSuccess, onRestaurantSeachFailed, null);
}
}
function onRestaurantSeachSuccess(result) {
var container = document.getElementById("restaurantList");
container.innerHTML = "";
result.forEach(function(r) {
var html = `<a href="javascript:restaurantSelection_Click('${r.RestaurantId}', '${r.LogoImageLocation}', '${r.Name}')">`
+ `<img src="${r.LogoImageLocation}" alt="${r.Name}" width="154" height="90" /></a>`;
var span = document.createElement("span");
span.innerHTML = html;
container.appendChild(span);
});
DisplayDiv("SearchResultsDivision");
}
function restaurantSelection_Click(id, logo, name) {
// 更新餐廳資訊區
document.getElementById("restaurantImage").src = logo;
document.getElementById("restaurantName").innerHTML = name;
document.getElementById("restaurantID").innerHTML = id;
var svc = new DinnerNow.Services.IMenuSearchService();
var menuType = return_MenuType();
svc.GetMenuItemsForMenu(id, menuType, restaurantSelection_onSuccess, restaurantSelection_onFailed, null);
svc.GetMenuTypes(getMenuTypes_onSuccess, getMenuTypes_onFailed, null);
}
function restaurantSelection_onSuccess(result) {
var container = document.getElementById("menuList");
container.innerHTML = "";
result.forEach(function(item, i) {
var html = `<table class='thinblueline'><tr>`
+ `<td><img src="${item.ImageLocation}" /></td>`
+ `<td><strong>${item.Name}</strong><br>${item.Description}<br>`
+ `預計 ${item.PreparationTime} 分鐘</td>`
+ `<td><strong>$${item.Price}</strong><br>`
+ `<a href="javascript:AddItemToShoppingCart(...)"><img src="images/selectbutton.gif" /></a></td>`
+ `</tr></table>`;
var span = document.createElement("span");
span.innerHTML = html;
container.appendChild(span);
});
DisplayDiv("MenuDivision");
}
function AddItemToShoppingCart(description, imageLocation, menuId, menuItemId, name, prepTime, price) {
var menuItem = new DinnerNow.Business.Data.RestaurantMenuItem();
menuItem.Description = description; menuItem.ImageLocation = imageLocation;
menuItem.MenuId = menuId; menuItem.MenuItemId = menuItemId;
menuItem.Name = name; menuItem.PreparationTime = prepTime; menuItem.Price = price;
var restaurant = new DinnerNow.Business.Data.RestaurantHeader();
restaurant.RestaurantId = document.getElementById("restaurantID").innerHTML;
restaurant.Name = document.getElementById("restaurantName").innerHTML;
restaurant.LogoImageLocation = document.getElementById("restaurantImage").src;
DinnerNow.ShoppingCartService.AddItem(menuItem, restaurant, menuSort,
addItemToShoppingCart_onSuccess, addItemToShoppingCart_onFailed, null);
}
DinnerNow.WebUX\Code\ShoppingCartService.cs 以 [OperationContract] 暴露給 AJAX:
[OperationContract]
public List<ShoppingCartItem> AddItem(RestaurantMenuItem selectedItem, RestaurantHeader restaurant, int selectedSortOption) {
var cartItem = new ShoppingCartItem {
DeliveryTime = selectedItem.PreparationTime,
MenuItemIdentifier = selectedItem.MenuItemId.ToString(),
MenuItemName = selectedItem.Name,
PreparationTime = selectedItem.PreparationTime,
Price = selectedItem.Price,
Quantity = 1,
RestaurantIdentifier = restaurant.RestaurantId.ToString(),
RestaurantImageLocation = restaurant.LogoImageLocation,
RestaurantName = restaurant.Name,
RestaurantItem = selectedItem,
Restaurant = restaurant
};
shoppingCart.AddItem(cartItem); // Session/記憶體購物車邏輯
return RefreshItems(selectedSortOption);
}
function _refreshShoppingCart(result) {
var container = document.getElementById("shoppingCartList");
container.innerHTML = "";
// 按餐廳分組渲染,含數量輸入框、刪除按鈕、小計
result.forEach(function(item) {
// ... 生成複雜 HTML 表格結構 ...
});
// 非同步取得總計
DinnerNow.ShoppingCartService.Totals(getTotals_onSuccess, getTotals_onFailed, null);
}
ScriptManager 註冊 <asp:ServiceReference Path="~/service/MenuSearch.svc/ajax" />,自動產生 JS Proxy(DinnerNow.Services.IMenuSearchService),呼叫語法如 C# 般強型別。RestaurantMenuItem, RestaurantHeader)自動雙向序列化,無需手寫 JSON.parse/stringify。onSuccess(result)/onFailed(error) 簽章,錯誤處理集中化。購物車狀態維持在 ShoppingCartService(WebUX 專案)的 Session/靜態集合中,而非資料庫。這適合「瀏覽-選購」臨時狀態,下單時才持久化至後端訂單系統。分離關注點:前端服務負責 UI 狀態,後端 WCF 負責業務查詢。
DinnerNow 點餐流程展示了 ASP.NET AJAX Extensions 如何構建類桌面應用的 Web 體驗:JS Proxy 呼叫 WCF 服務查詢資料 → 客戶端組裝 DTO 呼叫本地購物車服務 → 回調渲染動態 HTML。這種「厚客戶端、薄伺服器端點」模式,將渲染邏輯前移,大幅降低伺服器壓力與頻寬佔用,同時保持代碼結構清晰、可維護。