Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Template Refs i Provide/Inject z typami

W zaawansowanych systemach stacji NOVA LAB musimy czasem bezpośrednio odwoływać się do elementow DOM lub przekazywać dane między komponentami bez props drilling. TypeScript pomaga nam robić to bezpiecznie.

Template Refs z typami

Template ref pozwala uzyskać referencję do elementu DOM:

1<script setup lang="ts">
2import { ref, onMounted } from 'vue'
3
4// Typ elementu HTML — ref może być null przed montowaniem
5const inputRef = ref<HTMLInputElement | null>(null)
6const canvasRef = ref<HTMLCanvasElement | null>(null)
7
8onMounted(() => {
9  // Po montowaniu ref nie jest już null
10  inputRef.value?.focus()
11
12  const ctx = canvasRef.value?.getContext('2d')
13  if (ctx) {
14    ctx.fillStyle = '#00ff88'
15    ctx.fillRect(0, 0, 100, 100)
16  }
17})
18</script>
19
20<template>
21  <input ref="inputRef" placeholder="Wpisz komendę..." />
22  <canvas ref="canvasRef" width="400" height="300" />
23</template>

Typ

HTMLInputElement | null
mówi TypeScriptowi, że ref może być
null
(zanim komponent zostanie zamontowany).

Referencje do komponentów

Można też uzyskać referencję do komponentu dziecka:

1<script setup lang="ts">
2import { ref } from 'vue'
3import SensorPanel from './SensorPanel.vue'
4
5// InstanceType<typeof Component> daje typ instancji komponentu
6const sensorRef = ref<InstanceType<typeof SensorPanel> | null>(null)
7
8function refreshSensors() {
9  // Dostęp do publicznych metod komponentu
10  sensorRef.value?.refresh()
11}
12</script>
13
14<template>
15  <SensorPanel ref="sensorRef" />
16  <button @click="refreshSensors">Odśwież sensory</button>
17</template>

InjectionKey i Provide/Inject

provide/inject
pozwala przekazywać dane w dół drzewa komponentów.
InjectionKey<T>
zapewnia bezpieczeństwo typów:

1<!-- types.ts -->
2<script lang="ts">
3import type { InjectionKey, Ref } from 'vue'
4
5export interface StationConfig {
6  name: string
7  maxCrew: number
8  sector: string
9}
10
11// Klucz z typem — zapewnia że provide i inject używają tego samego typu
12export const StationConfigKey: InjectionKey<StationConfig> = Symbol('StationConfig')
13export const ThemeKey: InjectionKey<Ref<'dark' | 'light'>> = Symbol('Theme')
14</script>
1<!-- Provider (rodzic) -->
2<script setup lang="ts">
3import { provide, ref } from 'vue'
4import { StationConfigKey, ThemeKey } from './types'
5
6const theme = ref<'dark' | 'light'>('dark')
7
8provide(StationConfigKey, {
9  name: 'NOVA LAB',
10  maxCrew: 100,
11  sector: 'Alpha-7'
12})
13
14provide(ThemeKey, theme)
15</script>
1<!-- Consumer (dziecko/wnuk) -->
2<script setup lang="ts">
3import { inject } from 'vue'
4import { StationConfigKey, ThemeKey } from './types'
5
6// TypeScript zna typ! StationConfig | undefined
7const config = inject(StationConfigKey)
8
9// Z wartością domyślną — nie jest undefined
10const theme = inject(ThemeKey, ref('dark'))
11
12// Używamy optional chaining bo config może być undefined
13console.log(config?.name)    // 'NOVA LAB'
14console.log(theme.value)     // 'dark'
15</script>

Wzorzec z wartością domyślną

Aby uniknąć

undefined
, można użyć wartości domyślnej:

1const config = inject(StationConfigKey, {
2  name: 'Unknown',
3  maxCrew: 0,
4  sector: 'N/A'
5})
6// Teraz config jest zawsze StationConfig (nigdy undefined)

Można też użyć funkcji fabrykującej:

1const config = inject(StationConfigKey, () => ({
2  name: 'Default',
3  maxCrew: 50,
4  sector: 'Beta'
5}), true)  // trzeci arg = true oznacza że to factory function
Przejdź do CodeWorlds