Lekce 5 - Computed properties a dynamické styly ve Vue
V předchozí lekci, Podmínky a cykly ve Vue, jsme se naučili zobrazovat HTML elementy
nebo Vue komponenty podmíněně a v cyklech. Seznámili jsme se s direktivami
v-for
, v-if
, v-else
a
v-else-if
.
Dnes si ukážeme specifické použití direktivy v-bind pro atributy class a style, computed properties, princip reaktivity
Computed properties
Vue komponenty ve většině případů obsahují data. Některá z nich jsou
definována přímo v kódu dané komponenty (vzpomeňme na vlastnost
data
konfiguračního objektu), jiná jsou komponentě předávána
z rodičovské komponenty prostřednictvím props.
Mezi těmito dvěma datovými zdroji je jeden zásadní rozdíl. Zatímco
data
mohou své hodnoty různě měnit (nejčastěji k tomu
dochází v metodách konfiguračního objektu, o nichž bude řeč později),
hodnoty props jsou uvnitř komponenty neměnné. Pokud
bychom se kdekoli v kódu pokusili o změnu hodnoty některé z props,
Vue zobrazí na konzoli chybové hlášení a kód nebude fungovat.
Potřebujeme-li některou hodnotu z props měnit, respektive ji
použít k dalším výpočtům, oceníme jistě další nástroj, který nám
framework Vue nabízí - computed properties. Jde o další
vlastnost konfiguračního objektu s názvem computed
. Jejím
obsahem musí být objekt obsahující pouze metody. Každá
tato metoda musí povinně vracet hodnotu a může pracovat s
daty nebo props dané komponenty. K nim přistupuje pomocí
this
, které v tomto případě referuje samotnou komponentu.
Zápis this.something
tedy odkazuje na datovou hodnotu dané
komponenty s názvem something
, ať už byla definována v
props, v datech, nebo v computed properties.
Z toho logicky vyplývá další pravidlo. Názvy jednotlivých proměnných musí být unikátní v celé komponentě, název, který byl použit například v props, již nejde podruhé použít v datech ani v computed properties apod.
Ukažme si fungování computed properties na jednoduchém příkladu:
<template> <div>{{ fullName }}</div> </template> <script> export default { name: 'FullName', props: { firstName: { required: true, type: String }, lastName: { required: true, type: String } }, computed: { fullName () { return this.firstName + ' ' + this.lastName } } } </script>
Tato komponenta dostává od své rodičovské komponenty dvě hodnoty v
props: firstName
a lastName
. V computed
properties je pak definována nová hodnota - fullName
jako
výpočet z obou předchozích. Ta je potom zobrazována v šabloně. Komponentu
bychom mohli použít třeba takto:
<template> <div> <full-name first-name="Honza" last-name="Novák"/> <full-name first-name="Pepa" last-name="Horák"/> </div> </template> <script> import FullName from '@/components/FullName.vue' export default { components: { FullName } } </script>
Tento kód by zobrazil dvě celá jména - "Honza Novák" a "Pepa Horák".
Příklad se vám jistě může zdát triviální, smysl využití computed properties však roste s náročností výpočtu či aplikační logiky. Je také důležité vzít v úvahu, že hodnoty computed properties jsou automaticky přepočítány pokaždé, kdy se změní některá vstupní hodnota pro jejich výpočet. Když uvážíme, že hodnoty mohou být předávány z rodičovských do dceřinných komponent dynamicky, pak je zřejmé, že nám computed properties výrazně usnadňují práci.
Direktiva v-bind:class
Připomeňme si, že direktiva v-bind
přiřazuje hodnotu
obsaženého JavaScriptového výrazu, který může obsahovat proměnné dané
komponenty, do props komponenty dceřinné anebo přímo do atributu
HTML elementu. Mezi HTML atributy však existují dvě výjimky, kdy je třeba
použít odlišnou syntaxi - atributy class
a style
.
Vue nám umožňuje dynamicky přiřazovat CSS třídy do atributu
class
dvěma způsoby:
Objektová syntaxe
V objektu, který je přiřazen direktivě v-bind
, definujeme
CSS třídy jako názvy jednotlivých vlastností. Hodnoty těchto vlastností
jsou JavaScriptové výrazy, které se po vyhodnocení jako pravdivé
(truthy), je třída danému elementu přiřazena, v opačném
případě nikoliv.
<div :class=" { 'class-name': condition, { 'another-class': anotherCondition } "> </div>
Výše uvedeným kódem přiřazujeme jednotlivé CSS třídy elementu
<div>
podmíněně. Je-li výraz condition
pravdivý, bude classList elementu div
obsahovat CSS třídu
.class-name
. Dále, je-li pravdivý výraz
anotherCondition
, bude classList elementu obsahovat CSS třídu
.another-class
.
Array syntaxe
Direktivě v-bind
na atributu class
můžeme kromě
objektu přiřadit i pole obsahující stringy - názvy jednotlivých CSS
tříd:
<div :class="['red-border', 'light-shadow', className]"></div>
Kód výše přiřadí elementu <div>
CSS třídy
.red-border
, .light-shadow
a pak ještě tu třídu,
jejíž název je obsažen v proměnné className
.
Potřebujeme-li v array syntaxi použít podmínky, je to poměrně snadné. Namísto textového řetězce může být prvkem pole objekt s jednou vlastností (název třídy) a její hodnotou (výraz - podmínka):
<div :class="['light-shadow', { 'red-border': !isValid }]"></div>
Takto například přiřadíme elementu <div>
CSS třídu
"light-shadow" vždy a třídu "red-border" právě tehdy, když hodnota
proměnné isValid
bude nepravdivá (falsy).
V příkladech obou syntaxí jsme objekty/pole vypisovali přímo do šablony. Poměrně často je však využívána technika, kdy jsou objekty/pole vypočítány ve skriptu, a to nejčastěji právě v computed properties, o nichž jsme hovořili na začátku lekce.
Direktiva v-bind:style
Druhou výjimkou z obecné syntaxe direktivy v-bind
je atribut
style
, kterým jsou HTML elementu přiřazovány inline CSS
styly.
Objektová syntaxe
Názvy CSS vlastností píšeme jako názvy vlastností objektu. Můžeme zde
však vedle notace kebab-case psané v jednoduchých
uvozovkách použít i notaci camelCase. Zápisy
'border-top-color'
a borderTopColor
jsou tedy
ekvivalentní. Tyto vlastnosti stylového objektu pak obsahují požadované
hodnoty CSS vlastností (samozřejmě včetně jednotek):
<div :style="{fontSize: size + 'px'}"></div>
Kód výše tak tedy například definuje v elementu <div>
výšku písma (font-size
) právě tolik pixelů, kolik je hodnota
proměnné size
. Nedává-li výraz size + 'px'
platnou CSS hodnotu, bude tento zápis ignorován.
Array syntaxe
Podobně jako u v-bind:class
bývají stylové objekty
definované ve skriptu, a to nejčastěji v computed properties. Array
syntaxe nám umožňuje sloučit styly několika stylových objektů dohromady,
například takto:
<div :style="[styleObject1, styleObject2]"></div>
Kalkulačka - stylování tlačítek
Teorii vysvětlenou výše nyní uplatníme v naší kalkulačce. Po minulé
lekci, kde jsme si potřebovali ukázat podmíněné renderování, obsahuje
naše komponenta CalculatorButton
ve své šabloně opakovaný
kód. Opisujeme zde dvakrát tentýž element <div>
, jeho
jednotlivá opakování se liší pouze CSS třídou
.calculator-button-operator
, která v prvním výskytu obsažena
není, ve druhém ano. Poměrně nešikovně potom na základě podmínky v
direktivě v-if
zobrazujeme buď první nebo druhý element
<div>
.
Jistě by bylo praktičtější, stejně tak náš kód by byl lépe
čitelný a udržovatelný, kdybychom vždy zobrazovali jen jeden element
<div>
a vyhodnocení podmínky by rozhodovalo pouze o
přiřazení dané CSS třídy.
Dále chceme, aby tmavší oranžové pozadí měla všechna tlačítka kromě čísel a desetinné čárky. Tato tlačítka budeme pracovně nazývat "operátory".
Upravme šablonu a skript komponenty CalculatorButton
takto:
<template> <div class="calculator-button" :class="{ 'calculator-button-operator': isOperator }" > {{ displayValue }} </div> </template> <script> export default { name: 'CalculatorButton', props: { displayValue: { type: String, required: true } }, computed: { isOperator () { return this.displayValue !== ',' && isNaN(parseInt(this.displayValue)) } } } </script>
Ve scriptu jsme vytvořili jednu datovou položku v computed
properties. Její název je isOperator
a jde o funkci, která
vrací hodnotu false
pro tlačítka zobrazující číslo nebo
desetinnou čárku, hodnotu true
potom vrací pro všechna ostatní
tlačítka.
V šabloně jsme použili standardní atribut class
elementu
<div>
, ve kterém tomuto elementu přiřazujeme CSS třídu
.calculator-button
. Tato třída je tedy přiřazena vždy, v
každém tlačítku bez rozdílu. V tomtéž elementu jsme dále použili
direktivu :class
a přiřadili jí objekt s jedinou vlastností.
Takto bude třída .calculator-button-operator
elementu přiřazena
právě tehdy, když funkce isOperator
vrátí
true
.
Všimněme si, že Vue umožňuje v jediném elementu použít obojí -
standardní atribut class
spolu s direktivou :class
.
Danému elementu se v takovém případě přiřadí všechny CSS třídy
definované jedním i druhým způsobem dohromady. U atributu style
a direktivy :style
to funguje obdobně.
Naše kalkulačka nyní vypadá takto:
V další lekci se budeme věnovat událostem ve Vue. Představíme si
direktivu v-on
a naučíme se předávat data z dceřiných do
rodičovských komponent emitováním vlastních událostí.
Měl jsi s čímkoli problém? Zdrojový kód vzorové aplikace je ke stažení každých pár lekcí. Zatím pokračuj dál, a pak si svou aplikaci porovnej se vzorem a snadno oprav.