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

Modyfikatory v-model - dostrajanie sygnału z paneli

W poprzedniej lekcji podpiąłeś panel misji do zmiennych: nazwa załogi, notatki, checkboxy systemów, lista modułów. Formularz działa, ale sygnał, który z niego wychodzi, jest surowy. Dyżurny Centrum Kontroli zgłasza trzy usterki. Po pierwsze, dziennik telemetrii zapisuje nową wartość po każdym naciśnięciu klawisza, więc jedno słowo zostawia w nim kilkanaście wpisów. Po drugie, pole z liczbą kredytów oddaje tekst, a nie liczbę, więc każde dodawanie skleja cyfry zamiast je sumować. Po trzecie, operator wkleja nazwę modułu ze schowka razem ze spacją na końcu i porównanie z katalogiem stacji przestaje trafiać.

Żadnej z tych usterek nie musisz naprawiać ręcznie, @name. Tak jak do zdarzeń dopisywałeś

.prevent
czy
.stop
, tak do
v-model
możesz dopisać modyfikator: kropkę i nazwę tuż za nazwą dyrektywy. Modyfikator nie zmienia tego, z czym pole jest związane - zmienia sposób, w jaki wartość trafia do zmiennej. Vue udostępnia trzy:
.lazy
,
.number
i
.trim
. Każdy z nich naprawia dokładnie jedną z usterek dyżurnego.

Kiedy wartość ląduje w zmiennej

Zacznijmy od pierwszej usterki, bo dotyczy czasu, a nie treści. Pole tekstowe z

v-model
nasłuchuje przeglądarkowego zdarzenia
input
, które przeglądarka wysyła po każdym pojedynczym znaku. Dla pola z podglądem na żywo to idealne zachowanie - operator pisze, panel od razu pokazuje efekt. Dla dziennika telemetrii albo kosztownego przeliczenia trajektorii to marnotrawstwo: interesuje nas jedna, gotowa wartość, a nie kilkanaście stanów pośrednich.

Przeglądarka ma osobne zdarzenie na taką okazję:

change
. W polu tekstowym wysyła je dopiero wtedy, gdy zmieniona wartość zostaje zatwierdzona - czyli gdy opuścisz pole (zdarzenie
blur
) albo naciśniesz Enter. Modyfikator
.lazy
robi tylko jedną rzecz: przełącza nasłuch
v-model
ze zdarzenia
input
na
change
. Poniżej dwa identycznie zbudowane kanały odczytu, różniące się wyłącznie tym modyfikatorem.

1<template>
2  <label>Live (input)</label>
3  <input v-model="liveSignal" />
4  <p>Odczyt: {{ liveSignal }}</p>
5
6  <label>Lazy (change)</label>
7  <input v-model.lazy="committedSignal" />
8  <p>Odczyt: {{ committedSignal }}</p>
9</template>
10
11<script setup>
12import { ref } from 'vue'
13
14const liveSignal = ref('')
15const committedSignal = ref('')
16</script>

Wpisz coś w pierwsze pole - akapit pod nim zmienia się litera po literze. Wpisz to samo w drugie - akapit stoi pusty, dopóki nie klikniesz obok albo nie naciśniesz Enter. I teraz najważniejsze: co się nie zmieniło. Wiązanie nadal jest dwukierunkowe, zmienna

committedSignal
nadal jest zwykłym tekstem, a zapis w szablonie różni się o pięć znaków. Przesunął się wyłącznie moment aktualizacji.

Warto od razu wykluczyć trzy nieporozumienia, bo

.lazy
bywa mylony z rzeczami, którymi nie jest. To nie jest odliczanie czasu - nic nie dzieje się co 500 milisekund. Opóźnienie liczone zegarem to debounce i w Vue robisz je samodzielnie, watcherem z
setTimeout
, tak jak w module o systemach monitorowania. To nie jest też czekanie na wysłanie formularza - wartość ląduje w zmiennej, gdy tylko opuścisz pole, na długo przed kliknięciem przycisku. I na koniec: aktualizacja wciąż jest automatyczna, niczego nie musisz przepisywać ręcznie.

Liczba, która udaje tekst

Druga usterka jest podstępniejsza, bo panel wygląda poprawnie do chwili pierwszego działania arytmetycznego. Pole formularza w przeglądarce zawsze oddaje tekst - także wtedy, gdy wpisujesz w nie same cyfry. Jeśli więc przypiszesz je do zmiennej i dodasz liczbę, JavaScript nie policzy sumy, tylko sklei dwa napisy. Zobacz to na polu z kredytami misji, na razie bez żadnego modyfikatora.

1<template>
2  <label>Kredyty</label>
3  <input v-model="budget" />
4
5  <p>Typ: {{ typeof budget }}</p>
6  <p>budget + 10 = {{ budget + 10 }}</p>
7</template>
8
9<script setup>
10import { ref } from 'vue'
11
12const budget = ref('')
13</script>

Wpisz 1500. Pierwszy akapit pokazuje

string
, a drugi - zamiast 1510 - napis złożony z wpisanej wartości i doklejonej dziesiątki. To nie jest błąd Vue ani błąd przeglądarki, tylko naturalna konsekwencja tego, że pole formularza przechowuje znaki. Dopóki wartość służy wyłącznie do wyświetlenia, nikt tego nie zauważy. Kłopot zaczyna się przy sumowaniu budżetów, porównywaniu limitów i sortowaniu modułów po koszcie.

Lekarstwem jest modyfikator

.number
. Vue przepuszcza wtedy wpisaną wartość przez konwersję na liczbę - działa to tak jak
parseFloat
: z tekstu wyciągana jest liczba, a jeśli konwersja się nie uda, do zmiennej trafia niezmieniony oryginał. To bardzo ważne rozróżnienie, @name:
.number
konwertuje, ale nie waliduje. Nie odrzuci wpisu, nie zapali czerwonej lampki i nie zablokuje formularza - jeśli treści nie da się zamienić na liczbę, po prostu zostawi tekst takim, jakim był. Sprawdzanie poprawności to osobna praca, którą wykonasz w następnej lekcji.

1<template>
2  <label>Kredyty</label>
3  <input v-model.number="budget" type="number" />
4
5  <p>Typ: {{ typeof budget }}</p>
6  <p>budget + 10 = {{ budget + 10 }}</p>
7</template>
8
9<script setup>
10import { ref } from 'vue'
11
12const budget = ref(0)
13</script>

Ten sam szablon, jeden dopisany modyfikator - i pierwszy akapit pokazuje teraz

number
, a drugi prawdziwą sumę. Zwróć uwagę, czego
.number
nie robi. Nie pilnuje, żeby dało się wpisać wyłącznie cyfry - to zadanie atrybutu
type="number"
, który należy do przeglądarki i to on decyduje, co w ogóle wolno wystukać w polu. Nie formatuje też liczby do postaci z separatorami tysięcy; taki zapis dla wygody czytającego robi się osobno, przy wyświetlaniu, metodą
toLocaleString
. W samym polu wartość pozostaje surową liczbą.

Skoro atrybut

type="number"
już się pojawił, jedno uczciwe zastrzeżenie. Vue traktuje takie pole jako numeryczne z założenia i próbuje konwersji nawet bez modyfikatora, więc w tym konkretnym przykładzie
.number
niczego nie zmieni. Mimo to zalecam pisać go zawsze i wprost. Po pierwsze, intencja jest wtedy widoczna w szablonie i nie zależy od tego, jaki
type
ktoś kiedyś podmieni. Po drugie, ten sam zapis zadziała także na zwykłym polu tekstowym. Kanoniczna postać, którą warto mieć w palcach, wygląda dokładnie tak jak w bloku powyżej: najpierw
<input
, potem
v-model.number=
z nazwą zmiennej w cudzysłowie, a na końcu
type="number"
.

Spacje, których nikt nie wpisał

Trzecia usterka bierze się z ludzkiego odruchu. Operator kopiuje nazwę modułu z raportu i wraz z nią zabiera spację na końcu. Na ekranie nie widać niczego niepokojącego, ale w zmiennej siedzi znak, który psuje wszystko, co porównuje teksty: wyszukiwanie w katalogu stacji nie znajduje modułu, a naiwne sprawdzenie "czy pole jest puste" przepuszcza wpis złożony z samych spacji. Modyfikator

.trim
usuwa białe znaki z początku i z końca wartości, zanim ta trafi do zmiennej.

1<template>
2  <label>Raw</label>
3  <input v-model="rawName" />
4  <p>chars: {{ rawName.length }}</p>
5
6  <label>Trimmed</label>
7  <input v-model.trim="missionName" />
8  <p>chars: {{ missionName.length }}</p>
9</template>
10
11<script setup>
12import { ref } from 'vue'
13
14const rawName = ref('')
15const missionName = ref('')
16</script>

Wpisz w oba pola nazwę ze spacją na początku i na końcu. Licznik znaków pod pierwszym polem policzy spacje, licznik pod drugim - nie. Teraz trzy granice, o których łatwo zapomnieć.

.trim
nie skraca tekstu do zadanej długości - nie ma żadnego limitu znaków, przycinane są wyłącznie krawędzie. Nie usuwa też wszystkich spacji: nazwa "Oxygen Alpha" zachowa swoją spację w środku, bo modyfikator nie rusza wnętrza napisu. I nie zmienia wielkości liter - zamiana na małe litery to
toLowerCase
, zupełnie inne narzędzie. Co równie ważne, modyfikator pracuje na wartości wędrującej do zmiennej, a nie na tym, co fizycznie stukasz w pole - w trakcie pisania spacja jest w polu widoczna, tyle że zmienna jej nie dostaje.

Łączenie modyfikatorów

Panel rejestracji misji potrzebuje wszystkich trzech poprawek naraz i nie musisz wybierać jednej. Modyfikatory łączy się w łańcuch: kolejne nazwy dopisujesz jeden za drugim, każdą poprzedzoną kropką, bez spacji i bez przecinków. Zapis

v-model.lazy.trim="text"
jest w pełni poprawny - a przecinek między nazwami byłby dla Vue częścią jednej, nieznanej nazwy modyfikatora i po prostu nie zadziałałby.

Vue nie wymusza kolejności, więc

v-model.trim.lazy
znaczy dokładnie to samo co
v-model.lazy.trim
. Skoro wybór należy do Ciebie, polecam trzymać stałą konwencję: najpierw modyfikator opisujący, czym jest wartość (
.number
albo
.trim
), potem ten opisujący, kiedy trafia do zmiennej (
.lazy
). Stąd
v-model.number.lazy
- czyta się to jak zdanie: liczba, zatwierdzana po opuszczeniu pola.

1<template>
2  <form @submit.prevent="registerMission">
3    <label>Nazwa misji</label>
4    <input v-model.trim.lazy="missionName" />
5
6    <label>Kredyty</label>
7    <input v-model.number.lazy="budget" type="number" />
8
9    <button type="submit">Zarejestruj</button>
10  </form>
11
12  <p>Panel: {{ missionName }} / {{ budget }}</p>
13</template>
14
15<script setup>
16import { ref } from 'vue'
17
18const missionName = ref('')
19const budget = ref(0)
20
21function registerMission() {
22  console.log('Mission registered', missionName.value, budget.value)
23}
24</script>

Wypełnij oba pola i przejdź między nimi tabulatorem, nie dotykając przycisku. Akapit pod formularzem pokazuje już przyciętą nazwę i liczbowy budżet - dowód, że

.lazy
czeka na opuszczenie pola, a nie na wysłanie formularza. Kliknięcie przycisku najpierw zabiera fokus z pola, więc do funkcji
registerMission
zawsze trafiają wartości aktualne. Poza tym w tym bloku nie ma nic nowego:
@submit.prevent
znasz z lekcji o modyfikatorach zdarzeń, a
ref
i
v-model
towarzyszą Ci od początku modułu. Doszły tylko kropki z modyfikatorami.

Kiedy modyfikator przeszkadza

Na koniec przestroga, bo modyfikatory bywają dodawane odruchowo. Nie dawaj

.lazy
polu, które filtruje listę modułów na żywo - operator straci podgląd wyników w trakcie pisania i uzna panel za zepsuty. Uważaj z
.number
przy oznaczeniach, które tylko wyglądają jak liczby: numer sektora zapisany jako 007 po konwersji zostanie siódemką i przestanie pasować do katalogu. Z
.trim
przy nazwach, adresach i identyfikatorach kłopotów praktycznie nie ma i śmiało możesz go stosować domyślnie.

Pamiętaj: modyfikator

v-model
to pokrętło kalibracji przy panelu - sygnał płynie tym samym kablem, ale do komputera misji dociera już czysty, we właściwym typie i we właściwej chwili.

Przejdź do CodeWorlds