EP31 - ex3. Grid with Sort and Filter

更新於 發佈於 閱讀時間約 22 分鐘
表格耶!這幾個範例真的是蠻實用的!~
快學起來~看看這個表格會用到幾行程式碼就完成?
前端好好玩~ Grid with Sort and Filter

App.vue

<!--
An example of creating a reusable grid component and using it with external data.
-->

<script setup>
import DemoGrid from './Grid.vue'
import { ref } from 'vue'

const searchQuery = ref('')
const gridColumns = ['name', 'power']
const gridData = [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]
</script>

<template>
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<DemoGrid
:data="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</DemoGrid>
</template>

這是一個示例,展示了如何創建一個可重用的表格組件並使用外部數據。

<script setup> 部分

使用 Vue 3 的 <script setup> 語法來簡化組件的定義和設置。

import DemoGrid from './Grid.vue'

導入一個名為 DemoGrid 的網格組件,這個組件定義在 Grid.vue 文件中。

import { ref } from 'vue'

從 Vue 中導入 ref 函數,ref 用於創建響應式的引用變數。

const searchQuery = ref('')

創建一個響應式變數 searchQuery,初始值為空字符串。這個變數將用來存儲用戶的搜索查詢。

const gridColumns = ['name', 'power']

定義一個數組 gridColumns,其中包含表格的列名,分別是 'name' 和 'power'。

const gridData = [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]

定義一個數組 gridData,其中包含要顯示在網格中的數據。每個數據項是一個物件,包含兩個屬性:'name' 和 'power'。

<template> 部分

開始模板區塊,用於定義組件的 HTML 結構。

<form id="search">
Search <input name="query" v-model="searchQuery">
</form>

創建一個帶有 ID 為 "search" 的表單,其中包含一個輸入框。輸入框的 v-model 綁定到 searchQuery,這樣當用戶輸入內容時,searchQuery 的值會自動更新。

<DemoGrid
:data="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</DemoGrid>

使用 DemoGrid 組件,並傳遞三個屬性:

  • data:傳遞 gridData 作為網格數據。
  • columns:傳遞 gridColumns 作為網格列。
  • filter-key:傳遞 searchQuery 作為過濾關鍵字。

這段代碼創建了一個簡單的應用,該應用展示了一個可重用的表格組件 DemoGrid,並使用外部數據 gridData 來填充網格。用戶可以通過搜索框輸入文字,並根據輸入的關鍵字來過濾顯示的數據。

Grid.vue

<script setup>
import { ref, computed } from 'vue'

const props = defineProps({
data: Array,
columns: Array,
filterKey: String
})

const sortKey = ref('')
const sortOrders = ref(
props.columns.reduce((o, key) => ((o[key] = 1), o), {})
)

const filteredData = computed(() => {
let { data, filterKey } = props
if (filterKey) {
filterKey = filterKey.toLowerCase()
data = data.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
const key = sortKey.value
if (key) {
const order = sortOrders.value[key]
data = data.slice().sort((a, b) => {
a = a[key]
b = b[key]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
})

function sortBy(key) {
sortKey.value = key
sortOrders.value[key] *= -1
}

function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
</script>

<template>
<table v-if="filteredData.length">
<thead>
<tr>
<th v-for="key in columns"
@click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ capitalize(key) }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredData">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
<p v-else>No matches found.</p>
</template>

<style>
table {
border: 2px solid #42b983;
border-radius: 3px;
background-color: #fff;
}

th {
background-color: #42b983;
color: rgba(255, 255, 255, 0.66);
cursor: pointer;
user-select: none;
}

td {
background-color: #f9f9f9;
}

th,
td {
min-width: 120px;
padding: 10px 20px;
}

th.active {
color: #fff;
}

th.active .arrow {
opacity: 1;
}

.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}

.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}

.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}
</style>

這段代碼展示了一個可過濾和排序的表格組件。以下是逐行解釋:

<script setup> 部分

import { ref, computed } from 'vue'

從 Vue 中導入 refcomputed 函數,用於創建響應式變數和計算屬性。

const props = defineProps({
data: Array,
columns: Array,
filterKey: String
})

使用 defineProps 定義組件的 props,包括 data (數據數組),columns (列名數組),和 filterKey (過濾關鍵字)。

const sortKey = ref('')

創建一個響應式變數 sortKey,用來存儲當前的排序鍵,初始值為空字符串。

const sortOrders = ref(
props.columns.reduce((o, key) => ((o[key] = 1), o), {})
)

創建一個響應式變數 sortOrders,用來存儲每個列的排序順序。初始化時,使用 props.columns 中的每個列名將 sortOrders 設置為 1 (升序)。

Q: 什麼是reduce方法?

reduce 方法是一個用於累加數組中所有元素的高階函數。它會遍歷數組中的每一個元素,並將其累加到一個累加器(accumulator)中。這個累加器的初始值可以通過參數設定。 reduce 方法最終返回累加器的值。它通常用於需要從數組生成單個值(如總和、乘積、平均值、或轉換為另一種資料結構)的場合。

array.reduce(callback, initialValue)
  • callback: 在每個元素上執行的函數,接收四個參數:
    • accumulator: 累加器,累積回調的返回值。
    • currentValue: 當前正在處理的數組元素。
    • currentIndex (可選): 當前正在處理的數組元素的索引。
    • array (可選): 調用 reduce 的數組。
  • initialValue: 作為第一次調用 callbackaccumulator 的值。如果未提供 initialValue,將使用數組中的第一個元素,並從第二個元素開始迭代。
  1. 計算數組元素的總和
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 15
  1. 將數組轉換為物件

假設我們有一個包含列名的數組,我們希望將其轉換為一個物件,並將每個列名的排序順序設為 1。

const columns = ['name', 'power'];
const sortOrders = columns.reduce((accumulator, key) => {
accumulator[key] = 1;
return accumulator;
}, {});
console.log(sortOrders); // { name: 1, power: 1 }

在這個範例中:

  • columns.reduce 方法遍歷 columns 數組中的每一個元素。
  • accumulator 初始為空物件 {}
  • 對於數組中的每個 key,設置 accumulator[key] = 1
  • 最終返回累加結果 accumulator,也就是 { name: 1, power: 1 }

讓我們回到您的原始代碼:

const sortOrders = ref(
props.columns.reduce((o, key) => ((o[key] = 1), o), {})
);
  1. props.columns 是一個數組,包含列名。
  2. reduce 方法遍歷 props.columns 中的每個元素(列名)。
  3. 初始值 initialValue 為空物件 {}
  4. 對於每個列名 key,執行 (o[key] = 1),將 key 對應的值設為 1
  5. 返回累加結果 o,最終 o 變成 { name: 1, power: 1 }
  6. ref 函數將生成的物件包裝成響應式的,存儲在 sortOrders 中。

這段代碼的作用是創建一個物件,其中包含列名作為鍵,並將每個鍵的值設置為初始排序順序(1 表示升序)。

const filteredData = computed(() => {
let { data, filterKey } = props
if (filterKey) {
filterKey = filterKey.toLowerCase()
data = data.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
const key = sortKey.value
if (key) {
const order = sortOrders.value[key]
data = data.slice().sort((a, b) => {
a = a[key]
b = b[key]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
})

創建一個計算屬性 filteredData,用於根據 filterKey 過濾和根據 sortKey 排序數據:

  • 如果 filterKey 不為空,則將 filterKey 轉為小寫,並過濾 data,只保留包含 filterKey 的行。
  • 如果 sortKey 不為空,則根據 sortKeysortOrders 對數據進行排序。
function sortBy(key) {
sortKey.value = key
sortOrders.value[key] *= -1
}

定義 sortBy 函數,用於根據給定的鍵進行排序。點擊某列時,設置 sortKey 為該列,並反轉 sortOrders 中該列的排序順序。

function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}

定義 capitalize 函數,用於將字符串的首字母大寫。

<template> 部分

<template>
<table v-if="filteredData.length">
<thead>
<tr>
<th v-for="key in columns"
@click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ capitalize(key) }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredData">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
<p v-else>No matches found.</p>
</template>

定義模板結構:

  • 如果 filteredData 不為空,則顯示一個表格:
    • 表頭 (<thead>):使用 v-for 渲染每個列名,並設置 @click 事件以觸發 sortBy 函數。根據當前排序鍵設置 class
    • 表身 (<tbody>):使用 v-for 渲染每行數據,並在每行中使用 v-for 渲染每個列的數據。
  • 如果 filteredData 為空,則顯示 "No matches found." 提示。

<style> 部分

<style>
table {
border: 2px solid #42b983;
border-radius: 3px;
background-color: #fff;
}

th {
background-color: #42b983;
color: rgba(255, 255, 255, 0.66);
cursor: pointer;
user-select: none;
}

td {
background-color: #f9f9f9;
}

th,
td {
min-width: 120px;
padding: 10px 20px;
}

th.active {
color: #fff;
}

th.active .arrow {
opacity: 1;
}

.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}

.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}

.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}
</style>

定義表格的樣式:

  • 設置表格邊框、背景顏色、圓角等樣式。
  • 設置表頭和表格單元格的背景顏色、最小寬度、填充、字體顏色等樣式。
  • 設置表頭在排序時的樣式,包括排序箭頭的樣式。
raw-image
這一篇原來計算屬性是用在sort跟filter的地方,還需要多練習才能好好應用!
這樣以後寫好一次就可以重複使用這個表格摟~yeah!
留言
avatar-img
留言分享你的想法!
avatar-img
卡關的人生
2會員
73內容數
分享生活趣事~
卡關的人生的其他內容
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
看更多
你可能也想看
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
在 Vue 3 中使用計算屬性來實現表格的排序和篩選功能。通過創建一個可重用的表格組件(DemoGrid),可以輕鬆管理和顯示外部數據。
Thumbnail
在 Vue 3 中使用計算屬性來實現表格的排序和篩選功能。通過創建一個可重用的表格組件(DemoGrid),可以輕鬆管理和顯示外部數據。
Thumbnail
Ex6SimpleComponent.vue 使用 ref 來創建響應式變數 groceryList,並引入 TodoItem 子組件。模板部分使用 <ol> 標籤來創建有序列表,並透過 v-for 指令遍歷 groceryList 陣列,每個項目對應一個 TodoItem 組件。
Thumbnail
Ex6SimpleComponent.vue 使用 ref 來創建響應式變數 groceryList,並引入 TodoItem 子組件。模板部分使用 <ol> 標籤來創建有序列表,並透過 v-for 指令遍歷 groceryList 陣列,每個項目對應一個 TodoItem 組件。
Thumbnail
在這個範例中,我們展示了如何使用 Vue 的 v-model 來實現各種表單元件的雙向綁定。範例包括文字輸入框、單選框、多選框、單選按鈕、下拉選單和多選下拉選單。使用 ref 創建響應式變數,並在 template 中使用 v-model 進行雙向綁定。這使得表單元件能夠即時反映和更新數據。
Thumbnail
在這個範例中,我們展示了如何使用 Vue 的 v-model 來實現各種表單元件的雙向綁定。範例包括文字輸入框、單選框、多選框、單選按鈕、下拉選單和多選下拉選單。使用 ref 創建響應式變數,並在 template 中使用 v-model 進行雙向綁定。這使得表單元件能夠即時反映和更新數據。
Thumbnail
組件組成一棵樹狀結構,類似於嵌套的 HTML 元素,但 Vue 提供了自定義內容和邏輯的封裝。通常我們會在專用的 .vue 文件中定義組件,並使用 <script setup> 來輕鬆管理狀態和事件。組件可以重複使用,並透過 props 傳遞數據,使用插槽實現內容分發。
Thumbnail
組件組成一棵樹狀結構,類似於嵌套的 HTML 元素,但 Vue 提供了自定義內容和邏輯的封裝。通常我們會在專用的 .vue 文件中定義組件,並使用 <script setup> 來輕鬆管理狀態和事件。組件可以重複使用,並透過 props 傳遞數據,使用插槽實現內容分發。
Thumbnail
使用 v-bind 可以將 HTML 屬性、樣式或類別綁定到 Vue 的數據,實現單向數據綁定。而 v-model 則是用於表單元素的雙向數據綁定,使輸入和數據同步更新。使用 v-bind 可以讓我們綁定非字符串值,例如布爾值或對象,從而在處理更複雜的應用場景時更為靈活。
Thumbnail
使用 v-bind 可以將 HTML 屬性、樣式或類別綁定到 Vue 的數據,實現單向數據綁定。而 v-model 則是用於表單元素的雙向數據綁定,使輸入和數據同步更新。使用 v-bind 可以讓我們綁定非字符串值,例如布爾值或對象,從而在處理更複雜的應用場景時更為靈活。
Thumbnail
本文介紹了 Vue 的模板語法及虛擬 DOM 概念。學習如何使用 Vue 進行聲明式渲染,以及模板如何轉換為高效的 JavaScript 代碼。探討各種綁定方法、指令及安全注意事項,幫助開發者構建高效動態用戶界面並提升應用性能。文章深入淺出,適合想進一步瞭解 Vue 的開發者。
Thumbnail
本文介紹了 Vue 的模板語法及虛擬 DOM 概念。學習如何使用 Vue 進行聲明式渲染,以及模板如何轉換為高效的 JavaScript 代碼。探討各種綁定方法、指令及安全注意事項,幫助開發者構建高效動態用戶界面並提升應用性能。文章深入淺出,適合想進一步瞭解 Vue 的開發者。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
在 Vue 專案中使用 Apollo Graphql Client 從 API 獲取資料,由於資料結構較為複雜,筆者便跟著網路教學使用 codegen 工具自動化產生 TypeScript 型別定義。在某個元件中,需要使用 defineProps 來撰寫型別定義,結果⋯⋯
Thumbnail
在 Vue 專案中使用 Apollo Graphql Client 從 API 獲取資料,由於資料結構較為複雜,筆者便跟著網路教學使用 codegen 工具自動化產生 TypeScript 型別定義。在某個元件中,需要使用 defineProps 來撰寫型別定義,結果⋯⋯
Thumbnail
Vue3 學習筆記,專案建立與基礎響應式篇
Thumbnail
Vue3 學習筆記,專案建立與基礎響應式篇
Thumbnail
S1.建立資料庫 user S2.建立 index.htm a.載入 vue.js、jquery、bootstrap b.版面建立 S3.vue.js程式 新增、查看列表、互動視窗、修改、刪除 index.htm VueControl.js 原碼:https://reurl.cc/e3k8yL
Thumbnail
S1.建立資料庫 user S2.建立 index.htm a.載入 vue.js、jquery、bootstrap b.版面建立 S3.vue.js程式 新增、查看列表、互動視窗、修改、刪除 index.htm VueControl.js 原碼:https://reurl.cc/e3k8yL
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News