Kurs Vue.js · Moduł 5: Zdarzenia i formularze

Projekt: System Kontroli Misji

2 min czytania
W tej lekcji2

Finalny projekt Symulatora Sterowania! Zbuduj kompletny system formularzy i zdarzeń do kontroli misji kosmicznej na stacji NOVA LAB.

Podsumowanie modułu

W tym module poznałeś kluczowe mechanizmy interakcji w Vue.js, które są fundamentem każdej aplikacji webowej:

  • Obsługa zdarzeń - dyrektywy @click, @input, @submit, @keydown i inne zdarzenia DOM, które pozwalają reagować na akcje użytkownika w czasie rzeczywistym
  • Modyfikatory zdarzeń - .prevent (blokowanie domyślnej akcji), .stop (zatrzymanie propagacji), .once (jednorazowe wywołanie), .self (tylko na docelowym elemencie) - precyzyjne kontrolowanie zachowania zdarzeń
  • v-model - dwukierunkowe wiązanie danych w formularzach, które automatycznie synchronizuje dane między szablonem a stanem komponentu
  • Modyfikatory v-model - .lazy (aktualizacja po utracie focusu), .number (automatyczna konwersja na liczbę), .trim (usuwanie białych znaków) - dostosowywanie zachowania wiązania
  • Zaawansowane formularze - checkboxy, radio buttony, selecty, textarea i inne kontrolki, które łączą się z reaktywnymi danymi Vue

Każdy z tych mechanizmów odpowiada systemowi sterowania na stacji NOVA LAB - zdarzenia to przyciski na panelu kontrolnym, v-model to dwukierunkowa komunikacja z czujnikami, a modyfikatory to filtry sygnałów zapewniające precyzję.

Wymagania projektu

Zbuduj Panel Rejestracji Misji - kompletny interfejs do planowania ekspedycji marsjańskich, który zawiera:

1. Formularz danych misji

Stwórz rozbudowany formularz z polami: nazwa misji (input text), cel podróży (select z opcjami: Olympus Mons, Valles Marineris, Polar Cap, Jezero Crater), priorytet (radio: niski/średni/wysoki/krytyczny), szacowany czas trwania (input number w dniach), opis misji (textarea), oraz zgoda na regulamin bezpieczeństwa (checkbox). Każde pole powinno być powiązane z reaktywnym stanem przez v-model.

2. Modyfikatory v-model

Użyj .trim na polach tekstowych (nazwa misji, opis), aby automatycznie usuwać zbędne spacje. Zastosuj .number na polu czasu trwania, aby wartość była zawsze liczbą. Użyj .lazy na polu opisu misji, aby aktualizacja następowała dopiero po opuszczeniu pola - to zmniejsza liczbę przerenderowań.

3. Walidacja formularza

Zaimplementuj walidację w czasie rzeczywistym: nazwa misji minimum 3 znaki, opis minimum 20 znaków, czas trwania między 1 a 365 dni, wymagana zgoda na regulamin. Wyświetlaj komunikaty błędów pod każdym polem. Przycisk wysyłania powinien być zablokowany (disabled) gdy formularz jest niepoprawny.

4. Obsługa zdarzeń i podsumowanie

Dodaj @submit.prevent na formularzu, obsługę klawisza Escape (@keydown.escape) do czyszczenia formularza z potwierdzeniem (confirm()), oraz wyświetlanie podsumowania zarejestrowanej misji po wysłaniu formularza. Dodaj także historię zarejestrowanych misji wyświetlaną pod formularzem.

Rozbuduj starter poniżej!

Kod do tej lekcji: App.vue
1<script setup>
2import { ref, reactive } from 'vue'
3
4// System Kontroli Misji - Projekt koncowy modulu 5
5const missionForm = reactive({
6  name: '',
7  destination: 'mars',
8  priority: 'normal',
9  description: '',
10  crewSize: 4,
11  acceptTerms: false
12})
13
14const errors = ref({})
15const submitted = ref(false)
16const missions = ref([])
17
18function validate() {
19  const e = {}
20  if (!missionForm.name.trim()) e.name = 'Nazwa misji jest wymagana'
21  if (!missionForm.description.trim()) e.description = 'Opis jest wymagany'
22  if (missionForm.crewSize < 1 || missionForm.crewSize > 20) e.crewSize = 'Zaloga: 1-20 osob'
23  if (!missionForm.acceptTerms) e.acceptTerms = 'Musisz zaakceptowac regulamin'
24  errors.value = e
25  return Object.keys(e).length === 0
26}
27
28function submitMission() {
29  if (!validate()) return
30  missions.value.push({ ...missionForm, id: Date.now() })
31  resetForm()
32  submitted.value = true
33  setTimeout(() => { submitted.value = false }, 3000)
34}
35
36function resetForm() {
37  missionForm.name = ''
38  missionForm.destination = 'mars'
39  missionForm.priority = 'normal'
40  missionForm.description = ''
41  missionForm.crewSize = 4
42  missionForm.acceptTerms = false
43  errors.value = {}
44}
45</script>
46
47<template>
48  <div class="mission-control">
49    <h1>Rejestracja Misji NOVA LAB</h1>
50
51    <div v-if="submitted" class="success">Misja zarejestrowana pomyslnie!</div>
52
53    <form @submit.prevent="submitMission" @keyup.escape="resetForm">
54      <div class="field">
55        <label>Nazwa misji</label>
56        <input v-model.trim="missionForm.name" placeholder="Wprowadz nazwe..." />
57        <span class="error" v-if="errors.name">{{ errors.name }}</span>
58      </div>
59
60      <div class="field">
61        <label>Cel podrozy</label>
62        <select v-model="missionForm.destination">
63          <option value="mars">Mars</option>
64          <option value="europa">Europa (Jowisz)</option>
65          <option value="titan">Tytan (Saturn)</option>
66          <option value="asteroid">Pas Asteroidow</option>
67        </select>
68      </div>
69
70      <div class="field">
71        <label>Priorytet</label>
72        <div class="radio-group">
73          <label><input type="radio" v-model="missionForm.priority" value="low" /> Niski</label>
74          <label><input type="radio" v-model="missionForm.priority" value="normal" /> Normalny</label>
75          <label><input type="radio" v-model="missionForm.priority" value="high" /> Wysoki</label>
76        </div>
77      </div>
78
79      <div class="field">
80        <label>Wielkosc zalogi</label>
81        <input type="number" v-model.number="missionForm.crewSize" min="1" max="20" />
82        <span class="error" v-if="errors.crewSize">{{ errors.crewSize }}</span>
83      </div>
84
85      <div class="field">
86        <label>Opis misji</label>
87        <textarea v-model.lazy="missionForm.description" rows="3" placeholder="Opisz cel misji..."></textarea>
88        <span class="error" v-if="errors.description">{{ errors.description }}</span>
89      </div>
90
91      <div class="field">
92        <label><input type="checkbox" v-model="missionForm.acceptTerms" /> Akceptuje regulamin NOVA LAB</label>
93        <span class="error" v-if="errors.acceptTerms">{{ errors.acceptTerms }}</span>
94      </div>
95
96      <div class="actions">
97        <button type="submit">Zarejestruj misje</button>
98        <button type="button" @click="resetForm">Wyczysc (Escape)</button>
99      </div>
100    </form>
101  </div>
102</template>
103
104<style scoped>
105.mission-control { background: #0a0a2e; color: #e0e0ff; padding: 2rem; min-height: 100vh; font-family: monospace; }
106h1 { color: #00d4ff; text-align: center; }
107.success { background: #1a4a1a; color: #00ff88; padding: 1rem; border-radius: 4px; text-align: center; margin: 1rem 0; }
108.field { margin: 1rem 0; }
109label { display: block; margin-bottom: 0.3rem; color: #aaa; }
110input, select, textarea { width: 100%; background: #1a1a4e; border: 1px solid #333; color: white; padding: 0.5rem; border-radius: 4px; box-sizing: border-box; }
111.radio-group { display: flex; gap: 1rem; }
112.radio-group label { display: flex; align-items: center; gap: 0.3rem; }
113.error { color: #ff4444; font-size: 0.8rem; }
114.actions { display: flex; gap: 1rem; margin-top: 1rem; }
115button { background: #00d4ff; color: #0a0a2e; border: none; padding: 0.7rem 1.5rem; cursor: pointer; border-radius: 4px; font-weight: bold; }
116button[type="button"] { background: #333; color: white; }
117</style>

Widzisz błąd w tej lekcji?

Przydatne artykuły