目錄javascript
什麼是MVC:css
MVC的做用:html
效果:java
如下是全部代碼:node
後端(nodejs):程序員
server.jsajax
前端express
Model View Controller即:模型-視圖-控制器
通俗來說,在編程語言中,Model就是數據,能夠理解爲數據庫,View就是顯示數據的外觀,Controller是用來鏈接前二者的行爲,常見的Vue採用的是M-V-VM架構,與MVC相似,可是基於MVC
說到做用,就不得不提面向對象與面向過程的區別了
面向過程就是,將解決問題的思路流程一步一步進行,緊扣在一塊兒,最終達到結果
面向對象,是將某個問題的解決方式剝離開,其目的不是爲了完成某個步驟,而是將某個事物(對象)的角色(屬性)和行爲(方法)做爲核心
說了這些,到底MVC有什麼好處呢?
舉個栗子:A是某公司的一位前端程序員,平時用面向過程進行編程,這天,好不容易完成了手頭上的活,準備回家,這時,產品經理走過來,讓他改個小地方,這下就完了,面向過程的思惟使他的代碼環環相扣,代碼耦合性強,內聚性高,密不可分,改一個地方就要幾乎全改
A的哥哥也是一個前端程序員,平時用面向對象編程,產品經理讓他改一個效果,因爲用的面向對象,他的代碼沒有層次感,通用的方法所有提取出來,使得代碼耦合性低,想改哪直接改相關的類或者方法就行了
固然,在小型項目中沒法體現它的優勢,甚至會小題大作,大材小用,而在大型項目中,其耦合性低,代碼複用性高,搭建相對較快
又是這個購物車,業餘時間用MVC作了一個簡單的購物車:
目錄結構大體是這樣
購物車總體流程:
目錄結構將model view controller剝離開
Modedl層:存儲數據,顯示數據
View:根據Model數據渲染頁面
Controller:傳遞數據
Command:操做數據,獲取數據
Event:事件總線,註冊事件
商品列表:
初始化View層,創建Ajax獲取數據,以後由controller觸發事件至事件總線,而後再由註冊的事件將ajax數據傳至Model中完成商品列表初始化
當model獲取到商品列表數據時,經過代理set() 觸發新建商品列表事件,經過command操做view達到新建列表目的
購物車表格:
當用戶對view進行操做時,觸發註冊的事件,經過command修改Model中的數據(購物車列表)從而再由command驅動view中的刷新表格進行渲染
/* *後端採用node+express搭建一個簡單的接口,經過本地數據,將商品列表傳至前端 * */ const express = require('express'); const path = require('path'); const app = express(); const shopData = require('./data/shopData.js') let serverToken = 'hello' app.all("*", function (req, res, next) { //跨域 res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "content-type"); res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); next(); }); app.use('/getShopList', function (req, res) { let data = req.query if (!checkToken(data.token)) { //簡單獲取前端token,校驗 res.send({ result: 0, msg: 'token fail' }) return } res.send({ result: 1, msg: 'success', type: 'getShopList', shopData }) }) function checkToken(teken) { return teken == serverToken } app.use('/img', express.static(path.join(__dirname, './img'))); //後端目錄靜態化,用url+img訪問文件夾 app.use('/client', express.static(path.join(__dirname, '../client'))); app.listen(1024, "127.0.0.1", function () { console.log("服務開啓,開始偵聽"); });
module.exports = [{ "select": false, "id": 1001, "icon": "img/1.png", "name": "餐飲0", "num": 0, "price": 10, "sum": 0, "delete": false }, { "select": false, "id": 1002, "icon": "img/2.png", "name": "餐飲1", "num": 0, "price": 20, "sum": 0, "delete": false }, { "select": false, "id": 1003, "icon": "img/3.png", "name": "餐飲2", "num": 0, "price": 30, "sum": 0, "delete": false }, { "select": false, "id": 1004, "icon": "img/4.png", "name": "餐飲3", "num": 0, "price": 40, "sum": 0, "delete": false }, { "select": false, "id": 1005, "icon": "img/5.png", "name": "餐飲4", "num": 0, "price": 50, "sum": 0, "delete": false }, { "select": false, "id": 1006, "icon": "img/6.png", "name": "餐飲5", "num": 0, "price": 60, "sum": 0, "delete": false }, { "select": false, "id": 1007, "icon": "img/7.png", "name": "餐飲6", "num": 0, "price": 70, "sum": 0, "delete": false }, { "select": false, "id": 1008, "icon": "img/8.png", "name": "餐飲7", "num": 0, "price": 80, "sum": 0, "delete": false }, { "select": false, "id": 1009, "icon": "img/9.png", "name": "餐飲8", "num": 0, "price": 90, "sum": 0, "delete": false }, { "select": false, "id": 1010, "icon": "img/10.png", "name": "餐飲9", "num": 0, "price": 100, "sum": 0, "delete": false } ]
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>shopCar</title> <link rel="stylesheet" href="./src/style/shop.css"> </head> <body> <script type="module"> /* 購物車總體流程: 目錄結構將model view controller剝離開 Modedl層:存儲數據,顯示數據 View:根據Model數據渲染頁面 Controller:傳遞數據 Command:操做數據,獲取數據 Event:事件總線,註冊事件 商品列表: 初始化View層,創建Ajax獲取數據,以後由controller觸發事件至事件總線,而後再由註冊的事件將ajax數據傳至Model中完成商品列表初始化 當model獲取到商品列表數據時,經過代理set() 觸發新建商品列表事件,經過command操做view達到新建列表目的 購物車表格: 當用戶對view進行操做時,觸發註冊的事件,經過command修改Model中的數據(購物車列表)從而再由command驅動view中的刷新表格進行渲染 */ import ShopView from './src/js/view/ShopView.js' // 實例化View層入口函數 new ShopView() </script> </body> </html>
* { margin: 0; padding: 0; } .shopBox { overflow: hidden; width: 1000px; margin: 50px auto 0; } .liItem { float: left; list-style: none; padding: 10px; width: 150px; height: 200px; text-align: center; border: 1px solid lightcoral; } .liItem img { width: 100px; height: 100px; } .leftBtn, .rightBtn { width: 30px; height: 30px; background: white; border: 1px solid black; font-size: 25px; line-height: 30px; } .text { width: 50px; height: 26px; display: inline-block; vertical-align: bottom; text-align: center; } table { font-size: 30px; width: 1200px; border: 1px solid lightcoral; border-collapse: collapse; margin: 50px auto; } .checkbox { width: 30px; height: 30px; } td { border: 1px solid lightcoral; text-align: center; vertical-align: middle; } td button { width: 150px; height: 60px; } .numBox { width: 150px; height: 30px; margin: auto; position: relative; } .numBox>button { width: 40px; height: 42px; background-color: white; border: 1px solid #000000; } .numBox>input { width: 70px; height: 40px; border: 1px solid #000000; border-left: none; border-right: none; text-align: center; }
import ShopEvent from '../event/ShopEvent.js' import Utils from '../utils/Utils.js' import Api from '../config/Api.js' import ShopController from '../controller/ShopController.js' export default class Ajax {//Ajax類,用於請求後端或本地數據 // Ajax請求函數 static AjaxTool(method = Api.GET, url, data) { let xhr; if (window.ActiveXObject) { //ie瀏覽器 xhr = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { //其餘瀏覽器 xhr = new XMLHttpRequest(); } url = Api.URL + Api.PORT + Api.PATH + url if (method !== Api.POST) { method = Api.GET url = Utils.urlJoin(url, data) data = null } else { method = Api.POST } xhr.open(method, url); xhr.send(data ? JSON.stringify(data) : '') xhr.addEventListener('load', Ajax.loadHandler) //Ajax類是靜態類,沒法使用this } static loadHandler(e) { //this指向xhr let xhr = e.currentTarget; if (xhr.readyState === 4 && xhr.status === 200) { Ajax.data = xhr.response } else { Ajax.data = 'error' } } static set data(value) { //使用set對象代理模式替代請求數據回調函數(只寫set表示data只可寫入,不可讀取) let res = JSON.parse(value) switch (res.result) { case 1: console.log(res.msg) ShopController.dispatch(ShopEvent.GET_DATA, res)//獲取到數據後不作其餘操做,將數據經過事件拋出至Event總線中 break; case 0: console.log('加載失敗') console.log(res.msg) break; default: break; } } }
import GetDataCommand from '../command/GetDataCommand.js' import CreateListCommand from '../command/CreateListCommand.js' import CreateTableCommand from '../command/CreateTableCommand.js' import AddItemCommand from '../command/AddItemCommand.js' import DelItemCommand from '../command/DelItemCommand.js' import ReduceItemCommand from '../command/ReduceItemCommand.js' import ChangeItemCommand from '../command/ChangeItemCommand.js' import SelectItemCommand from '../command/SelectItemCommand.js' export default { GetDataCommand, CreateListCommand, CreateTableCommand, AddItemCommand, DelItemCommand, ReduceItemCommand, ChangeItemCommand, SelectItemCommand }
import ShopModel from '../model/ShopModel.js' export default class GetDataCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() { } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e ShopModel.getInstance().shopList = data.shopData//將ajax獲取的數據發送到Model } }
import CreateList from '../view/CreateList.js' export default class CreateListCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() {//建立商品列表 } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e for (let i = 0; i < data.length; i++) { let createList = new CreateList(document.body) createList.shopList = data[i] } } }
import CreateTable from '../view/CreateTable.js' export default class ShopCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() {//刷新購物車表格 } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e let createTable = new CreateTable(document.body) createTable.shoppingList = data } }
import ShopModel from '../model/ShopModel.js' export default class AddItemCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() { //新增商品 } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e AddItemCommand.addItem(ShopModel.getInstance().shoppingList, data) } static addItem(list, data) { //遍歷查詢某項商品增長或減小 let arr = list.filter(function (item) { return item.id === data.id; }); //如有返回值則對某項商品操做(在1-99區間,若爲0則直接刪除) if (arr.length == 0) { data.num++; data.sum = data.num * data.price; list.push(data); } else if (arr[0].num < 99) { arr[0].num++; arr[0].sum = arr[0].num * arr[0].price; } ShopModel.getInstance().shoppingList = list } }
import ShopModel from '../model/ShopModel.js' export default class ReduceItemCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() { //減小商品 } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e ReduceItemCommand.reduceItem(ShopModel.getInstance().shoppingList, data) } static reduceItem(list, data) { //遍歷查詢某項商品增長或減小 let arr = list.filter(function (item) { return item.id === data.id; }); //如有返回值則對某項商品操做(在1-99區間,若爲0則直接刪除) if (arr[0].num > 1) { arr[0].num--; arr[0].sum = arr[0].num * arr[0].price; } else { data.num = 0; //此處初始化model中的shopList,不然會假刪除(刪除棧中的數量) list = list.filter(function (item) { return item.id !== data.id; }); } ShopModel.getInstance().shoppingList = list } }
import ShopModel from '../model/ShopModel.js' export default class ChangeItemCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() { //修改商品數量 } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e ChangeItemCommand.changeItem(ShopModel.getInstance().shoppingList, data) } static changeItem(list, data) { let arr = list.filter(function (item) { return item.id === data.id; }); arr[0].sum = arr[0].num * arr[0].price; ShopModel.getInstance().shoppingList = list } }
import ShopModel from '../model/ShopModel.js' export default class DelItemCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() { //刪除商品 } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e DelItemCommand.delItem(ShopModel.getInstance().shoppingList, data) } static delItem(list, data) { //遍歷查詢某項商品增長或減小 data.num = 0; //此處初始化model中的shopList,不然會假刪除(刪除棧中的數量) data.select = false; //此處初始化model中的shopList,不然會假刪除(刪除棧中的數量) ShopModel.getInstance().shoppingList = list.filter(function (item) { //數組過濾函數,返回id屬性不等於當前id的數組,即刪除當前選中的對象,並從新賦值 return item.id !== data.id; }); } }
import ShopModel from '../model/ShopModel.js' export default class SelectItemCommand { //行爲類,用於執行ctrl層經過事件的方式發來的指令 constructor() { } eventHandler(e) { //使用動態方法,而不是static靜態方法,由於該方法將被使用屢次,使用new AddShopCommand實例化後調用 let { data } = e SelectItemCommand.selItem(ShopModel.getInstance().shoppingList, data) } static selItem(list, data) { //遍歷查詢某項商品增長或減小 if (!data) { //全選框 list.checkedAll = !list.checkedAll list.map(function (item) { item.select = list.checkedAll; //其餘選項框與全選框狀態一致 }) } else { //單選框 list.checkedAll = 1 //計數器,用來查詢是否爲全選狀態 list.map(function (item) { //單選,選中某一個(在表格初始化時執行checkAll判斷是否全選) if (item.id === data.id) { item.select = !item.select } list.checkedAll *= item.select }) } ShopModel.getInstance().shoppingList = list } }
import ShopEvent from '../event/ShopEvent.js' import ShopController from '../controller/ShopController.js' import Utils from '../utils/Utils.js' export default class Counter { //計數器組件 constructor(_data, _parentEle) { this.data = _data this.parentEle = _parentEle this.ele = this.createCounter() } createCounter() { //建立數量計數器 let div = Utils.createEle('div', {}, { className: 'numBox' }) this.parentEle.appendChild(div); let leftBtn = this.createMark(div, 'reduce') //減小商品按鈕 let input = Utils.createEle('input', {}, { type: 'text', value: this.data.num }) div.appendChild(input); let rightBtn = this.createMark(div, 'add') //新增商品按鈕 leftBtn.addEventListener("click", this.reduceItemEvent); rightBtn.addEventListener("click", this.addItemEvent); input.addEventListener("input", Utils.throttle(this.changeItemEvent, 500)); // 節流 return div; } createMark(parentEle, type) { //判斷增長或減小鍵 let markBtn = Utils.createEle('button', {}, { textContent: type == "add" ? '+' : '-' }) parentEle.appendChild(markBtn); return markBtn } addItemEvent = e => { //新增商品時,拋發事件至command控制model修改數據,刷新表格 ShopController.dispatch(ShopEvent.ADD_SHOPIING_ITEM, this.data) } reduceItemEvent = e => { //減小商品 ShopController.dispatch(ShopEvent.REDUCE_SHOPIING_ITEM, this.data) } changeItemEvent = e => { //修改商品 e.target.value = this.data.num = this.checkNumber(e.target.value) ShopController.dispatch(ShopEvent.CHANGE_SHOPIING_ITEM, this.data) } checkNumber(value) { //過濾數據 value = value.replace(/[^0-9]/g, ""); //只容許輸入數字 if (value === "0") { // 若是=0,就設爲1 value = "1"; } if (value.length > 2) { // 若是輸入的內容大於2位,讓這個值爲99(最大99個) value = "99"; } if (value.length === 0) { // 若是什麼都沒有輸入,也設爲1 value = "1"; } return value } }
export default class Api {//接口配置類 static URL = "http://127.0.0.1"; static PORT = ":1024"; static PATH = '/' static GET = "get"; static POST = "post"; static IMGPATH = Api.URL + Api.PORT + Api.PATH; static ServerApi = { getShopList: 'getShopList' //獲取商品列表 } }
export default class ShopController extends EventTarget { //控制層,處理用戶交互,路由,輸入,將model view controller剝離開,經過controller中的事件監聽拋發進行路由傳輸數據 constructor() { //繼承事件對象,用於拋發自定義事件 super(); } static get instance() { //單例寫法與java中getinstance類似,new會生成一個新對象,分配內存,而這麼寫能夠把一個已存在的引用給你使用,節省效能,若只使用get + 屬性名而不用set產生只讀屬性,只容許調用,沒法修改 if (!ShopController._instance) { Object.defineProperty(ShopController, "_instance", { value: new ShopController() }) } return ShopController._instance; } static dispatch(type, data) { //拋發自定義事件,傳遞數據 let event = new Event(type) event.data = data ShopController.instance.dispatchEvent(event) } static runCommand(type, Command) { //觀察者模式,當自定義事件觸發時調用其餘類中的方法,與dispatch對應,相似於addEventlistener,只不過將回調函數換成類中的動態方法 var command = new Command() ShopController.instance.addEventListener(type, command.eventHandler) } }
export default class ShopEvent { constructor() { } // 全部自定義事件名稱 static GET_DATA = 'get_data' static GET_SHOP_LIST = 'get_shop_list' static GET_SHOPIING_LIST = 'get_shopping_list' static ADD_SHOPIING_ITEM = 'add_shopping_item' static DEL_SHOPIING_ITEM = 'del_shopping_item' static REDUCE_SHOPIING_ITEM = 'reduce_shopping_item' static CHANGE_SHOPIING_ITEM = 'change_shopping_item' static SELECT_SHOPIING_ITEM = 'select_shopping_item' }
import ShopEvent from './ShopEvent.js' import ShopController from '../controller/ShopController.js' import MainCommand from '../command/MainCommand.js' let { GetDataCommand, CreateListCommand, CreateTableCommand, AddItemCommand, DelItemCommand, ReduceItemCommand, ChangeItemCommand, SelectItemCommand } = MainCommand export default class EventGroup { //事件總線,註冊全部model層與其它層的業務邏輯,全程經過controller層中的事件機制進行通訊 constructor() { /* 1.Ajax獲取到數據後,觸發GetDataCommand中的方法,用於傳遞數據至Model層中,而後經過Model層調用CreateListCommand創造商品列表 2.當用戶對商品作任何操做時,都會修改Model從而觸發CreateTableCommand,如下操做會觸發CreateTableCommand 3.點擊商品列表或點擊商品加號按鈕時觸發AddItemCommand,經過AddItemCommand修改model中的數據,從而驅動CreateTableCommand 4.點擊商品減號按鈕時觸發ReduceItemCommand,修改model中的數據,從而驅動CreateTableCommand 5.點擊商品刪除按鈕時觸發DelItemCommand,刪除model中的數據,從而驅動CreateTableCommand 6.修改商品數量時觸發ChangeItemCommand,更新model中的數據,從而驅動CreateTableCommand 7.選中商品時觸發SelectItemCommand,更新model中的數據,從而驅動CreateTableCommand */ ShopController.runCommand(ShopEvent.GET_DATA, GetDataCommand)//獲取商品列表數據 ShopController.runCommand(ShopEvent.GET_SHOP_LIST, CreateListCommand)//新建商品列表 ShopController.runCommand(ShopEvent.GET_SHOPIING_LIST, CreateTableCommand)//刷新購物車表格 ShopController.runCommand(ShopEvent.ADD_SHOPIING_ITEM, AddItemCommand)//商品新增或數量加一 ShopController.runCommand(ShopEvent.DEL_SHOPIING_ITEM, DelItemCommand)//商品刪除 ShopController.runCommand(ShopEvent.REDUCE_SHOPIING_ITEM, ReduceItemCommand)//商品數量減一 ShopController.runCommand(ShopEvent.CHANGE_SHOPIING_ITEM, ChangeItemCommand)//修改商品數量 ShopController.runCommand(ShopEvent.SELECT_SHOPIING_ITEM, SelectItemCommand)//選擇商品 } }
import ShopEvent from '../event/ShopEvent.js' import ShopController from '../controller/ShopController.js' export default class ShopModel { //模型層,用於數據存放及數據邏輯,經過事件處理機制(controller)傳遞數據,再由command進行對數據操做 constructor() { this._shopList = null this._shoppingList = [] } static getInstance() { //單例寫法與java中getinstance類似,new會生成一個新對象,分配內存,而這麼寫能夠把一個已存在的引用給你使用,節省效能,若只使用get + 屬性名而不用set產生只讀屬性,只容許調用,沒法修改 if (!ShopModel._instance) { Object.defineProperty(ShopModel, "_instance", { value: new ShopModel() }) } return ShopModel._instance; } set shopList(value) {//設置商品列表 this._shopList = value; ShopController.dispatch(ShopEvent.GET_SHOP_LIST, value) } get shopList() { return this._shopList } set shoppingList(value) {//數據修改時,驅動view進行表格刷新 this._shoppingList = value; ShopController.dispatch(ShopEvent.GET_SHOPIING_LIST, value) } get shoppingList() { return this._shoppingList } }
export default class Utils { //工具類 //將對象拼接到url中 static urlJoin(url, obj) { var list = [] for (var key in obj) { if (obj.hasOwnProperty(key)) { list.push(`${key}=${obj[key]}`) } } return `${url}?${list.join('&')}` } static createEle(ele, style, attribute) { //新增標籤,設置屬性及樣式 let element = document.createElement(ele) if (style) { for (let key in style) { element.style[key] = style[key]; } } if (attribute) { for (let key in attribute) { element[key] = attribute[key]; } } return element } // 函數節流 static throttle(fn, time) { let _timer = null return function () { if (_timer) { clearTimeout(_timer) _timer = null } _timer = setTimeout(fn.bind(this, ...arguments), time) } } }
import Api from '../config/Api.js' import AJAX from '../bussiness/Ajax.js' import EventGroup from '../event/EventGroup.js' export default class ShopView { //視圖層,用於元素渲染,顯示數據,依據(model)模型數據建立 constructor() { new EventGroup() //註冊全部自定義事件,用於數據傳輸 AJAX.AjaxTool(Api.GET, Api.ServerApi.getShopList, { //請求服務端購物車列表 token: 'hello'//發送從後端獲取的token用於驗證,此處未作獲取,直接用一個字符代替 }) } }
import Api from '../config/Api.js' import Utils from '../utils/Utils.js' import ShopEvent from '../event/ShopEvent.js' import ShopController from '../controller/ShopController.js' export default class CreateList { //視圖層,用於元素渲染,顯示數據,依據(model)模型數據建立 constructor(parentEle) { this.parentEle = parentEle this._shopList = null } set shopList(value) { //使用對象代理,每當數據發生更改時渲染商品列表 if (this._shopList) { this.createListEle(this._shopList, this.parentEle) return; } this._shopList = value this.createListEle(value, this.parentEle) } get shopList() { return this._shopList } createListEle(data, parentEle) { let li = Utils.createEle('li', {}, { 'className': 'liItem' }) let img = Utils.createEle('img', {}, { 'src': Api.IMGPATH + data.icon }) let title = Utils.createEle('div', {}, { 'textContent': data.name }) let price = Utils.createEle('span', {}, { 'textContent': data.price + "元" }) li.appendChild(img); li.appendChild(title); li.appendChild(price); li.addEventListener('click', this.addItemEvent) parentEle.appendChild(li); } addItemEvent = e => { //當用戶點擊添加商品時,將數據經過controller層發送至事件總線,再驅動model層並修改數據,後續由Model提供數據刷新表格 ShopController.dispatch(ShopEvent.ADD_SHOPIING_ITEM, this.shopList) } }
import Api from '../config/Api.js' import Utils from '../utils/Utils.js' import Counter from '../components/Counter.js' import ShopEvent from '../event/ShopEvent.js' import ShopController from '../controller/ShopController.js' export default class CreateTable { //視圖層,用於元素渲染,顯示數據,依據(model)模型數據建立 constructor(parentEle) { this.parentEle = parentEle this._shoppingList = null this._table = null } set shoppingList(value) { //對象代理,若model數據改變時,刷新表格 let table = document.getElementById('table') if (table) { //初始化表格,如有則刪除 table.remove() table = null; } this._shoppingList = value || [] this.createTab(value, this.parentEle) } get shoppingList() { return this._shoppingList } createTab(data, parentEle) { this._table = Utils.createEle('table', {}, { id: "table" }) let thr = Utils.createEle('tr') for (let prop in data[0]) { //建立表頭,若是屬性名是select,就建立全選按鈕 let th = Utils.createEle('th') if (prop === "select") { let input = Utils.createEle('input', {}, { type: "checkbox", className: "checkbox", checked: this.checkedAll() //查詢是否全選 }) input.addEventListener("change", this.seleteEvent); th.appendChild(input); } else { th.textContent = prop; } thr.appendChild(th) } this._table.appendChild(thr) for (let i = 0; i < data.length; i++) { let tr = Utils.createEle('tr') this._table.appendChild(tr); for (let str in this.shoppingList[i]) { let td = document.createElement("td"); this.selectTdType(td, this.shoppingList[i], str); tr.appendChild(td); } } parentEle.appendChild(this._table) } selectTdType(td, data, type) { switch (type) { case 'select': let input = Utils.createEle('input', {}, { type: 'checkbox', className: 'checkbox', checked: data['select'], data }) input.addEventListener("change", this.seleteEvent); td.appendChild(input); break; case 'icon': let img = Utils.createEle('img', {}, { 'src': Api.IMGPATH + data.icon }) td.appendChild(img); break; case 'num': new Counter(data, td) //實例化計數器組件 break; case 'delete': let btn = Utils.createEle('button', {}, { textContent: '刪除', data }) td.appendChild(btn); btn.addEventListener("click", this.deleteItemEvent); break; default: td.textContent = data[type]; break; } } deleteItemEvent = e => { //觸發刪除事件,經過command刪除後通知model驅動view進行刷新 ShopController.dispatch(ShopEvent.DEL_SHOPIING_ITEM, e.target.data) } seleteEvent = e => { //觸發選擇商品事件,經過command刪除後通知model驅動view進行刷新 ShopController.dispatch(ShopEvent.SELECT_SHOPIING_ITEM, e.target.data) } checkedAll() { let count = 1 this.shoppingList.map((item) => { count *= item.select }) return count } }