Try   HackMD

第四週:元件化

tags: 六角學院_Vue直播班_主線任務

目錄


產品頁面

刪除 modal 元件化

1.將原本刪除 modal 的 html,變成 x-template,元件標籤(delete-product-modal)

問題 成因 行動
預設匯出失敗 不知道怎麼寫 偷看助教寫的,改用 x-template 比較簡單

1.1 將 bootstap 實體化移到 delete-product-modal 的 mounted 裡面

問題 成因 行動
沒辦法把 modal 關掉 把關掉的 function ( hideModal ) 放到 methods 外面所以叫不到 塞回 methods

2.註冊 delete-product-modal

問題 成因 行動
註冊失敗 忘記區域註冊方法 偷看助教的,改成全域註冊

3.使用 delete-product-modal

問題 成因 行動
一直跳錯 x-template 的 script 標籤要放在 div.app 外面 放到外面

4.將選擇的產品(tempProduct) 傳入 delete-product-modal

5.點選確認刪除會刪掉商品,跳出提示後,關閉 modal

問題 成因 行動
按下確認刪除之後列表上的資料不會更新 刪除完後沒辦法呼叫外面的 this.getProducts() 內層用 emit 傳一個 update 事件呼叫外層的 getProducts()

新增編輯 modal 元件化

1 改成全域註冊的原件

1.1 將原本新增編輯 modal 的 html,變成 x-template

<script type="text/x-template" id="delProductModal"> <div>原本新增編輯 modal 的 html</div> </script>

1.2 進行全域註冊

app.component('update-product-modal', { template: '#productModal', data() { return { apiUrl: 'https://vue3-course-api.hexschool.io/v2', path: 'jasonchen', }; }, });

1.3 將 bootstrap 實體化移到 update-product-modal 的 mounted

mounted() { // 創建 bootstrap 實體 // 新增和編輯的 modal productModal = new bootstrap.Modal( document.getElementById('productModal'), { // esc 沒辦法關掉 modal keyboard: false, // 點選旁邊沒辦法關掉 modal backdrop: 'static', } ); }

2 將元件要用的資料傳入( update-product-modal )

2.1 在 html 使用 update-product-modal

<update-product-modal></update-product-modal>

2.2 將選擇的產品( tempProduct )傳入 update-product-modal

<update-product-modal :temp-product="tempProduct" ></update-product-modal>

2.3 將傳入的 tempProduct 用 props 接住

app.component('update-product-modal', { template: '#productModal', props: ['tempProduct'], });

2.4 將是否是編輯功能( isEdit )傳入,來決定 modal 是新增 modal 還是編輯 modal

<update-product-modal :temp-product="tempProduct" :is-edit="isEdit" ></update-product-modal>

2.5 將傳入的 isEdit 用 props 接住

app.component('update-product-modal', { template: '#productModal', props: ['tempProduct', 'isEdit'], });

3 將原本在外面的 methods 搬到元件裡面

3.1 點選新增 modal 的確認會呼叫 addProduct() 、editProduct(),當成功時會

3.1.1 呼叫 hideModal(),關閉 modal

3.1.1 向外傳出 update 事件

methods: { addProduct() { const url = `${this.apiUrl}/api/${this.path}/admin/product`; const addData = { data: this.tempProduct }; axios .post(url, addData) .then((res) => { alert(res.data.message); this.hideModal(); this.$emit('update'); }) .catch((err) => { alert(err.data.message); }); }, editProduct() { const url = `${this.apiUrl}/api/${this.path}/admin/product/${this.tempProduct.id}`; const editData = { data: this.tempProduct }; axios .put(url, editData) .then((res) => { alert(res.data.message); this.hideModal(); this.$emit('update'); }) .catch((err) => { alert(err.data.message); }); }, // 把關閉 modal 拉出來做成 function,將功能拆分清楚 hideModal() { productModal.hide(); }, },

3.2 外層接收 update 事件,呼叫 getProducts()

<update-product-modal :temp-product="tempProduct" :is-edit="isEdit" @update="getProducts" ></update-product-modal>
methods: { getProducts() { // 清空 tempProduct this.tempProduct = { imagesUrl: [] }; const url = `${this.apiUrl}/api/${this.path}/admin/products`; axios .get(url) .then((res) => { this.products = res.data.products; }) .catch((err) => { alert(err.data.message); }); }, }

分頁製作

1 放入分頁結構

<!-- 分頁 --> <nav aria-label="Page navigation "> <ul class="pagination"> <!-- 上一頁符號 --> <li class="page-item"> <a class="page-link" href="#" aria-label="Previous"> <span aria-hidden="true">&laquo;</span> </a> </li> <!-- 頁碼 --> <li class="page-item"><a class="page-link" href="#">1</a></li> <li class="page-item"><a class="page-link" href="#">2</a></li> <li class="page-item"><a class="page-link" href="#">3</a></li> <!-- 下一頁符號 --> <li class="page-item"> <a class="page-link" href="#" aria-label="Next"> <span aria-hidden="true">&raquo;</span> </a> </li> </ul> </nav>

2 data 部分新增 pagination ,在 getProducts() 拿到要做出分頁的資訊

const app = createApp({ data() { pagination: {}, }; }, methods: { getProducts() { // 清空 tempProduct this.tempProduct = { imagesUrl: [] }; const url = `${this.apiUrl}/api/${this.path}/admin/products`; axios .get(url) .then((res) => { this.products = res.data.products; this.pagination = res.data.pagination; }) .catch((err) => { alert(err.data.message); }); } } });

3 v-for 出幾個分頁

<li class="page-item" v-for="num in pagination.total_pages" :key="num"> <a class="page-link" href="#">{{num}}</a> </li>

4 是否有前一頁和後一頁

<!-- 上一頁符號 --> <li class="page-item" :class="{disabled : !pagination.has_pre}"> <a class="page-link" href="#" aria-label="Previous"> <span aria-hidden="true">&laquo;</span> </a> </li> <!-- 下一頁符號 --> <li class="page-item" :class="{disabled : !pagination.has_next}"> <a class="page-link" href="#" aria-label="Next"> <span aria-hidden="true">&raquo;</span> </a> </li>

5 在 data 新增現在的頁數( nowPage ),增加這個是為了在每次執行 getProducts() 時,不會跳回到第一頁

const app = createApp({ data() { nowPage: 1, }; }, methods: { getProducts() { // 清空 tempProduct this.tempProduct = { imagesUrl: [] }; const url = `${this.apiUrl}/api/${this.path}/admin/products?page=${this.nowPage}`; axios .get(url) .then((res) => { this.products = res.data.products; this.pagination = res.data.pagination; this.nowPage = res.data.pagination.current_page; }) .catch((err) => { alert(err.data.message); }); } });

6 使用 nowPage 決定頁碼的 style

<li class="page-item" v-for="num in pagination.total_pages" :key="num" :class="{active : num === nowPage}"> <a class="page-link" href="#">{{num}}</a> </li>

7 點擊分頁頁碼會去取得該頁的資訊

<li class="page-item" v-for="num in pagination.total_pages" :key="num" :class="{active : num === nowPage}"> <a class="page-link" href="#" @click="changePage(num)">{{num}}</a> </li>
const app = createApp({ methods: { changePage(page) { this.nowPage = page; this.getProducts(); } });

分頁元件化

1 改成全域註冊的原件

1.1 將原本分頁 的 html,變成 x-template

<script type="text/x-template" id="productsPagination"> <nav aria-label="Page navigation"> <ul class="pagination"> <!-- 上一頁符號 --> <li class="page-item" :class="{disabled : !pagination.has_pre}"> <a class="page-link" href="#" aria-label="Previous" @click.prevent="$emit('update',pagination.current_page-1)"> <span aria-hidden="true">&laquo;</span> </a> </li> <!-- 頁碼 --> <li class="page-item" v-for="num in pagination.total_pages" :key="num" :class="{active : num === pagination.current_page}" > <a class="page-link" href="#" @click.prevent="$emit('update',num)">{{num}}</a> </li> <!-- 下一頁符號 --> <li class="page-item" :class="{disabled : !pagination.has_next}"> <a class="page-link" href="#" aria-label="Next" @click.prevent="$emit('update',pagination.current_page+1)"> <span aria-hidden="true">&raquo;</span> </a> </li> </ul> </nav> </script>

1.2 進行全域註冊

app.component('products-pagination', { template: '#productsPagination' });

2 將元件要用的資料傳入( update-product-modal )

2.1 在 html 使用 update-product-modal

<products-pagination></products-pagination>

2.2 將選頁碼資訊( pagination ),傳入 update-product-modal

<products-pagination :pagination="pagination" ></products-pagination>

2.3 將傳入的 tempProduct 用 props 接住

app.component('products-pagination', { template: '#productsPagination', props: ['pagination'], });

3 內元件( products-pagination )向外元件傳送資訊

3.1 點選頁碼時會向外面傳出 update 事件和現在點選的頁碼數字

<li class="page-item" v-for="num in pagination.total_pages" :key="num" :class="{active : num === pagination.current_page}"> <a class="page-link" href="#" @click.prevent="$emit('update',num)">{{num}}</a> </li>

3.2 外層接收 update 事件和點選數字,呼叫 changePage()

<products-pagination :pagination="pagination" @update="changePage" ></products-pagination>
const app = createApp({ methods: { changePage(page) { this.nowPage = page; this.getProducts(); } });

4 點選上下一頁可以換頁

4.1 點選上下一頁會向外面傳出 update 事件和加或減現在點選的頁碼數字( nowPage加或減 1 )

<!-- 上一頁符號 --> <li class="page-item" :class="{disabled : !pagination.has_pre}"> <a class="page-link" href="#" aria-label="Previous" @click.prevent="$emit('update',pagination.current_page-1)"> <span aria-hidden="true">&laquo;</span> </a> </li> <!-- 下一頁符號 --> <li class="page-item" :class="{disabled : !pagination.has_next}"> <a class="page-link" href="#" aria-label="Next" @click.prevent="$emit('update',pagination.current_page+1)"> <span aria-hidden="true">&raquo;</span> </a> </li>

4.2 要避免第一頁和最後一頁爆掉問題

changePage(page) { // 第一頁不能往前 if (page <= 1) { page = 1; } // 最後一頁不能往後 if (page >= this.pagination.total_pages) { page = this.pagination.total_pages; } this.nowPage = page; this.getProducts(); }

外層元件

1.每次 getProducts(),就清空 tempProduct ,原本是寫在按各個 modal 確認按鈕時會執行,現在每次按確認都會傳出一個 update 事件執行 getProducts() ,所以寫在getProducts() 裡面一次就好

getProducts() { // 清空 tempProduct this.tempProduct = { imagesUrl: [] }; const url = `${this.apiUrl}/api/${this.path}/admin/products`; axios .get(url) .then((res) => { this.products = res.data.products; }) .catch((err) => { alert(err.data.message); }); },

2.另外在打開新增 modal 時,也要清空一次 tempProduct,保險起見

openModal(type, product) { // 使用深層拷貝避免改動 modal 的值時,改到外面清單的值 this.tempProduct = JSON.parse(JSON.stringify(product)); if (type === 'delete') { delProductModal.show(); } else if (type === 'add') { // 清空 tempProduct this.tempProduct = { imagesUrl: [] }; this.isEdit = false; productModal.show(); } else if (type === 'edit') { this.isEdit = true; productModal.show(); } }

問題紀錄模板

問題 成因 行動
cookie 寫不進去 忘記怎麼把回傳的 cookie 寫進去 偷看之前寫的
功能 步驟 問題 成因 行動
GitHub Sync
Browser Extension
Book Mode
Slide Mode
Share & Publish