Deze tutorial gaat helemaal in op het nieuwe autorijden. Dat elektrisch rijden de toekomst heeft, is onderhand wel duidelijk. Maar hoe ver kun je nou eigenlijk rijden op een volle accu? En welke invloed heeft bijvoorbeeld de snelheid, buitentemperatuur en de grootte van de velgen op het bereik? In deze tutorial gaan we aan de slag met Vue.js: een gemakkelijk te begrijpen JavaScript framework. We gaan een dashboard maken, waarmee berekend kan worden hoeveel range de Tesla heeft onder verschillende omstandigheden.
Auteur: Peter Eijgermans
Als uitgangspunt voor de tutorial, clone je deze Github repository: https://github.com/petereijgermans11/workshop-reactjs-vuejs/
cd workshop-reactjs-vuejs/vuejs-app
Lees de README.md voor de uit te voeren opdrachten. Afbeelding 1 is een voorbeeld van de applicatie, die we gaan bouwen. We gaan initieel uit van een buggy applicatie, die we moeten fixen en verder uitbouwen. Voordat we aan de slag gaan, leg ik eerst uit hoe deze applicatie is opgebouwd.
Om te starten met deze tutorial installeer je het volgende:
Het project waaraan we werken (zie Listing 1) heeft dezelfde structuur als Afbeelding 1 en geeft de componenten weer waaruit deze applicatie bestaat. De main.js is de entry point van de applicatie. App.vue is de entry component van de applicatie. De buitenste rand van Afbeelding 1 geeft de App component weer.
Een Vue applicatie wordt opgestart in main.js (zie Listing 2). In deze main.js maak je eerst een nieuwe “root Vue instantie” aan. Dit gaat als volgt:
render: h => h (App)
App.vue component
Deze App.vue (zie Listing 3) is de entry component van de applicatie en bestaat uit de volgende onderdelen:
script: dit is het JavaScript gedeelte van dit component. In bovenstaand voorbeeld geeft de name property de naam van de component aan (de naam is “app”). In de components property worden de child componenten gedefinieerd, die dit component gebruikt. In dit geval is TeslaBattery een child component van de App.vue component. Om gebruik te maken van de TeslaBattery component moet deze eerst geïmporteerd worden (import TeslaBattery from ‘…’). In de data()-function kun je state variabelen definiëren en initialiseren, zoals het geïmporteerde logo en de greeting property. Voor het renderen van het logo en de greeting dienen deze in de template (zie Listing 3) gedefinieerd te worden. Dit gehele component moet je uiteindelijk exporteren (middels export default), zodat deze weer geïmporteerd kan worden in andere componenten en in main.js.
template: is verantwoordelijk voor het definiëren van de output, die de component genereert. Vue.js gebruikt een op HTML-gebaseerde template syntax. Data afkomstig uit de data()-function kan eenvoudig gerenderd worden door middel van databinding. De meest eenvoudige vorm van databinding is tekst interpolatie met behulp van de Mustache syntax (dubbele accolades): {{greeting}}
In bovenstaand voorbeeld wordt {{greeting}}, vervangen door de waarde Hello Tesla !!! uit de betreffende data-function. Boven deze greeting wordt ook nog het logo gerenderd dankzij de img-tag. Om het logo toe te wijzen aan de img src-attribute, dient gebruik gemaakt te worden van attribute binding. Hiervoor kan je v-bind of : gebruiken (<img :src= “logo”> of <img v-bind:src= “logo”>). Attribute binding wordt veelvuldig toegepast in deze applicatie.
En tenslotte wordt middels de <tesla-battery> – tag de TeslaBattery component geïnstantieerd en gerenderd. Voor deze tag (ook wel “custom element” genoemd) dien je de Kebab-case te gebruiken. Hoe dit component functioneert zal verderop behandeld worden.
style: in Vue gebruiken we een SCSS-file voor het stylen van de gehele applicatie.
Bijna alle Vue-applicaties bestaan uit een samenstelling van componenten. Deze applicatie bestaat uit een entry App-component met de TeslaBattery als child component. En de TeslaBattery component bevat de volgende child componenten:
TeslaCar: voor het renderen van de TeslaCar image met wielanimatie.
TeslaStats: voor het renderen van de maximum battery range per Tesla-model. Het gaat hier om de modellen: 60, 60D, 75, 75D,90D en P100D.
TeslaCounter: voor het handmatig regelen van de snelheid (speed) en de buitentemperatuur.
TeslaClimate: deze verandert de verwarming naar airco wanneer de buitentemperatuur meer dan 20 graden is.
TeslaWheels: voor het handmatig aanpassen van de wielmaat van 19 inch naar 20 inch en vice versa.
De gebruikersinterface wordt als volgt weergegeven door een componenten tree (zie Listing 4)
Uit Listing 4 blijkt dat de “Tesla Battery component” een Container component is. Hierbij zijn de onderliggende child componenten Presentation componenten. Dit is een handig patroon dat kan worden gebruikt bij het ontwikkelen van een Vue-applicatie. Door componenten in twee categorieën te verdelen, zijn ze beter herbruikbaar.
Container componenten kenmerken zich door het volgende:
Presentation componenten kenmerken zich door het volgende:
Deze aanpak heeft de volgende voordelen:
De gegevens die we gebruiken, zijn hard-coded vastgelegd in tesla-battery.service.js.
Deze service heeft een methode getModelData() voor het ophalen van de modeldata.
Bekijk de structuur van deze modeldata in Listing 5.
Op basis van de volgende parameters wordt de maximum battery range per Tesla-model bepaald: Tesla-model (60, 60D…), wheelsize (19/20 inch), climate (on/off), speed en temparature (-10, 0 …).
Dit component is verantwoordelijk voor het definiëren, aanmaken en doorgeven van data aan child componenten middels “props”. Tevens is het verantwoordelijk voor het managen van de state van de applicatie. Helemaal ingeklapt (zie Listing 6) zien we dat dit component bestaat uit onderstaande properties.
De components property bevat alle child componenten waarvan dit component gebruik maakt. De computed property bevat de functies, die gecached worden. Dat wil zeggen dat zo een functie alleen uitgevoerd wordt als deze afhankelijk is van een specifieke data property en wanneer de state van deze property wijzigt. In onderstaande volledige versie van de TeslaBattery component (zie Listing 8), is de stats()-functie een voorbeeld van een computed function. Deze functie filtert de “maximum battery range per Tesla-model” uit de modeldata (zie Listing 5). In Listing 7 is een voorbeeld van de output van de stats()-functie. Deze maximum battery range is gebaseerd op de gebruikersinvoer, zoals de geselecteerde wheelsize, climate, speed en temperature. En deze stats()-functie wordt alleen uitgevoerd als deze gebruikersinvoer wijzigt. De gebruikersinvoer wordt vastgelegd in het tesla-object (state-object), die gedefinieerd is in de data-function.
Maximum battery range per model:
[ {"model":"60","miles":267}, {"model":"60D","miles":271}, {"model":"75","miles":323}, {"model":"75D","miles":332}, {"model":"90D","miles":365}, {"model":"P100D","miles":409} ]
Listing 7
De methods property bevat de alle functies die niet gecached worden. De changeClimate() functie wordt hierin gedefinieerd, omdat deze functie getriggerd wordt door een onClick event (en niet op basis van een data/state property).
De template van deze TeslaBattery component heeft dezelfde structuur als Listing 4, namelijk (zie Listing 9).
Doorgeven van data aan child componenten middels props
In Afbeelding 3 wordt stats data (afkomstig uit de stats()-functie, zie Listing 7) vanuit de TeslaBattery component aan de TeslaStats component doorgegeven.
Om data door te geven aan een child component, dien je v-bind of : te gebruiken in de template van de TeslaBattery component (zie Listing 10).
<template> <form> ... <tesla-stats :stats="stats" /> ... </form> </template>
Listing 10
Ontvangen van data in de TeslaStats Component
Dit component bevat een props property in de scripts-sectie, voor het ontvangen van de stats data. Deze stats is van het type Array (zie Listing 11). In de template wordt door de “stats” geïtereerd, met behulp van een v-for directive van Vue.js. De :key geeft aan dat deze lijst in een bepaalde volgorde moet worden gerenderd.
In de filter property kun je een custom filter definiëren. In de template wordt een custom filter “lowercase” met een pipe | aangeroepen voor de te renderen modelnamen. Er is ook een custom filter gedefinieerd voor het omzetten van miles naar “km”.
Met deze introductie kun je beginnen aan het oplossen van bugs en het uitvoeren van opdrachten, die beschreven zijn in de README.md van dit project. In dit Github project is ook een Powerpoint toegevoegd, die dieper ingaat op zaken als two-way databinding middels de v-model directive, toewijzen van een onClick event aan een button met @click en het aanmaken van andere componenten.
Bijna alle Vue-applicaties bestaan uit een samenstelling van componenten.