Kurs Vue.js · Moduł 11: Pinia
Getters w Pinia
W tej lekcji4
Getters to wartości wyliczane na podstawie state - jak panele analityczne NOVA LAB, które przetwarzają surowe dane z czujników i prezentują je w czytelnej formie. Zmieniają się automatycznie gdy zmienia się state.
Podstawowe getters
W Composition API getters to zwykłe computed():
1import { defineStore } from 'pinia'
2import { ref, computed } from 'vue'
3
4export const useInventoryStore = defineStore('inventory', () => {
5 const items = ref([
6 { name: 'Oxygen Tank', quantity: 12, category: 'life-support' },
7 { name: 'Water Filter', quantity: 5, category: 'life-support' },
8 { name: 'Solar Panel', quantity: 8, category: 'energy' },
9 { name: 'Drill Bit', quantity: 20, category: 'mining' }
10 ])
11
12 // Prosty getter
13 const totalItems = computed(() =>
14 items.value.reduce((sum, item) => sum + item.quantity, 0)
15 )
16
17 // Getter filtrujący
18 const lifeSupportItems = computed(() =>
19 items.value.filter(item => item.category === 'life-support')
20 )
21
22 // Getter sprawdzający warunek
23 const hasShortage = computed(() =>
24 items.value.some(item => item.quantity < 3)
25 )
26
27 return { items, totalItems, lifeSupportItems, hasShortage }
28})Getters używające innych getters
Getters mogą bazować na innych getters:
1const lowStockItems = computed(() =>
2 items.value.filter(item => item.quantity < 5)
3)
4
5const lowStockCount = computed(() => lowStockItems.value.length)
6
7const stockStatus = computed(() => {
8 if (lowStockCount.value === 0) return 'OK'
9 if (lowStockCount.value <= 2) return 'WARNING'
10 return 'CRITICAL'
11})Getters z parametrami
Computed nie przyjmuje argumentów, ale możesz zwrócić funkcję:
1// Getter z parametrem - zwraca funkcję
2const getItemsByCategory = computed(() => {
3 return (category) =>
4 items.value.filter(item => item.category === category)
5})
6
7const findItem = computed(() => {
8 return (name) =>
9 items.value.find(item =>
10 item.name.toLowerCase().includes(name.toLowerCase())
11 )
12})Użycie w komponencie:
1<template>
2 <div>
3 <p>Total: {{ store.totalItems }}</p>
4 <p>Status: {{ store.stockStatus }}</p>
5
6 <!-- Getter z parametrem -->
7 <div v-for="item in store.getItemsByCategory('energy')" :key="item.name">
8 {{ item.name }}: {{ item.quantity }}
9 </div>
10 </div>
11</template>Getters w storeToRefs
Getters (computed) są częścią storeToRefs:
1<script setup>
2import { storeToRefs } from 'pinia'
3import { useInventoryStore } from '@/stores/inventory'
4
5const store = useInventoryStore()
6const { items, totalItems, hasShortage } = storeToRefs(store)
7</script>Kod do tej lekcji: App.vue
1<script setup>
2import { ref, computed } from 'vue'
3
4// Symulacja store magazynu z getterami
5const items = ref([
6 { name: 'Butla z tlenem', quantity: 12, category: 'life-support', unit: 'szt.' },
7 { name: 'Filtr wody', quantity: 2, category: 'life-support', unit: 'szt.' },
8 { name: 'Panel słoneczny', quantity: 8, category: 'energy', unit: 'szt.' },
9 { name: 'Akumulator', quantity: 15, category: 'energy', unit: 'szt.' },
10 { name: 'Wiertło', quantity: 20, category: 'mining', unit: 'szt.' },
11 { name: 'Pojemnik na próbki skał', quantity: 1, category: 'mining', unit: 'szt.' }
12])
13
14const searchQuery = ref('')
15const selectedCategory = ref('all')
16
17// Getters
18const totalItems = computed(() =>
19 items.value.reduce((sum, item) => sum + item.quantity, 0)
20)
21
22const categories = computed(() =>
23 ['all', ...new Set(items.value.map(i => i.category))]
24)
25
26const filteredItems = computed(() => {
27 let result = items.value
28 if (selectedCategory.value !== 'all') {
29 result = result.filter(i => i.category === selectedCategory.value)
30 }
31 if (searchQuery.value) {
32 const q = searchQuery.value.toLowerCase()
33 result = result.filter(i => i.name.toLowerCase().includes(q))
34 }
35 return result
36})
37
38const lowStockItems = computed(() =>
39 items.value.filter(item => item.quantity < 5)
40)
41
42const stockStatus = computed(() => {
43 if (lowStockItems.value.length === 0) return 'OK'
44 if (lowStockItems.value.length <= 2) return 'WARNING'
45 return 'CRITICAL'
46})
47
48// Getter z parametrem
49const getByCategory = computed(() => {
50 return (cat) => items.value.filter(i => i.category === cat)
51})
52</script>
53
54<template>
55 <div class="nova-lab">
56 <h1>NOVA LAB - gettery magazynu</h1>
57
58 <div class="stats-bar">
59 <div class="stat-card">
60 <div class="stat-value">{{ totalItems }}</div>
61 <div class="stat-label">Sztuk łącznie</div>
62 </div>
63 <div class="stat-card">
64 <div class="stat-value" :class="stockStatus.toLowerCase()">{{ stockStatus }}</div>
65 <div class="stat-label">Stan magazynu</div>
66 </div>
67 <div class="stat-card">
68 <div class="stat-value warning">{{ lowStockItems.length }}</div>
69 <div class="stat-label">Niski stan</div>
70 </div>
71 </div>
72
73 <div class="controls">
74 <input v-model="searchQuery" placeholder="Szukaj..." class="search-input" />
75 <select v-model="selectedCategory" class="select">
76 <option v-for="cat in categories" :key="cat" :value="cat">
77 {{ cat === 'all' ? 'Wszystkie kategorie' : cat }}
78 </option>
79 </select>
80 </div>
81
82 <div class="inventory-grid">
83 <div v-for="item in filteredItems" :key="item.name"
84 class="item-card" :class="{ 'low-stock': item.quantity < 5 }">
85 <div class="item-name">{{ item.name }}</div>
86 <div class="item-qty">{{ item.quantity }} {{ item.unit }}</div>
87 <div class="item-cat">{{ item.category }}</div>
88 </div>
89 </div>
90
91 <div v-if="lowStockItems.length" class="alert-box">
92 Niski stan: {{ lowStockItems.map(i => i.name).join(', ') }}
93 </div>
94 </div>
95</template>
96
97<style scoped>
98.nova-lab {
99 background: #0a0e27;
100 color: #00ff88;
101 min-height: 100vh;
102 padding: 20px;
103 font-family: monospace;
104}
105h1 { color: #00ff88; margin-bottom: 20px; text-align: center; }
106.stats-bar { display: flex; gap: 10px; margin-bottom: 15px; justify-content: center; }
107.stat-card {
108 background: rgba(0, 180, 216, 0.1);
109 border: 2px solid #00b4d8;
110 padding: 12px 20px;
111 text-align: center;
112}
113.stat-value { font-size: 1.4em; font-weight: bold; }
114.stat-label { color: #00b4d8; font-size: 0.8em; margin-top: 4px; }
115.ok { color: #00ff88; }
116.warning { color: #ff9900; }
117.critical { color: #ff4444; }
118.controls { display: flex; gap: 10px; margin-bottom: 15px; max-width: 500px; margin: 0 auto 15px; }
119.search-input, .select {
120 background: #0a0e27;
121 border: 2px solid #00b4d8;
122 color: #00ff88;
123 padding: 8px;
124 font-family: monospace;
125 flex: 1;
126}
127.inventory-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
128.item-card {
129 background: rgba(0, 255, 136, 0.05);
130 border: 2px solid #00ff88;
131 padding: 12px;
132}
133.item-card.low-stock { border-color: #ff4444; background: rgba(255, 68, 68, 0.1); }
134.item-name { font-weight: bold; margin-bottom: 5px; }
135.item-qty { color: #00b4d8; }
136.item-cat { color: #666; font-size: 0.85em; margin-top: 4px; }
137.alert-box {
138 margin-top: 15px;
139 padding: 10px;
140 background: rgba(255, 68, 68, 0.1);
141 border: 2px solid #ff4444;
142 color: #ff4444;
143}
144</style>Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Czym są getters w Pinia (Composition API)?
2. Jak zdefiniować getter w Composition API style?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Edytor kodu
Stwórz gettery: totalItems, hasShortage i stockStatus.
- Układanie w poziomie
Ułóż definicję gettera computed w store:
- Klikanie w kolejności
Ułóż getter z parametrem (zwracający funkcję):
- Edytor kodu
Zdefiniuj lifeSupportItems (filtrujący) i getItemsByCategory (parametryczny getter).